vue js watch multiple properties with single handler

vue.jsVue Loader

vue.js Problem Overview


Currently I have to watch a few properties. And if each of them changes, I have to invoke the same function:

export default{
  // ...... rest of code 
  watch: {
    propa: function(after,before) {
      doSomething(after,before);
    },
    propb: function(after,before) {
      doSomething(after,before);
    }
    // ... so on
  }
}

So I am having to write the same code multiple times above. Is it possible to simply have all properties being watched and invoke their change handler without having to write same code multiple times?

PS: I am using vue 1.x

vue.js Solutions


Solution 1 - vue.js

Update: April-2020

For people who are using Vue 3, the watch API can accept multiple sources

import { watch, ref } from 'vue';

export default {
  setup(() => {
    const a = ref(1), b = ref('hello');

    watch([a, b], ([newA, newB], [prevA, prevB]) => {
      // do whatever you want
    });
  });
};


Original answer for Vue 2

there is no official way to solve your question(see this). but you can use the computed property as a trick:

    export default {
      // ...
      computed: {
        propertyAAndPropertyB() {
          return `${this.propertyA}|${this.propertyB}`;
        },
      },
      watch: {
        propertyAAndPropertyB(newVal, oldVal) {
          const [oldPropertyA, oldProvertyB] = oldVal.split('|');
          const [newPropertyA, newProvertyB] = newVal.split('|');
          // doSomething
        },
      },
    }

if you just want to do something and don't care about what's new/old values. ignore two lines

    const [oldPropertyA, oldProvertyB] = oldVal.split('|');
    const [newPropertyA, newProvertyB] = newVal.split('|');

Solution 2 - vue.js

Another possibility:

new Vue({
  el: '#app',
  data: {
    name: 'Alice',
    surname: 'Smith',
    fullName: '' // IRL you would use a computed for this, I'm updating it using a watch just to demo how it'd be used
  },
  mounted() {
    this.$watch(vm => [vm.name, vm.surname], val => {
      
      this.fullName = this.name + ' ' + this.surname;
      
    }, {
      immediate: true, // run immediately
      deep: true // detects changes inside objects. not needed here, but maybe in other cases
    }) 
  }
});

<script src="https://unpkg.com/vue"></script>

<div id="app">
  <div>
    name:<input v-model="name">
  </div>
  <div>
    surname:<input v-model="surname">
  </div>
  <div>
    full name: {{ fullName }}
  </div>
</div>

More info on the Vue API docs for vm.$watch.

Solution 3 - vue.js

like this:

data() {
  return {
    propa: '',
    propb: ''
  }
},
computed: {
  changeData() {
    const { propa, propb } = this
    return {
      propa,
      propb
    }
  }
},
watch: {
  changeData: {
    handler: function(val) {
      console.log('value change: ', val)
    },
    deep: true
  }
}

Solution 4 - vue.js

First, your definition could be simplified. doSomething does not appear to be a method on the Vue, so your watch could just be

watch:{
    propa: doSomething,
    propb: doSomething
}

Second, sometimes it's important to remember Vue definition objects are just plain javascript objects. They can be manipulated.

If you wanted to watch every property in your data object, you could do something like this

function doSomething(after, before){
  console.log(after,before);
}

function buildWatch(def){
  if (!def.watch)
    def.watch = {};
  for (let prop of Object.keys(def.data))
    def.watch[prop] = doSomething;
  return def;
}

let vueDefinition = {
  data:{
    propa: "testing",
    propb: "testing2",
    propc: "testing3"
  }
}

export default buildWatch(vueDefinition)

If you wanted to watch only some defined list of your properties:

// First argument is the definition, the rest are property names
function buildWatch(def){
  if (!def.watch)
    def.watch = {};
  const properties = Array.prototype.slice.call(arguments,1); 
  for (let prop of properties)
    def.watch[prop] = doSomething;
  return def;
}

export default buildWatch(vueDefinition, "propa", "propb")

Solution 5 - vue.js

For Vue typescript you can do like this. Tested.

  @Watch('varA')
  @Watch('varB')
  private oneOfAboveChanged(newVal) {
    console.log(newVal)
  }

Solution 6 - vue.js

My resolution for vue2:

export default {
  data() {
    return {
      status: null,
      name: null,
      date: null,
      mobile: null,
      page: 1,
    }
  },
  watch: {
    ...["status", "name", "date", "mobile", "page"].reduce((acc, currentKey) => {
      acc[currentKey] = (newValue) => {
        // doSomething
        // console.log(newValue, currentKey)
      }
      return acc
    }, {}),
  }
}

Solution 7 - vue.js

vm.$data

If you want to listen to all the properties inside data(), you can use this.$data

<script>
export default {
  data () {
    return {
      propA: 'Hello',
      propB: 'world'
    }
  }
  watch: {
    $data (newValue) { // Watches for any changes in data()
      // Do something with the new data
    }    
  }
}
</script>

Solution 8 - vue.js

Old question but the answer still may be useful for those who are still working in vue 1.

You can watch multiple props by wrapping them in quotes:

data() {
   return {
      foo: {
         prop1: 1,
         prop2: 2,
      }
   }
}
watch: {
    '[foo.prop1, foo.prop2]'(newVal, oldVal) {
        //do sth
        console.log(newVal); // prints ([prop1, prop2])
        console.log(oldVal); // prints ([prop1, prop2])
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionrahulserverView Question on Stackoverflow
Solution 1 - vue.jsYi Feng XieView Answer on Stackoverflow
Solution 2 - vue.jsacdcjuniorView Answer on Stackoverflow
Solution 3 - vue.jsgold threeView Answer on Stackoverflow
Solution 4 - vue.jsBertView Answer on Stackoverflow
Solution 5 - vue.jsBinh HoView Answer on Stackoverflow
Solution 6 - vue.jsMac ChowView Answer on Stackoverflow
Solution 7 - vue.jsW4G1View Answer on Stackoverflow
Solution 8 - vue.jsKendzioView Answer on Stackoverflow