I want to transmit the data from the input element of the child comp to the parent comp data property
using the$emit method the data can be transmited to the parent
directly binding the transmited property on the child property
using v-bind <input v-model="userinput" />
results in a Warning
runtime-core.esm-bundler.js?5c40:6870 [Vue warn]: Extraneous non-emits event listeners (transmitData) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option.
at <UserInput onTransmitData=fn<bound receaveData> >
at <App>
what is the correct way to do this kind of operation ?
\\ child
<template>
<input v-model="userinput" />
<button type="button" #click="transmitData">transmit</button>
</template>
<script>
export default {
data() {
return {
userinput: " dd",
}
},
methods: {
transmitData(event){
this.$emit('transmit-data', this.userinput)
}
}
}
</script>
\\ parent
<template>
<user-input #transmit-data="receaveData" />
<p>{{ childData }}</p>
</template>
<script>
import UserInput from "./components/UserInput.vue";
export default {
name: "App",
components: {
UserInput,
},
data() {
return {
childData: " "
};
},
methods: {
receaveData(compTransmit){
console.log(compTransmit)
this.childData = compTransmit
},
},
};
</script>
you have to specify a emits option in the child component
docs
so in your case should look like this
export default {
emits: ['transmit-data'], // you need to add this :)
data() {
return { userinput: " dd" }
},
methods: {
transmitData(event){
this.$emit('transmit-data', this.userinput)
}
}
}
Related
I try to bind child input value to the parent value. I pass value searchText from a Parent component to a Child component as prop :valueProp. There I assign it to property value: this.valueProp and bind input:
<input type="text" :value="value" #input="$emit('changeInput', $event.target.value)" />
The problem is that input doesn't work with such setup, nothing displays in input, but parent searchText and valueProp in child update only with the last typed letter; value in child doesn't update at all, though it is set to equal to searchText.
If I remove :value="value" in input, all will work fine, but value in child doesn't get updated along with parent's searchText.
I know that in such cases it's better to use v-model, but I want to figure out the reason behind such behavior in that case.
I can't understand why it works in such way and value in child component doesn't update with parent's searchText. Can you please explain why it behaves in that way?
Link to Sanbox: Sandbox
Parent:
<template>
<div>
<Child :valueProp="searchText" #changeInput="changeInput" />
<p>parent {{ searchText }}</p>
</div>
</template>
<script>
import Child from "./Child.vue";
export default {
name: "Parent",
components: { Child },
data() {
return {
searchText: "",
};
},
methods: {
changeInput(data) {
console.log(data);
this.searchText = data;
},
},
};
</script>
Child:
<template>
<div>
<input type="text" :value="value" #input="$emit('changeInput', $event.target.value)" />
<p>value: {{ value }}</p>
<p>child: {{ valueProp }}</p>
</div>
</template>
<script>
export default {
emits: ["changeInput"],
data() {
return {
value: this.valueProp,
};
},
props: {
valueProp: {
type: String,
required: true,
},
},
};
</script>
You set the value in your Child component only once by instantiating.
In the data() you set the initial value of your data properties:
data() {
return {
value: this.valueProp,
};
},
Since you don't use v-model, the value will never be updated.
You have the following options to fix it:
The best one is to use v-model with value in the Child.vue
<input
type="text"
v-model="value"
update value using watcher
watch: {
valueProp(newValue) {
this.value = newValue;
}
},
use a computed property for value instead of data property
computed: {
value() {return this.valueProp;}
}
Respect for creating the sandbox!
You are overwriting the local value every time the value changes
data() {
return {
value: this.valueProp, // Don't do this
};
},
Bind directly to the prop:
<input ... :value="valueProp" ... />
I need to execute a v-for, however I do not know how to add a v-model for each different component inside of v-for;
<template>
<ProfilePasswordField
v-for="(item, index) in profilePasswordItems"
:key="index"
:profilePasswordItem="item"
v-model="???"
>
</template>
This v-for will always be three items and I want to name the v-model's as: ['passaword', 'newPassword', confirmNewPassword']
How can I add those names dinamically for the v-model inside v-for?
I tried to do a list inside data() but it did not work. Something like that:
data (){
return{
listPassword: ['passaword', 'newPassword', 'confirmNewPassword']
}
},
methods: {
method1 () {
console.log(this.passaword)
console.log(this.newPassword)
console.log(this.confirmNewPassword)
}
}
The v-model directives cannot update the iteration variable itself therefore we should not use a linear array item in for-loop as the v-model variable.
There is another method you can try like this-
In the parent component-
<template>
<div>
<ProfilePasswordField
v-for="(item, index) in listPassword"
:key="index"
:profilePasswordItem="item"
v-model="item.model"
/>
<button #click="method1()">Click to see changes</button>
</div>
</template>
<script>
export default {
name: "SomeParentComponent",
data() {
return {
listPassword: [
{ model: "passaword" },
{ model: "newPassword" },
{ model: "confirmNewPassword" },
],
};
},
methods: {
method1() {
this.listPassword.forEach((item) => {
console.log(item.model);
});
},
},
}
</script>
And in your ProfilePasswordField you can emit the input event to listen to the respected changes in v-model binding. For example, the ProfilePasswordField component could be like this-
<template>
<v-text-field :value="value" #input="$emit('input', $event)"/>
</template>
<script>
export default {
name: "ProfilePasswordField",
props: {
value: {
required: true
}
}
</script>
In the parent component's console, you should see the changes made by the child component.
I have a parent form template and each question of the form is inside a child component, like this
<template>
<question1
#display-answer-1="setAnswer1"
/>
<!-- other child components here... -->
</template>
<script>
import Question1 from '...path...';
export default{
components: { Question1 },
data() {
answer1: ''
},
methods: {
setAnswer1(answer1) {
this.answer1 = answer1;
}
}
};
and my child component is like this
<template>
<input type="text" v-model="answer1"/>
<div>
<button
type="button"
#click="saveQ2"
>Save
</button>
</div>
</template>
<script>
export default {
data() {
return {
answer1: ''
};
},
methods: {
saveQ2() {
const answer1 = this.answer1;
this.$emit('display-answer-1', answer1);
}
}
};
This code works, but in this way I'm forced to put a button whenever there is a question to pass data from the child to the form template parent. Is there a smart alternative not to put a save button under each question?
you can use the blur event whenever an input gets unfocused it'll fire the event .
<template>
<input #blur="saveQ2" type="text" v-model="answer1"/>
</template>
<script>
export default {
data() {
return {
answer1: ''
};
},
methods: {
saveQ2() {
const answer1 = this.answer1;
this.$emit('display-answer-1', answer1);
}
}
};
Here's sample of my child component
HTML:
<div v-for="(input, index) in form.inputs" :key="index">
<div>
<input :name"input.name" :type="input.type" />
</div>
</div>
JavaScript (Vue):
<script>
export default {
name: "child",
props: ['parentForm'],
data() {
return {
form: {
inputs: [
{
name: 'name',
type: 'text'
],
[...]
}
}
}
And sample of root component
HTML:
<child :parentsForm="form"></child>
JavaScript (Vue):
<script>
import child from "./child";
export default {
name: "root",
components: { child },
data() {
return {
form: {
data: {
name: null,
email: null,
...
}
}
}
The question is, how do I achieve combining root + v-for?
Example I want to using child component this way
<input :name"input.name" :type="input.type" v-model="parentForm.data . input.name" />
Since parentForm.data will bind form:data:{ and this will be the variable get from input.name }
Output in v-model should be bind form.data.name or form.data.email on root component
Thank you
You can use it as per follow,
<input :name="input.name" :type="input.type" v-model="parentForm.data[input.name]" />
This will bind parentForm.data.name for input.name = 'name' to v-model.
If I understood you correctly, you want to update parent data from your child component. If yes then you have two options.
In you child component use $parent.form.data to bind.
Or you can pass it down as prop assign it to a data property in child. Bind this new data property in your child and emit it whenever any changes are made. Receive this emit in your parent and update the parent property respectively (Recommended)
Here is the parent component:
<template lang="pug">
.wrapper
el-button(type="primary", #click="dialogAddUser = true") New User
hr
// Dialog: Add User
add-edit-user(:dialog-visible.sync="dialogAddUser")
</template>
<script>
import * as data from '#/components/partials/data'
import AddUser from './partials/AddUser'
export default {
name: 'users',
components: { AddUser },
data () {
return {
users: data.users,
dialogAddUser: false
}
}
}
</script>
Here is the child component:
<template lang="pug">
el-dialog(width="75%", title="New User", :visible.sync="dialogVisible", top="5vh")
div 'el-dialog-body' - content goes here
</template>
<script>
export default {
name: 'add-user',
props: {
dialogVisible: Boolean
}
}
</script>
I am able to open the dialog but when close the dialog using top right button inside the dialog then I am getting this error:
Avoid mutating a prop directly since the value will be overwritten
whenever the parent component re-renders. Instead, use a data or
computed property based on the prop's value. Prop being mutated:
"dialogVisible"
Later I tried to play and did something like below, but now I cannot even open the dialog:
<template lang="pug">
el-dialog(width="75%", title="New User", :visible.sync="visibleSync", top="5vh")
div 'el-dialog-body' - content goes here
</template>
<script>
export default {
name: 'add-user',
props: {
dialogVisible: Boolean
},
watch: {
visibleSync (val) {
this.$emit('update:dialogVisible', val)
}
},
data () {
return {
visibleSync: this.dialogVisible
}
}
}
</script>
If visible.sync works, the component is emitting an update:visible event.
So, to not mutate in the child and, instead, propagate the event to the parent, instead of:
:visible.sync="dialogVisible"
Do
:visible="dialogVisible", v-on:update:visible="visibleSync = $event"
Full code:
<template lang="pug">
el-dialog(width="75%", title="New User", :visible="dialogVisible", v-on:update:visible="visibleSync = $event", top="5vh")
div 'el-dialog-body' - content goes here
</template>
<script>
export default {
name: 'add-user',
props: {
dialogVisible: Boolean
},
watch: {
visibleSync (val) {
this.$emit('update:dialogVisible', val)
}
},
data () {
return {
visibleSync: this.dialogVisible
}
}
}
</script>
As another alternative, you could emit directly from the v-on listener and do without the visibleSync local property:
<template lang="pug">
el-dialog(width="75%", title="New User", :visible="dialogVisible", v-on:update:visible="$emit('update:dialogVisible', $event)", top="5vh")
div 'el-dialog-body' - content goes here
</template>
<script>
export default {
name: 'add-user',
props: {
dialogVisible: Boolean
}
}
</script>
I think a nice way to handle this is to:
Use a prop to pass the visible state from the parent to the child.
Forward el-dialog's close event from the child to the parent.
In the parent, handle the close event to set the prop to false.
Child:
<el-dialog :visible="visible" #close="$emit('close')">
export default {
props: {
visible: Boolean
},
...
Parent (assuming you store the open state in state.modalOpen):
<el-button #click="state.modalOpen = true">Open Modal</el-button>
<child-component :visible="state.modalOpen" #close="state.modalOpen = false" />