-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
$watch multiple properties for single callback #844
Comments
I don't think this is an improvement significant enough for further complicating the API, but thanks for the suggestion :) |
I know this is an old request but I've just encountered this "need" and it would be super nice to have. Rather than doing: $watch('x', function() {
this.doSomething();
});
$watch('y', function() {
this.doSomething();
});
$watch('z', function() {
this.doSomething();
}); |
You could easily do something like this:
...and then do: |
@nirazul 's solution is good, but you should maybe leave the context binding to the calling code to be more useful as a mixin: methods : {
baywatch : function(props, watcher) {
var iterator = function(prop) {
this.$watch(prop, watcher);
};
props.forEach(iterator, this);
}
},
//...
this.baywatch(['a', 'b', 'c'], this.doSomething.bind(this)); |
Yes this is a viable solution. |
How can the above be done during extend and not on per instance basis? |
You could look into making a plugin I can't write an example at the moment but look at some of the other plugins on that page and see how they add methods to the edit:
|
I was looking for this feature as well. I have three |
$watch('x', 'y', 'z', function() {
// do something
}) This form seems sensible enough to implement. If it's not hard to document, I'm for it. |
For this, I would just build a computed property which is derived from your desired properties, then watch that: const $app = new Vue({
computed: {
compoundProperty() {
// `.join()` because we don't care about the return value.
return [this.x, this.y, this.z].join()
}
},
watch: {
compundProperty() {
// do something
}
}
}) UPDATE To skip the performance penalty of allocating and discarding an array, you can comma-separate all the dependent values, and finally return something that will always be different ( computed: {
computedProperty() {
return this.x, this.y, this.z, Date.now();
}
} Some linters might complain, but it's perfectly valid JS. |
@sirlancelot Very nice solution. Whenever I need a computed property to accomplish this, I append |
It can be made simpler to just reference the reactive properties, and then return a value that's guaranteed to be different every time. new Vue({
computed: {
compoundProperty() {
// it's only required to reference those properties
this.x;
this.y;
this.z;
// and then return a different value every time
return Date.now() // or performance.now()
// object literals also work but we don't need that overhead
// return {}
// return []
}
},
watch: {
compoundProperty() {
// do something
}
}
}) |
Here's my two cents. In 1.x watchers supported expressions, so you could globally mix in a In 2.x you can not just watch an expression, and have to define a computed prop to watch instead. So the best way I can think of is to ease the creation of computed field: https://jsfiddle.net/kmj6Lsae/3/ |
Just wanted to also add my two cents. I got a situation where I also have to watch for two variables. But as already mentioned: I don't think that if it would be possible natively this would be a significant improvement, too. |
and how to debounce the callback function ? |
I also encounter a similar situation when I need to watch multiple state change in some component. computed: {
...mapGetters('bim', [
'folders',
'items'
]),
...mapState('bim', [
'projects',
'activeProject'
])
},
watch: {
// wait listProjects action to be completed before listing folder
projects: function (projectObj) {
this.listFolders()
this.listItems()
},
activeProject: function (projectName) {
this.listFolders()
this.listItems()
}
}, |
@sirlancelot very interesting solution! A small suggestion would be to use a Object ( Buggy example: https://jsfiddle.net/Sergio_fiddle/hdqgyLvx/ |
@sirlancelot thanks for a nice solution! +1 |
FYI you can directly watch a getter via this.$watch(vm => [vm.x, vm.y, vm.z].join(), val => {
// ...
}) |
i think @sirlancelot and @yyx990803 's solutions pose risks to performance because you have to construct an entirely new object just for the sake of hashing multiple watchers, which is particularly dangerous when you want to hash a large number of watchers together. wouldnt it be safer to define all the watchers in the beginning when the component is constructed? with the spread operator and a simple reducer, this can be done with relatively little clutter (my guess is this is pretty similar to how vuex does it): watch: {
...[
'key1',
'key2',
'key3'
].reduce((watchers, key) => ({
...watchers,
[key](newVal, oldVal){
// ...
}
}), {}),
} it would be nice if a utility function to map watchers were provided too import { mapWatchers } from 'vue' allowing for something like this: watch: {
...mapWatchers([
'key1',
'key2',
'key3'
], function(newVal, oldVal){
// ...
})
} or even this if we want to provide access to the key, allowing for programmatic generation of watchers: watch: {
...mapWatchers([
'key1',
'key2',
'key3'
], key => function(newVal, oldVal){
// ...
})
} |
This solution worked pretty well for me and I found it more readable then making a computed properties then watch on them. Vue.prototype.$watchAll = function(props, callback) {
props.forEach(prop => {
this.$watch(prop, callback);
});
}; and then in any component I can use it, for instance, like this this.$watchAll(["state.price", "state.amount"], this.onStateChange); based on @thattomperson suggestion |
Based on @denwerboy's solution - pass the watched property name to the callback function. Vue.prototype.$watchAll = function(props, callback) {
props.forEach(prop => {
// Pass the prop as the first argument to our callback
this.$watch(prop, callback.bind(null, prop));
});
}; Then call this.$watchAll(["field1", "field2"], function(field, value){
console.log("changed", field, value);
localStorage.setItem(field, JSON.stringify(value));
}); |
Ractive solves watching multiple paths with the asterisk, so you could do things like: this.$watch('*', function() { ... });
this.$watch('root.*', function() { ... }); // passes in individual sub-properties when they change, and not `root` Seems strange you can't easily define a single callback watcher for multiple properties in Vue. |
I wanted to watch all my data props because I emit them as v-model together in a single object. I ended up going for this: mounted () {
Object.keys(this._data).forEach(key => {
this.$watch(key, () => {
this.emit()
})
})
} |
maybe another solution could be updated() {
this.$nextTick(() => {
if (this.first || this.second) {
this.doSomething()
}
})
}, |
Evan's solution is already the best. Here's my slight adaptation to avoid array construction: this.$watch(
(vm) => (vm.x, vm.y, vm.z, Date.now()),
function () {
// Executes if `x`, `y`, or `z` have changed.
}
) |
This thread seems to have bikeshed a lot while answers have been already provided. Let's stop it here so this doesn't get worse and to prevent spamming around 20 people 😛 See #844 (comment) and #844 (comment) for solutions |
What do you think about being able to
$watch
over an array of data properties for a given callback?Not exactly pressing as I can easily make do with multiple
$watch
statements.The text was updated successfully, but these errors were encountered: