Join the Stack Overflow Community
Stack Overflow is a community of 6.7 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

Here is a simplified version of my code:

Vue.component('entry', {
  template: '\
    <div>\
        <label>Name: <input type="text" v-model="entry.name"></label>\
    </div>\
  ',
  props: ['entry']
});

Vue.component('entries', {
  template: '\
    <div>\
      <entry v-for="entry in entries" :entry="entry"></entry>\
      <pre>{{ combined }}</pre>\
    </div>\
  ',
  props: ['entries'],
  computed: {
    combined: function() {
      combined = [];
      this.entries.forEach(function(entry) {
        combined.push(entry.name);
      });
      return combined.join("\n");
    }
  }
});

var entries = [
  {name: "Entry 1"},
  {name: "Entry 2"},
  {name: "Entry 3"}
];

JSFiddle here: https://jsfiddle.net/msw3Lx98/3/

The top-level component (entries) takes a list of objects, and creates a subcomponent (entry) for each one. The subcomponent binds the entry data to an input.

When the user modifies the data by typing into one of the inputs, the top-level component needs to push the updated value of combined to a separate bit of code.

My understanding of the watcher model is that if the top-level component watches entries it can detect when things are added to or removed from the list, but not when data within an entry changes.

However, the top-level component clearly does see this sort of change in some way, because as the user types into an input the value of combined gets updated.

Is there a "correct" way to detect this sort of change?

Here are a few options that I've explored:

  • Define an updated method on the subcomponent. In that method emit a custom event, and handle this event in the parent. This is fairly awkward and I don't think it will scale very well.
  • Define a beforeUpdate method on the parent -- this (but not updated) runs whenever there's a change in a subcomponent. I'm not sure why, and it doesn't smell right to me.
  • In the top-level component, watch the combined property. I'm sort of surprised this works, but it does. This seems the most straightforward option, but it's not obvious from the Vue docs that computed properties are intended to be watchable, so I'm reluctant to rely on this.
share|improve this question

One of the promise of VueJS is that it allows developers to code normally with business logic in mind, while the framework takes care of performance using its Reactivity system.

VueJS does this by defining getters and setters on known objects and arrays when the component is instantiated.

Here is an updated fiddle (forked from your version): https://jsfiddle.net/6wkyfxs6/3/

As you can see, I have added a method to addNewEntry, which allows you to add "Element 4" to the list.

It is not surprising that the first 3 elements have observer methods, as it was constructed when Vue got instantiated. But if you add "Element 4" in my new fiddle, which was not present originally, even it gets observed and changes are notified. This is how the Reactivity system works.

On deeper inspection, if you set window.entries = this.entries inside the entries component and check in console, you will notice that it has an observer method (entries.__ob__) at the top level. Here is a screenshot (taken from a local version in Google Chrome, not on jsFiddle):

Vue.js observer for an array object

This is what Vue uses to update and notify itself of changes in the Array as a whole, unlike Angular 1.x that uses dirty checking. So anytime you change something inside Vue, the Reactivity System triggers and sets up the observers (along with getters / setters) for all the new and modified objects.

The reactivity system will not trigger if you add or change an object directly in javascript. Specifically in the case of arrays (as in your example), there are some caveats as shown here: https://vuejs.org/guide/list.html#Caveats, but your example steers clear of these caveats.

In summary, VueJS allows developers like us to code normally with business logic in mind, while it takes care of the performance optimizations. I don't think you should be using any of the updated or beforeUpdate methods unless you run into some issues - in which case the first choice should be to file a bug report instead of the work-arounds.

share|improve this answer
    
This just restates what's in the Vue docs, which doesn't answer the question. I know that the data changed, and Vue knows it too. The question is where I can properly hook my own functionality to be triggered by that change. – Adam Oct 20 '16 at 16:09
    
watch is the correct place to hook your functionality for object changes like entry in your example. But if you try to watch entries, it does not work unless you are adding new entries. Only the computed property - combined - gets triggered as it depends on individual entry objects. – Mani Oct 20 '16 at 16:18

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.