1

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

9
  • Aside from the unnecessary separation of :value and @input (just use v-model), I don't see any issue with what you're doing here. Maybe I misunderstood your question? Commented Apr 2, 2020 at 5:51
  • @DecadeMoon It doesn't work sadly. Should it? Commented Apr 2, 2020 at 5:52
  • Nevermind, I re-read your code and found some syntax errors. See my answer. Commented Apr 2, 2020 at 5:58
  • Those edits you made clears things up, thanks. I still think v-model can (and should) be used here, but since we're dealing with a custom component, you may not be receiving the native event object for the input event that it emits. Can you confirm that? Commented Apr 2, 2020 at 6:24
  • @DecadeMoon How would I know when that event is received? I know it's firing based on the Vue console but it throws an error for Error in v-on handler: "TypeError: Cannot read property 'value' of undefined" Commented Apr 2, 2020 at 6:29

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

8 Comments

Great! Let me try to implement this.
@user3162553 I have tested it, and it works. please tell me if it works for you.
It does work but I cannot change the phone or email values when state is pre-selected. That's just supposed to be the default. I forgot to mention that though, so I will upvote this regardless.
@user3162553 I just updated my code example in my answer to make phone and email updatable. My Codepen is also updated, so please check it out.
@user3162553 my code is updated. The cause of problems why your code doesn't work is explained in my updated answer. This is because you are trying to modify the value of props which is not allowed by Vue. Data flown through props is One-Way binding. please reread my answer, take a look at updated Codepen, update your code, and please tell me if it works now.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.