Core points
$watch
function in AngularJS is a powerful tool for observing changes in variable values or expressions. When a change is detected, it triggers a callback function that is executed every time the monitored variable changes. $watch
Use JavaScript's equality operator (===) for comparison. If the new value is different from the old value, the callback function is triggered. However, it should be noted that by default, $watch
only checks for reference equality, which means that the callback function is triggered only when a new value is assigned to the monitored variable. $watchGroup
and $watchCollection
as convenient shortcuts for setting up multiple monitors or monitoring arrays or objects with the same callback function, respectively. However, these methods only perform shallow monitoring and react only to reference changes. $watch
, especially on multiple variables, may affect performance because changes in all monitored variables need to be checked for each summary cycle. Developers should consider using $watchGroup
or $watchCollection
depending on the situation, or limit the number of monitored variables to improve performance. This article was reviewed by Mark Brown. Thanks to all the peer reviewers of SitePoint for getting SitePoint content to its best!
AngularJS offers many different options to use the publish-subscribe mode through three different "watch" methods. Each method takes optional parameters to modify its behavior.
The official documentation about $watch
is far from exhaustive: after all, this is a problem that bothers AngularJS v1 as a whole. Even online resources that explain how to proceed are fragmented at best.
So, ultimately, it is difficult for developers to choose the right approach for a particular situation. This is especially true for beginners in AngularJS! The results can be surprising or unpredictable, which inevitably leads to errors.
In this article, I will assume that you are familiar with the AngularJS concept. If you feel you need to review, you may need to read about $scope
, bindings, and $apply
and $digest
.
Check your understanding
For example, what is the best way to monitor the first element of an array? Suppose we declare an array on our scope, $scope.letters = ['A','B','C']
;
$scope.$watch('letters', function () {...});
trigger its callback function? $scope.$watch('letters[0]', function () {...});
? Will it work the same way, or is it better? $watch
, $watchCollection
and $watchGroup
? If you are confused by all these questions, please continue reading. My goal is to walk you through the process by explaining this as clearly as possible with a few examples.
$scope.$watch
Let's start with $scope.$watch
. Here is the core of all watch features: every other method we will see is just a convenient shortcut to $watch
.
$watch
The advantage of Angular is that you can explicitly use the same mechanism to perform complex operations triggered by data changes in the controller. For example, you can set up a monitor for certain data that can be changed in response to:
You can set up just one listener to handle any data changes, no matter what the cause is.
However, to do this, you need to call $scope.$watch
yourself.
Let's look at the code of $rootscope.watch()
.
This is its signature: function(watchExp, listener, objectEquality, prettyPrintExpression)
.
In detail, its four parameters:
watchExp
Monitored expression. It can be a function or a string, and it is evaluated in every digest cycle.
A key aspect to note here is that If the expression is evaluated as a function, the function needs to be idempotent. In other words, for the same input set it should always return the same output. If this is not the case, Angular will assume that the monitored data has been changed. This in turn means it will continue to detect differences and call the listener in each iteration of the digest cycle.
listener
A callback function that is fired when the monitor is first set up, and then every time a change in the watchExp
value is detected during the summary cycle. The initial call at setting is intended to store the initial value for the expression.
objectEquality
The monitor will perform a depth comparison if and only if this value is true. Otherwise, it performs shallow comparisons, i.e. only comparison references.
Let's take array as an example: $scope.fruit = ["banana", "apple"]
;
objectEquality == false
means that only the reassignment of the fruit
field will cause the listener to be called.
We also need to check how deep the “deep” is: we will discuss this later.
prettyPrintExpression
If passed, it will override the monitoring expression. This parameter is not intended to be used in normal calls to $watch()
; it is used internally by the expression parser.
Beware: As you can see yourself, unexpected results are prone to unexpected results when the fourth parameter is passed accidentally.
Now we are going to answer some of the questions in the introduction. Please check out the examples of this section:
Feel free to familiarize yourself with them; you can directly compare behavior differences, or do it in the order in the article.
So you need to monitor the array on the scope to make changes, but what does "change" mean?
Suppose your controller looks like this:
app.controller('watchDemoCtrl', ['$scope', function($scope){ $scope.letters = ['A','B','C']; }]);
One option is to use a call like this:
$scope.$watch('letters', function (newValue, oldValue, scope) { // 对 $scope.letters 执行任何操作 });
In the above callback, newValue
and oldValue
have self-explanatory meanings and are updated every time the $digest
cycle calls it. The meaning of scope
is also intuitive, because it holds references to the current scope.
But the key is: When will this listener be called? In fact, you can add, delete, replace elements in an array without anything happening. This is because, by default, letters
assumes that you only want $watch
reference equality, so the callback function is triggered only when you assign a new value to . $scope.letters
as the third parameter to true
(i.e. as the value of the optional watch
parameter described above). objectEquality
$scope.$watch('letters', function (newValue, oldValue, scope) { // 对 $scope.letters 执行任何操作 }, true);
is false, you just monitor any reassignment to that scope variable, and if true, the callback function will be triggered every time the element in the object is changed. objectEquality
, each time the callback function is triggered, objectEquality === true
and newValue
will be the old and new values of the entire array. So you have to compare them with each other to understand what actually changes. oldValue
as the first argument: $watch
$scope.$watch('letters[4]', function (newValue, oldValue, scope) { //... }, true);
If you record oldValue
, you will see that in both cases it will be undefined. Compare this to what happens when monitoring existing elements: When set up, you still have oldValue == undefined
. So $watch
cannot be processed!
A more interesting question now: Do we need to pass objectEquality === true
here?
Short answer: Sorry, there is no short answer.
It really depends on:
objectEquality
. $scope.board = [[1, 2, 3], [4, 5, 6]]
; and we want to monitor the first row. Then we might want to get an alert when an assignment like $scope.board[0][1] = 7
changes it. Perhaps more useful than monitoring any element in an array, we can monitor any field in an object. But that's not surprising, right? After all, an array in JavaScript is a object.
app.controller('watchDemoCtrl', ['$scope', function($scope){ $scope.letters = ['A','B','C']; }]);
At this point, we also need to clarify one last but crucial detail: What happens if we need to monitor a complex nested object where each field is a non-primitive value? For example, a tree or graph, or just some JSON data.
Let's check it out!
First of all, we need an object to monitor:
$scope.$watch('letters', function (newValue, oldValue, scope) { // 对 $scope.letters 执行任何操作 });
Let's set up the monitor for the entire object: I think so far it's clear that in this case objectEquality
must be set to true
.
$scope.$watch('letters', function (newValue, oldValue, scope) { // 对 $scope.letters 执行任何操作 }, true);
The question is: if assignments like $scope.b.bb[1].bb2a = 7;
happen, will Angular be kind enough to let us know?
The answer is: Yes, luckily, it will (see in the previous CodePen demo).
Other methods
$scope.$watchGroup
$watchGroup()
Is it really a different approach? The answer is no, it is not.
$watchGroup()
is a convenient shortcut that allows you to set up multiple monitors using the same callback function and pass an array of watchExpressions
.
Each passed expression will be monitored using the standard $scope.$watch()
method.
$scope.$watch('letters[4]', function (newValue, oldValue, scope) { //... }, true);
It is worth noting that using $watchGroup
, newValues
and oldValues
will save the list of values of the expression, including the values that have changed and those that have kept the same value, in the order of them in the first parameter The order of passing in the array is the same.
If you checked the documentation for this method, you may notice that it does not take the objectEquality
option. This is because it shallowly monitors expressions and only reacts to reference changes.
If you use the $watchGroup()
demo below, you may be surprised by some subtleties. For example, unshift
will cause the listener to be called, at least to some extent: This is because when passing the expression list to $watchGroup
, any triggers an expression that will result in execution Callback function.
will not produce any updates - updates will only be generated if a new value is assigned to the b field itself. $scope.obj.b
$scope.$watchCollection
does not allow $watchCollection()
, so it only shallowly monitor elements/fields and does not react to changes in their subfields. objectEquality
Conclusion
Hope these examples help you discover the power of this Angular feature and understand how important it is to use the right options.Feel free to copy CodePen and try to use these methods in different contexts and don't forget to leave your feedback in the comments section!
If you want to have a deeper look at some of the concepts we discussed in this article, here are some suggestions for further reading:
$apply()
$digest()
$watch
FAQs (FAQ)$watch
in AngularJS
$watch
What is the main purpose in AngularJS? The $watch
function in AngularJS is mainly used to observe changes in the value of a variable or expression. It is part of AngularJS scoped object to monitor changes in the value of a variable or expression. When a change is detected, the $watch
function triggers a callback function that is executed every time the monitored variable changes.
$watch
How does it work in AngularJS? The $watch
function in AngularJS works by comparing the old and new values of the monitored variable or expression. It uses JavaScript's equality operator (===) for comparison. If the new value is different from the old value, the $watch
function will trigger the callback function.
$watch
in AngularJS? To use $watch
in AngularJS, you need to call the $watch
method on the scope object and pass it two parameters: the name of the variable or expression to be monitored, and happens when the variable being monitored The callback function to be executed when changing. Here is an example:
app.controller('watchDemoCtrl', ['$scope', function($scope){ $scope.letters = ['A','B','C']; }]);
$watch
and $apply
in AngularJS? function in $watch
in AngularJS is used to observe changes in variables or expressions, while the $apply
function is used to manually start the AngularJS digest period, which checks for any changes in the monitored variable and updates the view accordingly. The $apply
function is usually used when making model changes outside the AngularJS context, such as in the DOM event handler or the setTimeout
function.
$watch
? Yes, you can use $watch
to monitor multiple variables in AngularJS. You can do this by passing an array of variable names to the $watch
function. However, remember that monitoring multiple variables can affect performance, because the $watch
function needs to check for changes in all monitored variables in each digest cycle.
$watch
in AngularJS? When you call the $watch
function in AngularJS, it returns a logout function. You can call this function to stop monitoring variables. Here is an example:
$scope.$watch('letters', function (newValue, oldValue, scope) { // 对 $scope.letters 执行任何操作 });
$watchGroup
in AngularJS? The $watchGroup
function in AngularJS is used to monitor a set of expressions. It works like the $watch
function, but it triggers the callback function only once per digest cycle, even if multiple monitored expressions change. This can improve performance when monitoring multiple expressions.
$watchCollection
in AngularJS? The $watchCollection
function in AngularJS is used to monitor the attributes of an object or elements of an array. It triggers the callback function as long as any attribute or element changes, but unlike $watch
, it does not deeply monitor objects or arrays, which can improve performance.
$watch
in AngularJS directive? Yes, you can use $watch
in the AngularJS directive. In fact, it is common to use $watch
in directives to respond to changes in attributes or scope variables of directives.
$watch
? Using $watch
in AngularJS can affect performance, especially when monitoring many variables or expressions. This is because the $watch
function needs to check the changes of all monitored variables in each digest cycle. To improve performance, consider using $watchGroup
or $watchCollection
according to the situation, or limit the number of monitored variables.
The above is the detailed content of Mastering $watch in AngularJS. For more information, please follow other related articles on the PHP Chinese website!