[Vuejs]-What's the best way to pass and retrieve data from a child form component?

1πŸ‘

βœ…

A properly encapsulated child component would be decoupled from the parent data structure. It would take type and value as separate props, plus an opaque id to tell the parent which value the component is emitting about.

By making a settable computed based on the value parameter, the component can use v-model on its form elements. The set function emits an input event with the id and the newValue, and the parent takes it from there.

Update: I decided I didn’t like the id going to the component, so I handled that in the input handler: @input="updateField(index, $event).

new Vue({
  el: '#app',
  data: {
    schema: [{
        type: 'text',
        name: 'one',
        value: "1"
      },
      {
        type: 'textarea',
        name: 'two',
        value: "stuff in the textarea"
      }
    ]
  },
  methods: {
    updateField(index, newValue) {
      this.schema[index].value = newValue;
    }
  },
  components: {
    formInput: {
      props: ['type', 'value'],
      computed: {
        proxyValue: {
          get() { return this.value; },
          set(newValue) {
            this.$emit('input', newValue);
          }
        }
      }
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
  <div v-for="field in schema">
    {{field.name}} = {{field.value}}
  </div>
  <form-input inline-template v-for="field, index in schema" :type="field.type" :key="index" :value="field.value" @input="updateField(index, $event)">
    <div>
      <input v-if="type === 'text'" v-model="proxyValue">
      <textarea v-if="type === 'textarea'" v-model="proxyValue"></textarea>
    </div>
  </form-input>
</div>
πŸ‘€Roy J

1πŸ‘

For what you are doing here, there is no need to separate the form into a component. Just make it part of the parent and use v-model.

new Vue({
  el: '#app',
  data: {
    schema: [{
        type: 'text',
        name: 'one',
        value: "1"
      },
      {
        type: 'textarea',
        name: 'two',
        value: "stuff in the textarea"
      }
    ]
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
  <div v-for="field in schema">
    {{field.name}} = {{field.value}}
  </div>
  <div v-for="field in schema">
    <input v-if="field.type === 'text'" v-model="field.value">
    <textarea v-if="field.type === 'textarea'" v-model="field.value"></textarea>
  </div>
</div>

If you want the component for reusability and you don’t care about insulating the parent from changes (best practice is not to have anything outside a component change its data), you can just wrap the same thing in a component:

new Vue({
  el: '#app',
  data: {
    schema: [{
        type: 'text',
        name: 'one',
        value: "1"
      },
      {
        type: 'textarea',
        name: 'two',
        value: "stuff in the textarea"
      }
    ]
  },
  components: {
    formChild: {
      props: ['value']
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
  <div v-for="field in schema">
    {{field.name}} = {{field.value}}
  </div>
  <form-child inline-template v-model="schema">
    <div>
      <div v-for="field in value">
        <input v-if="field.type === 'text'" v-model="field.value">
        <textarea v-if="field.type === 'textarea'" v-model="field.value"></textarea>
      </div>
    </div>
  </form-child>
</div>
πŸ‘€Roy J

Leave a comment