5.0 KiB
AngularJS - Watch and Apply
Lesson Objectives
- Use $watch to listen for changes on variables
- Use $apply to force a redraw of the page
Use $watch to listen for changes on variables
Use 1
We can create event listeners for any property of the $scope variable. This can be useful if you have a scope variable that changes frequently from multiple sources, but you want to perform the same action each time it updates.
$scope.$watch('somevar', function(newValue, oldValue){
//will run whenever $scope.somevar changes
});
Example 1
file: index.html
<!DOCTYPE html>
<html >
<head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body ng-app="MyApp">
<main ng-controller="MainController as main">
{{somevar}}
<button ng-click="main.changeScopeVar();">Click Me</button>
</main>
</body>
</html>
file: js/app.js
const app = angular.module('MyApp', []);
app.controller('MainController', ['$scope', function($scope){
$scope.somevar = 'foo';
$scope.$watch('somevar', function(newValue, oldValue){
//will run whenever $scope.somevar changes
console.log(newValue, oldValue);
});
this.changeScopeVar = function(){
$scope.somevar = 'bar';
};
}]);
Use 2
If no string is passed as first parameter. Entire scope is watched. This is useful for controller properties when controller alias is not known.
NOTE that the two parameters for the old value and new value of $scope will appear identical, since they both point to the same object in memory.
$scope.$watch(function(newValue, oldValue){
//will run whenever $scope changes
//NOTE: newValue and oldValue will have same property values, since both point the same object, which has been modified
});
Example 2
file: index.html
<!DOCTYPE html>
<html >
<head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body ng-app="MyApp">
<main ng-controller="MainController as main">
{{somevar}}
<button ng-click="main.changeScopeVar();">Click Me</button>
</main>
</body>
</html>
file: js/app.js
const app = angular.module('MyApp', []);
app.controller('MainController', ['$scope', function($scope){
$scope.somevar = 'foo';
$scope.$watch(function(newValue, oldValue){
//will run whenever $scope.somevar changes
console.log(newValue, oldValue);
});
this.changeScopeVar = function(){
$scope.somevar = 'bar';
};
}]);
Use 3
You can also watch any variables by passing in a function as the first param that returns the variable to be watched
$scope.$watch(function(){ return foo; }, function(newValue, oldValue){
//will fire when global foo variable is changed
});
Example 3
file: index.html
<!DOCTYPE html>
<html >
<head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body ng-app="MyApp">
<main ng-controller="MainController as main">
<button ng-click="main.clickFunction()">Click Me</button>
</main>
</body>
</html>
file: js/app.js
const app = angular.module('MyApp', []);
const someThirdPartyVariable = 'foo';
app.controller('MainController', ['$scope', function($scope){
$scope.$watch(
function(){ return someThirdPartyVariable; },
function(newValue, oldValue){
//will fire when global foo variable is changed
console.log(newValue, oldValue);
}
);
this.clickFunction = function(){
someThirdPartyVariable = 'bar';
}
}]);
Use $apply to force a redraw of the page
Angular doesn't watch $scope properties constantly for changes. This would be processing intensive. Instead, it has listeners for events like ng-click, ng-submit, etc. When these fire, Angular looks at the scopes to see if anything changes. If there's a change, Angular will call the $digest cycle, which basically means it will redraw the view.
If you change a $scope property outside of the Angular environment (perhaps through a third party JS library), the view won't refresh. You can manually force the $digest cycle, though. To do this, simply call:
$scope.$apply(function(){
//once the code in here completes, force the $digest cycle to run
});
Example
file: index.html
<!DOCTYPE html>
<html >
<head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body ng-app="MyApp">
<main ng-controller="MainController as main">
{{foo}}
<button>Click Me</button>
</main>
</body>
</html>
file: js/app.js
const app = angular.module('MyApp', []);
app.controller('MainController', ['$scope', function($scope){
$scope.foo = 'foo';
document.querySelector('button').addEventListener('click', function(){
$scope.$apply(function(){
$scope.foo = 'bar';
})
})
}]);