Vue: how to track changes for array/list elements?

Created on 7 Apr 2014  Â·  6Comments  Â·  Source: vuejs/vue

Hi,

I need to create directive that accepts a list (or array) binding - similar to v-repeat. Is there a recommended way of determining what actually has changed in such bound list?

Let's say I want to remove one element in the middle of the list. Is there API for detecting such changes?

Actually, if a list has changed, only three options are valid:

  • some elements (one or more) have been removed)
  • some elements have been inserted
  • some elements have changed their positions (index)

...or a combination of above. It would be great if there was some kind of built-in mechanism for detecting such changes for array-like parameters :)

All 6 comments

when you do vm.$watch() on an array, the callback's second parameter is mutation. If it exists, it means the array itself mutated. It's an object containing the mutation info.

this.$watch('list', function (value, mutation) {
    if (mutation) {
        mutation.method // e.g. 'push'
        mutation.args // raw arguments to the mutation method
        mutation.result // return value
        mutation.inserted // new, inserted elements
        mutation.removed // removed elements
    }
})

It works fine, many thanks! :)

when you do vm.$watch() on an array, the callback's second parameter is mutation

No it's not. In my component's created hook:

this.$watch('users', function(value, mutation) {
    console.log(mutation);
});

Expected: log this mutation object you speak of
Actual: logs the array itself

Was this reverted?

Hi @CrescentFresh - this issue was made two years ago when Vue was at version 0.10. You can find the current docs for $watch here.

Reading the docs and looking at the relevant code I see there is no information made available to watchers to know what mutation event caused the array to change. Is that an accurate statement?

If so, :(

when you do vm.$watch() on an array, the callback's second parameter is mutation. If it exists, it means the array itself mutated. It's an object containing the mutation info.

this.$watch('list', function (value, mutation) {
    if (mutation) {
        mutation.method // e.g. 'push'
        mutation.args // raw arguments to the mutation method
        mutation.result // return value
        mutation.inserted // new, inserted elements
        mutation.removed // removed elements
    }
})

I can confirm this feature doesn't exist anymore in Vue. So I've had to reproduce it myself.

The below code is not bulletproof but at least it detects arr.push, arr.splice and this.$set(arr, index, something) – I also set up a quick demo:

function eventify (arr) {
  let eventify = function(arr) {
    arr.isEventified = true;
    // overwrite 'push' method
    let pushMethod = arr.push;
    arr.push = function(e) {
      pushMethod.call(arr, e);
      // the user added something into the array using 'push'
      // so at this point I can trigger my own code
    };

    // overwrite 'splice' method
    let spliceMethod = arr.splice;
    arr.splice = function() {
      let args = [], len = arguments.length;
      while (len--) args[len] = arguments[len];
      spliceMethod.apply(arr, args);
      let idx = args[0];
      let nbDel = args[1];
      let elemsLen = args.length - 2;

      if (elemsLen === 0) {
        // we reach this part of the code when the user removed some elements from the array
        nbDel += idx;
        while(idx < nbDel) {
          // element at position 'idx' has been deleted
          idx++;
        }
      }
      else if (elemsLen > 0) {
        // we reach this part of the code when the user used this.$set(list, i, "new value")
        for (let i=2; i<elemsLen+2; i++) {
          // element at position 'idx' has been changed
          idx++;
        }
      }
    };
  };

  // make sure we don't "eventify" this array
  if (!arr.isEventified) eventify(arr);
}

// watch the array called 'list'
this.eventify(list);
Was this page helpful?
0 / 5 - 0 ratings