How to watch nested object properties in Vue
When it's necessary to react to a change of state in a more generic way, we can use Vue's watchers. They work quite well for most data structures, but it can cause some confusion when you're watching arrays or objects with nested properties. For example:
export default {
data() {
return {
price: 100.0
}
},
watch: {
price: function (newPreco, oldPrice) {
// whenever the car price changes, this function will be executed
}
}
}
In this case, since price
is a simple number, everytime it changes somewhere in the component, the function price
inside the watch
block will be executed. Now, let's say the price we want to keep watching is inside an object, like in the following example:
export default {
data() {
return {
car: {
color: 'black',
price: 100.0
}
}
},
watch: {
car: function (newCar, oldCar) {
// whenever the price changes, this function will be executed
// or will it?
}
}
}
Now, even change the name of the watcher to car
, if the car price changes, the function won't be executed. Well, in this case Vue has a "trick up its sleeve". You can define the watcher as an object that receives a handler
function and a property called deep
which will watch for changes in the nested properties of the object, like this:
export default {
data() {
return {
car: {
color: 'black',
price: 100.0
}
}
},
watch: {
car: {
handler: function (newCar, oldCar) {
// whenever the car price changes, this function will be executed
},
deep: true
}
}
}
Now, every time the car price changes, the watcher function will run.
We solved a problem, but introduced another: if any other property in the car object changes, for instance the color
, the watcher will execute that function.
For a long time I thought there wasn'' much to do regarding this. I would usually check if the newCar
price was different than oldCar
price and execute whatever I needed. But thanks to a tweet, I found out there was a much much simpler way to do this:
Yes, we can simplify the previous code like this:
export default {
data() {
return {
car: {
color: 'black',
price: 100.0
}
}
},
watch: {
'car.price': function (newPrice, oldPrice) {
// now, only when the car price changes, it will execute the function
}
}
}
And besides, it's clear which property you want to watch in the car object.