You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5.0 KiB

AngularJS - Watch and Apply

Lesson Objectives

  1. Use $watch to listen for changes on variables
  2. 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';
		})
	})
}]);