Vuejs : How to pass an object as prop and have the

2019-02-25 16:10发布

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 emit events. The example won't work, but it's simply to demonstrate what I'm trying to achieve.

Here's a snippet of what I'm trying to achieve :

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 (value) {
      //Not sure if both fields should call the same updateValue method that returns the complete object, or if they should be separate
      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.js"></script>

<div id="app">
  <p>Parent value: {{parentObject}}</p>
  <child v-model="parentObject"></child>
</div>

<template id="child">
   <input type="text" v-bind:value.value1="value" v-on:input="updateValue($event.target.value)">
   <input type="text" v-bind:value.value2="value" v-on:input="updateValue($event.target.value)">
</template>

标签: vue.js vuejs2
3条回答
小情绪 Triste *
2楼-- · 2019-02-25 16:55

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.

查看更多
够拽才男人
3楼-- · 2019-02-25 17:05

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))).

查看更多
Lonely孤独者°
4楼-- · 2019-02-25 17:07

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/

查看更多
登录 后发表回答