Hưng hoàng
Hưng hoàng

Reputation: 356

Passing the properties of an object as props Vuejs

hi everyone, I have some confusion. I have two component ( child and parent component) and I pass the properties of an object as props

<child :child-data="abc" ></child>
Vue.component('childComponent', {
props: ['childData'],
 data: function(){
    return {
        count:this.childData,// recommend use Vue.util.extend
    }
 },
});

Vue will recursively convert "data" properties of childComponent into getter/setters to make it “reactive”. So why doesn't it automatic bind data to the template? I read some recommend use Vue.util.extend. Why Vue.util.extend?

UPDATE

my example: https://jsfiddle.net/hoanghung1995/xncs5qpd/56/

when i set default value of parentData ,childDataA will display it. But when i use v-model to override parentData then childDataA not “reactive”. I must use "watch" to override "data" ,similar to childDataB

Vue.util.extend example: https://jsfiddle.net/sm4kx7p9/3/

Why do Vue.util.extend work fine but not use "watch"?,

Upvotes: 1

Views: 2202

Answers (2)

Terry
Terry

Reputation: 66218

To explain what is actually happening in the background, Linus Borg has as excellent answer for your question. To summarize his answer, the reason why your approach doesn't work is being data is a computed property while props are being passed in as primitive types. In other words, data makes a copy of your props (instead of passing by reference).

Another way to bypass this is to declare your childData as computed properties instead of data, i.e.:

computed: {
    childDataA() {
        return this.childPropsA;
    },
    childDataB() {
        return this.childPropsB;
    }
}

The reason why using computed works is because the computed properties now watches changes to their dependencies.

A proof-of-concept example based on your original fiddle:

Vue.component('child', {
  props: ['childPropsA', 'childPropsB'],
  template: "#sub",
  computed: {
    childDataA() {
      return this.childPropsA;
    },
    childDataB() {
      return this.childPropsB;
    }
  }
});
new Vue({
  el: '#app',
  data: {
    parentData: '123'
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
  parentData:{{parentData}}<br>
  <input type="text" v-model="parentData">
  <child :child-props-a="parentData" :child-props-b="parentData"></child>
</div>
<template id="sub">
  <div>
    <p> 1- {{ childDataA }}</p> 
    <p> 2- {{ childDataB }}</p> 
    </div>
</template>

The approach above is functionally identical to the data + watch approach, but I find it rather cumbersome and adds unnecessary verbosity to your code:

data: function() {
    return {
      childDataA: this.childPropsA,
      childDataB: this.childPropsB
    };
  },
  watch: {
    childPropsA() {
      this.childDataA = this.childPropsA;
    },
    childPropsB() {
      this.childDataB = this.childPropsB;
    }
  }

Vue.component('child', {
  props: ['childPropsA', 'childPropsB'],
  template: "#sub",
  data: function() {
    return {
      childDataA: this.childPropsA,
      childDataB: this.childPropsB
    };
  },
  watch: {
    childPropsA() {
      this.childDataA = this.childPropsA;
    },
    childPropsB() {
      this.childDataB = this.childPropsB;
    }
  }
});
new Vue({
  el: '#app',
  data: {
    parentData: '123'
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
  parentData:{{parentData}}<br>
  <input type="text" v-model="parentData">
  <child :child-props-a="parentData" :child-props-b="parentData"></child>
</div>
<template id="sub">
  <div>
    <p> 1- {{ childDataA }}</p> 
    <p> 2- {{ childDataB }}</p> 
    </div>
</template>

Upvotes: 1

reinerBa
reinerBa

Reputation: 1660

Watch does only watch properties which are indeed reactive. Passing objects doesn`t makes their properties reactive. Just pass the properties that you want as props. With Vue.util.extend you force the property to be reactive in this instance.

Upvotes: 0

Related Questions