Vuejs : How to pass an object as prop and have the component update sub-objects

前端 未结 5 1288
小鲜肉
小鲜肉 2021-01-02 23:16

I am trying to create a component that accepts an object as prop and can modify different properties of that object and return the value to the parent, using either sync or

相关标签:
5条回答
  • 2021-01-02 23:37

    You shouldn't modify the object being passed in as a prop. Instead, you should create a new data property in the child component and initialize it with a copy of the prop object.

    Then, I would just use a v-model for each of the inputs in the child component and add a deep watcher to the internal value which would emit an update whenever the internal value changes.

    Vue.component('child', {
      template: '#child',
      props: ['value'],
      data() {
        return { val: {...this.value} };
      },
      watch: {
        val: {
          deep: true,
          handler(value) {
            this.$emit('input', value);
          }
        }
      }
    });
    
    new Vue({
      el: '#app',
      data: {
        parentObject: {value1: "1st Value", value2: "2nd value"}
      }
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
    
    <div id="app">
      <p>Parent value: {{parentObject}}</p>
      <child v-model="parentObject"></child>
    </div>
    
    <template id="child">
      <div>
        <input type="text" v-model="val.value1">
        <input type="text" v-model="val.value2">
      </div>
    </template>

    I made a shallow copy of the prop in my example ({...this.value}), because the object doesn't have any nested properties. If that wasn't the case, you might need to do a deep copy (JSON.parse(JSON.stringify(this.value))).

    0 讨论(0)
  • 2021-01-02 23:38

    Another method without using props could be to access the components attributes $attrs in the child component.

    In parent

    <child v-model="parentObject" />
    

    And in child, the value attribute generated by v-model can be accessed in template as

    $attrs.value.value1
    

    And this is reactive.

    Demo HERE

    0 讨论(0)
  • 2021-01-02 23:41
    <TradeTableItem v-for="(debtReserve, index) in debtReserves" :key="debtReserve.id" :debtReserve="debtReserve" :market="market" :id="'order_by_' + index"></TradeTableItem>
    

    In upper step, we generated id for each rows.

    And in TradeTableItem (your template where we are populating, the table rows), Write id as :id="this.id" where this.id is a part of props.

    Hope this helps

    0 讨论(0)
  • 2021-01-02 23:58

    In #app, shoudld be parentObject, not parentValue.

    In child, you had two inpyt, but you must have a single root element. In the example below I created a <div> root element for the component.

    To update the parent, emit the events. This approach does not modify the parent's property in the child, so there's no breaking of the One-Way data flow.

    Vue.component('child', {
      template: '#child',
      
      //The child has a prop named 'value'. v-model will automatically bind to this prop
      props: ['value']
    });
    
    new Vue({
      el: '#app',
      data: {
        parentObject: {value1: "1st Value", value2: "2nd value"}
      }
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
    
    <div id="app">
      <p>Parent value: {{parentObject}}</p>
      <child v-model="parentObject"></child>
    </div>
    
    <template id="child">
    <div>
       <input type="text" v-bind:value="value.value1" v-on:input="$emit('input', {value1: $event.target.value, value2: value.value2})">
       <input type="text" v-bind:value="value.value2" v-on:input="$emit('input', {value1: value.value1, value2: $event.target.value})">
    </div>
    </template>

    About the <input>s: you can bind each to a property of the parent's value. Then, when edited, emit an event modifying just that property (v-on:input="$emit('input', {value1: $event.target.value, value2: value.value2})) and keeping the other's value. The parent updates in consequence.

    If you have many properties, you can replace in the second input, for example:

    $emit('input', {value1: value.value1, value2: $event.target.value})
    

    With

    $emit('input', Object.assign({}, value, {value2: $event.target.value}))
    


    Using a method instead of emitting directly from the template

    I kept the previous demo because it is more direct and simpler to understand (less changes from your original code), but a more compact approach would be to use a method (e.g. updateValue) and reuse it in the template:

    Vue.component('child', {
      template: '#child',
      
      //The child has a prop named 'value'. v-model will automatically bind to this prop
      props: ['value'],
      methods: {
        updateValue: function(propertyName, propertyValue) {
          this.$emit('input', Object.assign({}, this.value, {[propertyName]: propertyValue}))
        }
      }
    });
    
    new Vue({
      el: '#app',
      data: {
        parentObject: {value1: "1st Value", value2: "2nd value"}
      }
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
    
    <div id="app">
      <p>Parent value: {{parentObject}}</p>
      <child v-model="parentObject"></child>
    </div>
    
    <template id="child">
    <div>
      <input type="text" v-bind:value="value.value1" v-on:input="updateValue('value1', $event.target.value)">
      <input type="text" v-bind:value="value.value2" v-on:input="updateValue('value2', $event.target.value)">
    </div>
    </template>

    The demo above, as you can see, already uses Object.assign(), meaning it will handle an indefinite number of properties.

    0 讨论(0)
  • 2021-01-03 00:03

    It is not possible. There is a closed issue asking for it. This is the shortest method I found:

    Script

    Vue.component('child', {
      template: `
        <div>
            <input :value="parent.foo" @change="$emit('update:parent', $event.target.value)" />
        </div>
      `,
      props: {
        parent: {
          type: Object
        }
      }
    })
    
    new Vue({
      el: '#app',
      data: () =>  ({
        parent: {foo: 'a', bar: 'b'}
      }),
    
      methods: {
        mutate (value) {
            this.parent.foo = value
        }
       }
    })
    

    Template

    <div id="app">
      {{parent}}
      <child :parent="parent" @update:parent="mutate"></child>
    </div>
    

    https://jsfiddle.net/5k4ptmqg/475/

    0 讨论(0)
提交回复
热议问题