Reputation: 356
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
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
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