BadCoder
BadCoder

Reputation: 103

How to properly watch an object in Vue?

I am trying to use a Vue watcher on a computed object and it is not working at all. It works properly if it's just a string, but not if it's an object. I followed the Vue documentation but I am still getting nothing logged to the console when the object changes. The properties of the object and computed property are changing, as I have confirmed in Vue Tools. What am I doing wrong? Thanks

<v-btn small dark @click="test1.asdf = 'blah'">Test</v-btn>
    data() {
      return {
        test1: {},
      }
    },
    computed: {
      test() {
        return this.test1
      }
    },

    watch: {
      test: {
        handler: function(val, oldVal) {
          console.log(oldVal, val);
        },
        deep: true
      }
    }

Upvotes: 4

Views: 6950

Answers (3)

RuNpiXelruN
RuNpiXelruN

Reputation: 1920

@BadCoder Objects and Arrays are pass by reference in JavaScript, not pass by value. This means that when you add a key to the object as you are you doing in your question, you're just adding to the same Object. It's contents have changed but your variable test1 is still referencing the original object and unaware that its contents have updated. The watcher doesn't pick this change up. You can add deep: true to your watcher as another answerer has suggested, but this only watches for a couple of levels deep, so not ideal if you have a large object with lots of nested data. The most reliable way to trigger a watcher when dealing with arrays or objects is to create a new instance of that object. You can achieve this with object destructing. Something like,

<v-btn small dark @click="test1 = { ...test1, asdf: 'blah'}">Test</v-btn>

works because you're creating a new object (using the previous objects attributes, plus anything new), triggering the watcher.

Upvotes: 0

Ryo Shiina
Ryo Shiina

Reputation: 568

Just tried by replacing the whole object. It works pretty well, and there is no need to initialize your object with the asdf property in your data:

<v-btn small dark @click="test1 = { ...test1, asdf: 'blah'}">Test</v-btn>

The spread syntax ...test1 helps keeping other properties in the target object if there is any, so you can safely add the asdf property without worrying about losing anything.

JSFiddle: https://jsfiddle.net/vh72a3bs/22/

Upvotes: 1

Mohammad
Mohammad

Reputation: 621

Try this code, its works fine

<template>
  <div id="app">
    {{ test1 }}
    <hr />
    <button @click="test1.asdf = 'blah'">Click</button>
  </div>
</template>

<script >
export default {
  data() {
    return {
      test1: { asdf: "HEY" },
    };
  },
  computed: {
    test() {
      return this.test1;
    },
  },

  watch: {
    test: {
      handler: function (val, oldVal) {
        console.log(oldVal, val);
      },
      deep: true,
    },
  },
};
</script>

I'd guess in your case you should add .native at end of your click event like this @click.native="test1.asdf = 'blah'"

Upvotes: 3

Related Questions