38

Trying to use vue watch methods but it doesn't seem to trigger for some objects even with deep:true.

In my component, I recieve an array as a prop that are the fields to create the following forms. I can build the forms and dynamicly bind them to an object called crudModelCreate and everything works fine (i see in vue dev tools and even submiting the form works according to plan)

But I have a problem trying to watch the changes in that dynamic object.

  <md-input v-for="(field, rowIndex) in fields" :key="field.id" v-model="crudModelCreate[field.name]" maxlength="250"></md-input>

   ...

   data() {
      return {
         state: 1, // This gets changed somewhere in the middle and changes fine
         crudModelCreate: {},
      }
   },
   ...
   watch: {
        'state': {
            handler: function(val, oldVal) {
                this.$emit("changedState", this.state);
                // this works fine
            },
        },
        'crudModelCreate': {
            handler: function(val, oldVal) {
                console.log("beep1")
                this.$emit("updatedCreate", this.crudModelCreate);
                // This doesn't work
            },
            deep: true,
            immediate: true
        },
    }
Sérgio Reis
  • 2,483
  • 2
  • 19
  • 32
  • How are changes in your object performed? – Sui Dream Oct 10 '17 at 12:33
  • 14
    You may have to use [`Vue.set`](https://vuejs.org/v2/api/#Vue-set) to update object properties, especially if adding new ones. – Andrei Nemes Oct 10 '17 at 12:38
  • Possible duplicate of [Vue.js watching deep properties](https://stackoverflow.com/questions/44760474/vue-js-watching-deep-properties) – Roy J Oct 10 '17 at 12:57
  • @SuiDream v-model makes the changes on the object. @AndreiNemes Absolutuly correct. On component.mounted i was calling an initializer function that would loop through the fields and set default value for the `crudModelCreate` variable (depending on the type of field) and i was doing it by doing `this.crudModelCreate[prop] = value;` changing this to `this.$set(this.crudModelCreate, prop, value);` makes the watch trigger. – Sérgio Reis Oct 10 '17 at 13:12
  • @RoyJ , you're correct, my bad, it did have alot of similarities. – Sérgio Reis Oct 10 '17 at 13:20
  • 1
    Thanks @AndreiNemes , `Vue.set` was the answer in my case. – iedmrc Jan 01 '20 at 11:49

3 Answers3

50

From the docs

Due to the limitations of modern JavaScript (and the abandonment of Object.observe), Vue cannot detect property addition or deletion. Since Vue performs the getter/setter conversion process during instance initialization, a property must be present in the data object in order for Vue to convert it and make it reactive.

Please take a look to Reactivity in Depth https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats

tony19
  • 125,647
  • 18
  • 229
  • 307
ricardoorellana
  • 2,270
  • 21
  • 35
  • 2
    Even adding the watch by doing something with the likes of `this.$watch('$data.crudModelCreate', this.updated, { deep: true })` after the values have been initialized didn't quite work. And after doing what Andrei Nemes said it worked so i guess in my use case wasn't that much of a limitation. – Sérgio Reis Oct 10 '17 at 13:18
  • 6
    Actually what the docs says it's exactly the problem you were having, when you define an object on data like `crudModelCreate: {}` and later you add a properties to it like inside the watch `crudModelCreate` those new props won't be reactive, unless you assign it as `Vue.set(object, key, value)` like @Andrei Nemes correctly mentioned it and which I point you from the docs – ricardoorellana Oct 10 '17 at 13:47
  • You have no idea how long I have been looking for an answer to this.... Thank you! – Tim B. Feb 25 '21 at 20:55
1

In certain circumstances it is possible to force a refresh by adding a key property to the child component containing a json string of the object being passed to it in v-model.

<Component v-model="deepObject" :key="JSON.stringify(deepObject)" />
  • This worked for my purposes. Put a watcher on the key instead of the object; worked like a charm. – kwphl Oct 02 '22 at 01:24
0

In case you missed it, @AndreiNemes in comments section proposed to use Vue.set() which helped me to trigger watch on adding new properties into existing object in state.

More about Vue.set(): Vue 2 documentation