Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Right way to send data to nested custom input component

I am on migrating to Vue from React, and I just liked the v-model thing Vue offers. Now I am in a situation and not able to find out what is the vue-ish way of doing this.

Here is how my component tree looks like:

- FormContainer
  -FormView
    - CustomInput
      - input

I have my states in FormContainer let's call it name and age. I want to avoid writing custom setter methods as I usually do using v-model, how can I go about passing the data down to the input component.

Currently I am doing something like this:

// in my container
<form-view :name="name" :age="age" />

// in my form view I am doing something like
<custom-input v-model="age"/>
<input v-model="name" />

both of them do not work and I get the following error Avoid mutating a prop directly

I should be able to do something like

<form-view @age="age" :age="age" @name="name" :name="name"/>

or something similar. Please let me know if you need more details on this.

like image 785
aks Avatar asked Nov 22 '25 15:11

aks


1 Answers

I've answered this question twice already, and though my answers had merit, I don't think they were quite on-target for your question, so I've deleted them.

Fundamentally, you want events to "bubble up" from the elements where the events are happening to the Vue that owns the data items. Vue events don't bubble, but native events do, so if you have a native input event being generated at the bottom of a hierarchy, you can catch it with @input.native in any component up the tree from there.

For the example you gave, if the name and age data items live in the top-level Vue, the outermost component can take them as props with the .sync modifier. That means that any update:name and update:age events from the component will result in the data items being updated. So far so good.

Moving inside the form-view component, we have a native input element. Normally, it emits native input events. We want those to bubble up as update:name events, so we handle that like so:

 <input :value="name" @input="$emit('update:name', $event.target.value)">

Now here's the fun part: inside the form-view component, we have the custom-input component. Somewhere in its heart (and we don't really care how far down that might be), it generates a native input event. Instead of catching that at each level and bubbling it up, we can let it bubble up naturally and catch it at the root of the custom-input component, and emit the required update:age event:

<custom-input :value="age" @input.native="$emit('update:age', $event.target.value)">

Putting it all together, we have two variables that are passed through components to input elements, two input event handlers, and no computeds.

new Vue({
  el: '#app',
  data: {
    name: 'Jerry',
    age: 21
  },
  components: {
    formView: {
      props: ['age', 'name'],
      components: {
        customInput: {
          props: ['value']
        }
      }
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
  <div>Name: {{name}}</div>
  <div>Age: {{age}}</div>
  <form-view :name.sync="name" :age.sync="age" inline-template>
    <div>
      <custom-input :value="age" @input.native="$emit('update:age', $event.target.value)" inline-template>
        <div>
          Age: <input :value="value">
        </div>
      </custom-input>
      <div>Name: <input :value="name" @input="$emit('update:name', $event.target.value)"></div>
    </div>
  </form-view>
</div>
like image 155
Roy J Avatar answered Nov 25 '25 05:11

Roy J