Vue: set watch deep: true, when data changes, val === oldval is true

Created on 11 Apr 2016  ·  6Comments  ·  Source: vuejs/vue

Vue.js version

1.0.16

Reproduction Link

Steps to reproduce

What is Expected?

What is actually happening?

Most helpful comment

Wanted to share another solution that was provided by a user as an answer to my relative question on StackOverflow:

<div id="app">
  <input type="text" v-for="(person, index) in people" v-model="people[index].age" />
</div>

new Vue({
  methods: {
    setValue:function(){
      this.$data.oldPeople=_.cloneDeep(this.$data.people);
    },},
    mounted() {
      this.setValue();
    },
    el: '#app',
    data: {
      people: [
      {id: 0, name: 'Bob', age: 27},
      {id: 1, name: 'Frank', age: 32},
      {id: 2, name: 'Joe', age: 38}
      ],
      oldPeople:[]
    },
    watch: {
      people: {
        handler: function (after, before) {
        // Return the object that changed
        var vm=this;
        let changed = after.filter( function( p, idx ) {
          return Object.keys(p).some( function( prop ) {
           return p[prop] !== vm.$data.oldPeople[idx][prop];
         })
        })
        // Log it
        vm.setValue();
        console.log(changed)
      },
      deep: true,
    }
  }
})

With a note: "if you are adding or removing something from the original array, just call the vm.setValue() method again"

All 6 comments

This is working as intended, because they are the same object.

I have a very deep object and some key in it changes, is there any method I can get the changed key name, value and it's parent object? I don't need a whole object, thanks.

@murphymeng You may want to clone the original object and compare the clone with the modified object during watch callback. Also, please direct these questions to Gitter or the forum. Thanks.

@murphymeng you would have to:

  1. Avoid using v-model and .sync in favor of unidirectional data flow (:value="..." @change="...").
  2. Use immutable data (there's a useful library called object-path-immutable).
  3. Whenever watch triggered, run a comparison to figure out what has changed and what didn't.

Wanted to share another solution that was provided by a user as an answer to my relative question on StackOverflow:

<div id="app">
  <input type="text" v-for="(person, index) in people" v-model="people[index].age" />
</div>

new Vue({
  methods: {
    setValue:function(){
      this.$data.oldPeople=_.cloneDeep(this.$data.people);
    },},
    mounted() {
      this.setValue();
    },
    el: '#app',
    data: {
      people: [
      {id: 0, name: 'Bob', age: 27},
      {id: 1, name: 'Frank', age: 32},
      {id: 2, name: 'Joe', age: 38}
      ],
      oldPeople:[]
    },
    watch: {
      people: {
        handler: function (after, before) {
        // Return the object that changed
        var vm=this;
        let changed = after.filter( function( p, idx ) {
          return Object.keys(p).some( function( prop ) {
           return p[prop] !== vm.$data.oldPeople[idx][prop];
         })
        })
        // Log it
        vm.setValue();
        console.log(changed)
      },
      deep: true,
    }
  }
})

With a note: "if you are adding or removing something from the original array, just call the vm.setValue() method again"

@phanan Thank you! It's working. I think for very deep and complex object plus key occurrence uncertain, this way is much easier.
use Copy = Object.assign({},Target) to create a new object.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

franciscolourenco picture franciscolourenco  ·  3Comments

loki0609 picture loki0609  ·  3Comments

seemsindie picture seemsindie  ·  3Comments

lmnsg picture lmnsg  ·  3Comments

bfis picture bfis  ·  3Comments