Vue.js $watch массив объектов

mounted: function() {
  this.$watch('things', function(){console.log('a thing changed')}, true);
}

things - это массив объектов [{foo:1}, {foo:2}]

$watch определяет, когда объект добавляется или удаляется, но не когда значения объекта изменяются. Как я могу это сделать?

Ответы

Ответ 1

Вы должны передать объект вместо логического значения в качестве options, поэтому:

mounted: function () {
  this.$watch('things', function () {
    console.log('a thing changed')
  }, {deep:true})
}

Или вы можете установить наблюдателя в экземпляр vue следующим образом:

new Vue({
  ...
  watch: {
    things: {
      handler: function (val, oldVal) {
        console.log('a thing changed')
      },
      deep: true
    }
  },
  ...
})

[демо]

Ответ 2

Если кому-то нужно получить элемент, который был изменен внутри массива, пожалуйста, проверьте его:

Пример JSFiddle

Код примера поста:

new Vue({
  ...
  watch: {
    things: {
      handler: function (val, oldVal) {
        var vm = this;
        val.filter( function( p, idx ) {
            return Object.keys(p).some( function( prop ) {
                var diff = p[prop] !== vm.clonethings[idx][prop];
                if(diff) {
                    p.changed = true;                        
                }
            })
        });
      },
      deep: true
    }
  },
  ...
})

Ответ 3

с https://vuejs.org/v2/api/#vm-watch:

Примечание: при замене (а не замене) объекта или массива старое значение будет совпадать с новым значением, поскольку они ссылаются на один и тот же объект/массив. Vue не хранит копию значения перед изменением.

Тем не менее, вы можете перебирать dict/array и $ watch каждый элемент независимо. то есть. $watch.foo.bar - отслеживает изменения в свойстве 'bar' в словаре 'foo'.

В этом примере мы наблюдаем все элементы в arr_of_numbers, а также свойства 'foo' всех элементов в arr_of_objects:

mounted() {
        this.arr_of_numbers.forEach( (index, val) => {
            this.$watch(['arr_of_numbers', index].join('.'), (newVal, oldVal) => {
                console.info("arr_of_numbers", newVal, oldVal);
            });
        });

        for (let index in this.arr_of_objects) {
            this.$watch(['arr_of_objects', index, 'foo'].join('.'), (newVal, oldVal) => {
                console.info("arr_of_objects", this.arr_of_objects[index], newVal, oldVal);
            });
        }

    },
    data() {
        return {
            arr_of_numbers: [0, 1, 2, 3],
            arr_of_objects: [{foo: 'foo'}, {foo:'bar'}]
        }
    }

Ответ 4

Существует более простой способ просмотра элементов массива без глубокого наблюдения: использование вычисленных значений

{
  el: "#app",
  data () {
    return {
      list: [{a: 0}],
      calls: 0,
      changes: 0,
    }
  },
  computed: {
    copy () { return this.list.slice() },
  },
  watch: {
    copy (a, b) {
      this.calls ++
      if (a.length !== b.length) return this.onChange()
      for (let i=0; i<a.length; i++) {
        if (a[i] !== b[i]) return this.onChange()
      }
    }
  },
  methods: {
    onChange () {
      console.log('change')
      this.changes ++
    },
    addItem () { this.list.push({a: 0}) },
    incrItem (i) { this.list[i].a ++ },
    removeItem(i) { this.list.splice(i, 1) }
  }
}

https://jsfiddle.net/aurelienlt89/x2kca57e/15/

Идея состоит в том, чтобы создать copy с вычисленным значением, которая имеет именно то, что мы хотим проверить. Вычисленные значения являются магическими и помещают наблюдателей только в те свойства, которые действительно были прочитаны (здесь, элементы list читаются в list.slice()). Проверки в наблюдателе copy самом деле почти бесполезны (за исключением странных угловых случаев, может быть), потому что вычисленные значения уже чрезвычайно точны.