I have an modal component, and inside this i have two input how i can get the value inserted in this input and send him to another component?
<template>
<modal v-if="Modal">
div(class="modal-title has-text-centered" slot="header")
div(slot="content")
div(class="control")
div(class="columns")
div(class="column")
div(class="field")
div(class="control")
input(class="input is-info is-large" type="text" placeholder="Analysis name")
div(class="field")
div(class="control")
input(class="input is-info is-large" type="text" placeholder="Item name")
div(slot="footer")
button(class="button is-info is-large modal-button" #click="closeModal" style="margin-right:5em;") Cancel
<router-link :to="{name: 'analyse'}" class="button is-info is-large modal-button">Create</router-link>
</modal>
</template>
<script>
import JwtDecode from 'jwt-decode'
import Modal from '../layout/modal/Basemodal.vue'
import Section from '../../views/Section.vue'
export default {
data: function() {
return {
user: '',
isActive: false,
isDrop: false,
Modal: false,
analyseName: '',
analyseItem: '',
application: '',
}
},
components: {
'modal': Modal,
'section-content': Section,
},
methods: {
// on-click logout the user and send him to the initial page
logout: function(){
this.$store.dispatch('logout')
.then(resp => {
this.$router.push('/')
})
},
// used to change the color of the selected button
toggle: function(event) {
event.ldaModal = !event.ldaModal
},
createAnalyse: function() {
this.$route.push('/analyse')
}
},
mounted() {
// render user token to get all user information
this.user = JwtDecode(localStorage.getItem('token'))
}
}
</script>
and here i have another component, in this component i need to get the value of modal and render it
<template lang="pug">
p {{ input1-value }}
p {{ input2-value }}
</template>
please, what's the best approach to do this?
I think that you should use the EventBus to solve your question.
in main.js
const bus = new Vue()
Vue.prototype.$bus = bus
you can register an event listener
this.$bus.on( 'test', ( data ) => {
console.log( 'test', data );
} );
and emit it
this.$bus.emit( 'test', { code: 1 } );
you also can look at https://github.com/yyued/hub.js
Related
I have two child components I have to pass dynamically props from first child to parent and from parent to second child.
Parent
<script>
data: () => ({
model: {}
}),
methods: {
changeData(payload) {
this.model.personalData = {...payload}
}
}
</script>
<template>
<first-child #changeData="(payload) => changeData(payload)"/>
<second-child :enter-object="model" />
</template>
Child one
<script>
data: () => ({
model: {}
}),
methods: {
changeData() {
this.$emit("changeData", this.model);
}
}
</script>
<template>
<v-text-field v-model="model.name" #input="changeData()">
<v-text-field v-model="model.email" #input="changeData()">
</template>
Child two
<script>
props: {
enterObject: {
type: Object,
required: false,
default: () => ({})
}
},
data: () => ({
model: {}
}),
watch: {
enterObject: {
immediate: true,
handler() {
Object.assign(this.model.personalData, this.enterObject.personalData);
}
}
</script>
<template>
<div>
<div v-if="model.personalData.name || model.personalData.email">
<span class="mr-3">{{ model.personalData.name }}</span>
<span>{{ model.personalData.email }}</span>
</div>
<div v-else>
No data
</div>
</div>
</template>
I get data in parent component with no problem, but this data doesn't pass to second child, why I have always "No data" ?
I tested your code and found a few things:
You need to create "personalData" inside the model in "childTwo".
<template>
<div>
// I changed the validation for personalData
<div v-if="model.personalData">
<span class="mr-3">{{ model.personalData.name }}</span>
<span>{{ model.personalData.email }}</span>
</div>
<div v-else>No data</div>
</div>
</template>
export default {
props: {
enterObject: {
type: Object,
required: false,
default: () => ({})
}
},
data: () => ({
model: {
personalData: {}
}
}),
watch: {
enterObject: {
deep: true,
handler() {
// Add a validation in the handler, you can use Object assign inside the validation.
if(this.enterObject) {
Object.assign(this.model.personalData, this.enterObject.personalData)
}
}
}
}
It's worked for me.I hope it helps you.
You have to assign the value of the object using this.$set for more about object reactivity click here
your Parent component should be like this:-
here is the working example
<template>
<div>
<first-child #change-data="(payload) => changeData(payload)" />
<second-child :enter-object="model" />
</div>
</template>
<script>
import FirstChild from "./FirstChild";
import SecondChild from "./SecondChild";
export default {
data: () => ({
model: {},
compKey: 0,
}),
components: {
FirstChild,
SecondChild,
},
methods: {
changeData(payload) {
this.$set(this.model, "test", payload);
//this.model.test = payload;
},
},
};
</script>
I have the following UI:
I will try to explain what is going on here. I have "Add message" button. When I click on the button I have new form with message: title, body, image, language (just multiple select via this plugin). I have clicked twice - I have 2 messages. Simple.
I don't use vue router. Implementation of my routing is with the help of backend. It means that for each route I have new vuex state.
I'm going to keep my messages in vuex, but it's impossible to use v-model for this case.
So, I will show my code.
store:
export const store = new Vuex.Store({
state: {
messages: [],
// more props are here ...
},
mutations: {
setMessages(state, messages) {
state.messages = messages;
},
// more setters are here
},
getters: {
getMessages: state => {
return state.messages;
},
// more getters are here
},
actions: {
updateMessagesAction: function({commit}, value) {
console.log(value)
},
}
});
Messages component:
<template>
<div>
<button class="btn btn-outline-info" #click.prevent="createNewMessage">
<i class="fa fa-language"/> Add message
</button>
<div>
// now it works with local state, but I need to work with vuex
<div v-for="(message, index) in messages">
<button class="btn btm-sm btn-danger" #click="deleteMessage(index, message)"><i class="fa fa-remove"/>
</button>
<b-collapse collapsed :id="`collapse-${index}`">
<form>
<div class="form-group">
<label class="typo__label">Languages</label>
<multiselect
v-model="message.languages"
:options="getLanguagesOptions"
:multiple="true"
:close-on-select="true"
:clear-on-select="false"
:preserve-search="true"
placeholder="Languages"
label="name"
track-by="id"
>
</multiselect>
</div>
<div class="form-group">
<label for="title" class="typo__label">Title</label>
<input type="text" id="title" class="form-control" autocomplete="off" ??? how to bind it to vuex ???? I dont understand :(((>
</div>
<div class="form-group">
<label for="text" class="typo__label">Body</label>
<textarea class="form-control" id="text" ??? how to bind it to vuex ???? I dont understand :(((/>
</div>
<div class="form-group">
<div id="upload-image">
<div v-if="!message.imageSrc">
<h2>Image</h2>
<input type="file" ref="file" #change="onFileChange($event, message)">
</div>
<div v-else>
<img :src="message.imageSrc"/>
<button #click.prevent="removeImage($event, message)">Remove</button>
</div>
</div>
</div>
<hr class="mb-2">
</form>
</b-collapse>
</div>
</div>
</div>
</template>
<script>
// imports
export default {
async created() {
// set languages from servert to vuex
let res = (await axios.post(this.urlForGettingLanguagesFromServerProp)).data;
this.$store.commit('setLanguagesOptions', res);
},
name: "MessagesComponent",
props: {
urlForGettingLanguagesFromServerProp: String,
uploadImageUrl: String,
deleteImageUrl: String,
selectedLanguagesIdsProp: {
type: Array,
default: () => []
},
},
methods: {
...mapMutations(['setLanguagesSelected']),
...mapActions(['updateMessagesAction']),
createNewMessage: function () {
let message = {
languages: [],
languagesIds: [],
title: "",
text: "",
imageSrc: "",
imageDbId: 0
};
this.messages.push(message);
},
deleteMessage: function (index, message) {
this.removeImage("", message);
this.messages.splice(index, 1);
},
onFileChange: async function (e, message) {
// this method add send image on server and save to state db image id ant path
},
removeImage: function (event, message) {
// remove image from server
}
},
computed: {
...mapGetters(['getLanguagesOptions', 'getMessages'])
},
watch: {
messages: {
deep: true,
immediate: true,
handler(val, oldVal) {
let message = JSON.parse(JSON.stringify(val));
this.$store.commit("setMessages", message);
}
}
},
data() {
return {
messages: [],
}
}
}
</script>
I call this component in a parent component. In the parent component I initiate vuex during update operation.
As you can see this component works with local state and synchronize local state with vuex. It's ok for Create operation. I can send messages to vuex, then I can take it in the parent component with other information and send it on the server. But what to do with Update? I have data from the server in the parent component but local state, of course, is empty for the Messages component. How to bind all messages to vuex and have ability to change each message separately? I mean, for example, to change title of the first message and to have it in vuex immediately?
Please, help me improve this component.
I'm a newbie of Vue, and I'm trying to simply clear the data of input component once I've submitted, but it seems I'm missing something, because since it's parent data is cleared, I still see the filled value of the input component.
Here is a living example.
I've set to the input child component v-model="title" from it's parent wrapper. Once I submit the data to the parent, I call addItem and in the end, I supposed to clear the data model just by clear it this.title = '', but I probably do something wrong on how to bind data from parent to child.
And above the code, starting from the parent component:
<template>
<form #submit="addItem" class="todo-insert">
<input-item
icon="create"
name="title"
placeholder="Add a ToVue item..."
v-model="title"
/>
<button-item tone="confirm" class="todo-insert__action">
Aggiungi
</button-item>
</form>
</template>
<script>
import ButtonItem from '#vue/Form/ButtonItem/ButtonItem.vue'
import InputItem from '#vue/Form/InputItem/InputItem.vue'
import uuid from 'uuid'
export default {
name: 'TodoInsert',
components: {
ButtonItem,
InputItem
},
data () {
return {
title: ''
}
},
methods: {
addItem (e) {
e.preventDefault()
const todo = {
id: uuid.v4(),
isComplete: false,
title: this.title
}
this.$emit('add-todo', todo)
this.title = ''
}
}
}
</script>
<style lang="scss" scoped src="./TodoList.scss"></style>
This is the child input component:
<template lang="html">
<label class="input">
<div v-if="label" class="input__label text-sans text-sans--label">
{{ label }}
</div>
<div class="input__row">
<input
:autocomplete="autocomplete"
:class="[hasPlaceholderLabel, isDirty]"
:name="name"
:placeholder="placeholder"
class="input__field"
type="text"
v-on:input="updateValue($event.target.value)"
v-on:blur="updateValue($event.target.value)"
>
<div v-if="placeholderLabel" class="input__placeholder text-sans text-sans--placeholder">
{{ placeholderLabel }}
</div>
<div v-if="icon" class="input__icon-area">
<icon-item
:name="icon"
/>
</div>
</div>
</label>
</template>
<script>
import IconItem from '../../IconItem/IconItem.vue'
export default {
name: 'InputItem',
props: {
autocomplete: {
type: String,
default: 'off'
},
icon: String,
label: String,
name: {
type: String,
default: 'input-text'
},
placeholder: String,
placeholderLabel: String
},
computed: {
hasPlaceholderLabel () {
return this.placeholderLabel ? 'input__field--placeholder-label' : ''
},
isDirty () {
// return this.$attrs.value ? 'input__field--dirty' : ''
return 'input__field--dirty'
}
},
methods: {
updateValue: function (value) {
this.$emit('input', value)
}
},
components: {
IconItem
}
}
</script>
<style lang="scss" src="./InputItem.scss"></style>
What am I missing?
Your child component is bound unidirectionally. It means that it can change the value, but does not receive any update from the parent component. To receive updates, you need to receive the property value in your child:
props: {
value: String
}
Then, you need to pass the value received to the input :
<input
:value="value"
:autocomplete="autocomplete"
:class="[hasPlaceholderLabel, isDirty]"
:name="name"
:placeholder="placeholder"
class="input__field"
type="text"
v-on:input="updateValue($event.target.value)"
v-on:blur="updateValue($event.target.value)"
>
Now the input should update when the parent component changes the value
Good afternoon, I have two child components Header and Pagination. In Header, I have an input search engine and two inputs (title and body) in order to be able to add a post to Pagination. I managed to transfer the search value to the Pagination component, but I don’t know how to transfer the value from two inputs (title, body). I use to transfer the event bus. Help me please pass the value of the two inputs (title, body) into the Pagination component when you click the AddPost button.
My code on GitHub
Screenshot of app
My code of component Header:
<template>
<div class="header">
<input type="text" v-model="search" class="header_input_search" placeholder="Search" #input="saveMessage" />
<img src="src/assets/milk.png">
<div class="header_div_inputs">
<input type="text" v-model="createTitle" class="created"/>
<p><input type="text" v-model="createBody" class="createBody"/></p>
</div>
<button #click="addPost()" class="addPost">AddPost</button>
</div>
</template>
<script>
import axios from 'axios';
import {eventEmitter} from './main'
export default {
name: 'Header',
data () {
return {
search: '',
createTitle: '',
createBody: '',
}
},
methods:{
saveMessage(){
eventEmitter.$emit('messageSave', this.search)
},
}
}
</script>
My code of component Pagination:
<template>
<div class = "app">
<ul>
<li v-for="(post, index) in paginatedData" class="post" :key="index">
<router-link :to="{ name: 'detail', params: {id: post.id, title: post.title, body: post.body} }">
<img src="src/assets/nature.jpg">
<p class="boldText"> {{ post.title }}</p>
</router-link>
<p> {{ post.body }}</p>
</li>
</ul>
<div class="allpagination">
<button type="button" #click="page -=1" v-if="page > 0" class="prev"><<</button>
<div class="pagin">
<button class="item"
v-for="n in evenPosts"
:key="n.id"
v-bind:class="{'selected': current === n.id}"
#click="page=n-1">{{ n }} </button>
</div>
<button type="button" #click="page +=1" class="next" v-if="page < evenPosts-1">>></button>
</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
import {eventEmitter} from './main'
export default {
name: 'app',
data () {
return {
current: null,
page: 0,
visiblePostID: '',
pSearch: ''
}
},
mounted(){
this.$store.dispatch('loadPosts')
},
computed: {
...mapState([
'posts'
]),
evenPosts: function(posts){
return Math.ceil(this.posts.length/6);
},
paginatedData() {
const start = this.page * 6;
const end = start + 6;
return this.filteredPosts.slice(start, end);
},
filteredPosts() {
return this.posts.filter((post) => {
return post.title.match(this.pSearch);
});
},
},
created(){
eventEmitter.$on('messageSave', (string) => {
this.pSearch = string
})
}
}
</script>
You can wrap title and body in an object
addPost() {
const post = {
title: this.createTitle,
body: this.createBody
}
eventEmitter.$emit('postAdd', post)
}
and then listen as normal
created(){
eventEmitter.$on('postAdd', (post) => {
console.log(post)
// do whatever you want
})
}
I have not worked on vue js but agreed with #ittus answer. You can make an object consisting of your required data which you want to share across the component and pass it as an event data.
I am struggling to show a dynamic content inside my popover built with Bootstrap Vue component.
I want to show a form first, and when that form is submitted successfully I want to show a confirmation text instead.
I tried to manage that with v-show directive within the popover content with no success. Everything is displayed of course outside the div with the v-show directive.
The result is that it just shows an empty popover (except the close link) inside the popover.
Here is my component :
<template>
<div class="email-content">
<div class="email-content__buttons">
<b-button variant="blue" class="fv-round" id="testEmailSend" :disabled="isPopoverDisplayed" #click="isPopoverEmailFormDisplayed = true">{{locales.testButton}}</b-button>
<b-popover target="testEmailSend"
:show="isPopoverDisplayed"
placement="auto"
#show="isPopoverEmailFormDisplayed = true"
#shown="setFocusOnPopoverForm">
<template>
<b-btn #click="isPopoverEmailFormDisplayed = false" class="close">
<span class="d-inline-block">×</span>
</b-btn>
</template>
<div v-show="isPopoverConfirmationDisplayed">
<b-form-group :label="`${locales.sendTo} :`" label-for="popoverEmailForm" class="mb-1" :invalid-feedback="locales.emailMandatory">
<b-input-group>
<b-form-input ref="emailTestReceiver" id="popoverEmailForm" :validationState="$v.emailTestReceiver" size="sm" v-model="emailTestReceiver"></b-form-input>
<b-input-group-append>
<b-btn #click="sendEmailTest" size="sm" :disabled="$v.$invalid" variant="primary">Ok</b-btn>
</b-input-group-append>
</b-input-group>
</b-form-group>
</div>
</b-popover>
<div v-show="isPopoverConfirmationDisplayed" placement="auto">
{{locales.emailSentConfirmation}}
</div>
<b-button variant="primary" class="fv-round" #click="saveTemplates">{{locales.saveButton}}</b-button>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
import { required, email } from 'vuelidate/lib/validators';
import feedbackHandler from '#/components/_shared/Alerts/ErrorHandler';
import SellerEmailContentConfig from '#/components/SellerDashboard/SellerEmail/SellerEmailContent/SellerEmailContentConfig';
import locales from './sellerEmailContent.locales.json';
export default {
name: 'SellerEmailContent',
data() {
return {
locales,
emailTestReceiver: '',
isPopoverEmailFormDisplayed: false,
isPopoverConfirmationDisplayed: false
}
},
validations: {
emailTestReceiver: {
required,
email
}
},
computed: {
isPopoverDisplayed() {
return this.isPopoverEmailFormDisplayed || this.isPopoverConfirmationDisplayed;
}
},
methods: {
...mapActions({
sendEmail: 'sendAuthCustomEmail'
}),
async sendEmailTest() {
const { error: errorSending } = await this.sendEmail({ type: 'Seller', email: this.emailTestReceiver });
if (!errorSending) {
this.isPopoverEmailFormDisplayed = false;
this.isPopoverConfirmationDisplayed = true;
setTimeout(() => { this.isPopoverConfirmationDisplayed = false }, 3000);
} else feedbackHandler.showError(errorSending);
},
setFocusOnPopoverForm() {
this.$refs.emailTestReceiver.focus();
}
},
}
</script>
My last was as you can see above using a computed property which combines both situation (form or confirmation to be displayed).
Any idea ? I must have missed something :)