I am pretty new to Vue Framework. I am trying to propagate the changes from parent to child whenever the attributes are added or removed or, at a later stage, updated outside the component. In the below snippet I am trying to write a component which shows a greeting message based on the name attribute of the node which is passed as property from the parent node.
Everything works fine as expected if the node contains the attribute “name” (in below snippet commented) when initialized. But if the name attribute is added a later stage of execution (here for demonstration purpose i have added a set timeout and applied). The component throws error and the changes are not reflected . I am not sure how I can propagate changes for dynamic attributes in the component which are generated based on other events outside the component.
Basically I wanted to update the component which displays different type of widgets based on server response in dynamic way based on the property passed to it .Whenever the property gets updated I would like the component update itself. Why the two way binding is not working properly in Vuejs?
Vue.component('greeting', { template: '#treeContainer', props: {'message':Object}, watch:{ 'message': { handler: function(val) { console.log('###### changed'); }, deep: true } } }); var data = { note: 'My Tree', // name:"Hello World", children: [ { name: 'hello' }, { name: 'wat' } ] } function delayedUpdate() { data.name='Changed World'; console.log(JSON.stringify(data)); } var vm = new Vue({ el: '#app', data:{ msg:data }, method:{ } }); setTimeout(function(){ delayedUpdate() ;}, 1000)
<script src="https://vuejs.org/js/vue.js"></script> <div id="app"> <greeting :message="msg"></greeting> </div> <script type="text/x-template" id="treeContainer"> <h1>{{message.name}}</h1> </script>
Edit 1: @Craig’s answer helps me to propagate changes based on the attribute name and by calling set on each of the attribute. But what if the data was complex and the greeting was based on many attributes of the node. Here in the example I have gone through a simple use case, but in real world the widget is based on many attributes dynamically sent from the server and each widget attributes differs based on the type of widget. like “Welcome, {{message.name}} . Temperature at {{ message.location }} is {{ message.temp}} . ” and so on. Since the attributes of the node differs , is there any way we can update complete tree without traversing through the entire tree in our javascript code and call set on each attribute .Is there anything in VUE framework which can take care of this ?
Advertisement
Answer
Vue
cannot detect property addition or deletion unless you use the set method (see: https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats), so you need to do:
Vue.set(data, 'name', 'changed world')
Here’s the JSFiddle: https://jsfiddle.net/f7ae2364/
EDIT
In your case, I think you are going to have to abandon watching the prop
and instead go for an event bus if you want to avoid traversing your data. So, first you set up a global bus for your component to listen on:
var bus = new Vue({});
Then when you receive new data you $emit
the event onto the bus with the updated data:
bus.$emit('data-updated', data);
And listen for that event inside your component (which can be placed inside the created hook), update the message and force vue
to re-render the component (I’m using ES6
here):
created(){ bus.$on('data-updated', (message) => { this.message = message; this.$forceUpdate(); }) }
Here’s the JSFiddle: https://jsfiddle.net/9trhcjp4/