Reputation: 2859
I have a form where I need to update multiple other form fields when another field is updated. For example, I have a contact name and depending on the contact name, I need to update the values of the email and phone.
<template>
<custom-input :value="contact.name" @input="event => contact.name = event.target.value" />
<custom-input :value="contact.phone" @input="event => contact.phone = event.target.value" />
<custom-input :value="contact.email" @input="event => contact.email = event.target.value" />
</template>
<script>
...
props: {
contact: {
type: Object,
required: true
}
},
watch:
"contact.name": function(value) {
if (this.contact.name === "john") {
this.contact.email = "[email protected]"
this.contact.phone = "1111111111"
} else if (this.contact.name === "ed") {
this.contact.email = "[email protected]"
this.contact.phone = "2222222222"
}
}
}
...
</script>
I understand that Vue doesn't like this because it separates the DOM from the data model. My first thought was to use $refs
but those are read only. What's the right way to do this?
Another thought I had was to set the value of name
and phone
to computed properties. The problem with that is that it doesn't get watched on the form in the parent component.
This might also tie in with my misunderstanding of "two-way" binding. I've always reasoned that the form is one way, and the data within the component's script is the other which it is not. So, what's the other way?
A final thought I have is that I might have to emit an event instead?
<template>
<custom-input :value="contact.name" @input="event => contact.name = event.target.value" />
<custom-input ref="phone" :value="contact.phone" @input="event => contact.phone = event.target.value" />
<custom-input ref="email" :value="contact.email" @input="event => contact.email = event.target.value" />
</template>
<script>
...
props: {
contact: {
type: Object,
required: true
}
},
watch:
"contact.name": function(value) {
if (this.contact.name === "john") {
this.$refs.email.$emit("input", "[email protected]")
this.$refs.phone.$emit("input", "111111111111")
} else if (this.contact.name === "ed") {
this.$refs.email.$emit("input", "[email protected]")
this.$refs.phone.$emit("input", "222222222222")
}
}
}
That doesn't seem to work either. Bummer.
Edit
Fixed syntax errors
Edit 2
Showed that input
was actually a separate child component
Upvotes: 1
Views: 2494
Reputation: 2939
I just noticed that you are trying to modify the value of props
which prohibited by Vue. All child components cannot modify the data in props
flown from the parent, because it makes the data flow harder to understand. you can read more about this at the official site: https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
This is why Vue also have data
which is local private memory that can be modified by the component.
So to solve you problem you need to copy data in props
to data
on the child component when it is mounted and modify the value of data
instead.
This is the updated code example (Codepen is also updated) that does exactly what you want.
Template:
<div id="app">
<div>
<my-form :contact="contact"></my-form>
</div>
</div>
Javascript:
Vue.component('my-form', {
data() {
return { name: '', phone: '', email: '' }
},
props: {
contact: { type: Object, required: true }
},
mounted() {
this.name = this.contact.name;
this.phone = this.contact.phone;
this.email = this.contact.email;
},
template: `
<div>
<input v-model="name" />
<input v-model="phone"/>
<input v-model="email"/>
<p>
value of input: {{ JSON.stringify({ name, phone, email }) }}
</p>
</div>
`,
watch: {
name(value) {
if(value === 'john') {
this.phone = '123456';
this.email = '[email protected]';
}
}
}
});
new Vue({
el: '#app',
data() {
return { contact: { name: 'initial name', phone: '123456', email: '[email protected]' } }
}
})
And my updated code pen: https://codepen.io/aptarmy/pen/PoqgpNJ
Upvotes: 2