A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from http://www.bennadel.com/blog/2566-scope-watch-vs-watchcollection-in-angularjs.htm below:

Scope $watch() vs. $watchCollection() In AngularJS

AngularJS has always had a Scope.$watch() function as means to observe [and react to] changes in a given value. With AngularJS 1.1.4, however, they added the Scope.$watchCollection() function as a means to observe changes in a collection (either as an Array or an Object). Between the two current functions, there are three unique ways to watch a value for changes. And to be honest, it can get a bit confusing. As such, I wanted to take a quick look at these three different watch-configurations and nail down what kind of changes each one will track.

Run this demo in my JavaScript Demos project on GitHub.

By default, the $watch() function only checks object reference equality. This means that within each $digest, AngularJS will check to see if the new and old values are the same "physical" object. This means that the vanilla $watch() statement will only invoke its handler if you actually change the underlying object reference.

The $watch() function takes a third, optional argument for "object equality." If you pass-in "true" for this argument, AngularJS will actually perform a deep-object-tree comparison. This means that within each $digest, AngularJS will check to see if the new and old values have the same structure (not just the same physical reference). This allows you to monitor a larger landscape; however, the deep object tree comparison is far more computationally expensive.

With AngularJS 1.1.4, the $watchCollection() function was added for collection-oriented change management. The $watchCollection() function is a sort-of mid-ground between the two $watch() configurations above. It's more in-depth than the vanilla $watch() function; but, it's not nearly as expensive as the deep-equality $watch() function. Like the $watch() function, the $watchCollection() works by comparing physical object references; however, unlike the $watch() function, the $watchCollection() goes one-level deep and performs an additional, shallow reference check of the top level items in the collection.

To see this in action, I've put together a demo that uses all three watch configurations to observe changes in a single array array. Then, I've provided several means to change the structure of the array. Each watch function tracks and logs the changes it observes.

<!doctype html>
<html ng-app="Demo">
<head>
	<meta charset="utf-8" />

	<title>
		Scope $watch() vs. $watchCollection() In AngularJS
	</title>

	<style type="text/css">

		a[ ng-click ] {
			cursor: pointer ;
			text-decoration: underline ;
		}

	</style>
</head>
<body ng-controller="AppController">

	<h1>
		Scope $watch() vs. $watchCollection() In AngularJS
	</h1>

	<p>
		<a ng-click="changeDeepValue()">Change Deep Value</a>
		&mdash;
		<a ng-click="changeShallowValue()">Change Shallow Value</a>
		&mdash;
		<a ng-click="rebuild()">Rebuild</a>
		&mdash;
		<a ng-click="clear()">Clear</a>
	</p>


	<h2>
		$watchCollection( collection ) Log
	</h2>

	<ul>
		<li ng-repeat="item in watchCollectionLog">
			{{ item }}
		</li>
	</ul>


	<h2>
		$watch( collection ) Log
	</h2>

	<ul>
		<li ng-repeat="item in watchLog">
			{{ item }}
		</li>
	</ul>


	<h2>
		$watch( collection, [ Equality = true ] ) Log
	</h2>

	<ul>
		<li ng-repeat="item in watchEqualityLog">
			{{ item }}
		</li>
	</ul>


	<!-- Load scripts. -->
	<script type="text/javascript" src="../../vendor/jquery/jquery-2.0.3.min.js"></script>
	<script type="text/javascript" src="../../vendor/angularjs/angular-1.2.min.js"></script>
	<script type="text/javascript">


		// Create an application module for our demo.
		var app = angular.module( "Demo", [] );


		// -------------------------------------------------- //
		// -------------------------------------------------- //


		// I control the root of the application.
		app.controller(
			"AppController",
			function( $scope ) {

				// These are the log item to render upon change.
				$scope.watchCollectionLog = [];
				$scope.watchLog = [];
				$scope.watchEqualityLog = [];

				// I am the collection being watched.
				$scope.collection = [
					{
						id: 1,
						value: 0
					}
				];


				// Use the relatively new watchCollection().
				$scope.$watchCollection(
					"collection",
					function( newValue, oldValue ) {

						addLogItem( $scope.watchCollectionLog );

					}
				);

				// Use the old watch() with default object equality,
				// which defaults to use object REFERENCE checks.
				$scope.$watch(
					"collection",
					function( newValue, oldValue ) {

						addLogItem( $scope.watchLog );

					}
				);

				// Use the old watch() method, but turn on deep object
				// equality, which will compare the deep object tree
				// for changes.
				$scope.$watch(
					"collection",
					function( newValue, oldValue ) {

						addLogItem( $scope.watchEqualityLog );

					},
					true // Object equality (not just reference).
				);


				// ---
				// PUBLIC METHODS.
				// ---


				// Change a deep value in an existing item on in the
				// current collection.
				$scope.changeDeepValue = function() {

					// Add new item to collection.
					$scope.collection[ 0 ].value = now();

				};


				// Add a new item to the collection, causing a change
				// in the shallow topology of the collection.
				$scope.changeShallowValue = function() {

					// Add new item to collection.
					$scope.collection.push({
						id: ( $scope.collection.length + 1 ),
						value: now()
					});

				};


				// I clear the log items.
				$scope.clear = function() {

					$scope.watchCollectionLog = [];
					$scope.watchLog = [];
					$scope.watchEqualityLog = [];

				};


				// I rebuild the underlying collection, completely
				// changing the reference.
				$scope.rebuild = function() {

					$scope.collection = [{
						id: 1,
						value: 0
					}];

				};


				// ---
				// PRIVATE METHODS.
				// ---


				// I add a log item to the beginning of the given log.
				function addLogItem( log ) {

					var logItem = (
						"Executed: " + now() +
						" ( length: " + $scope.collection.length + " )"
					);

					log.splice( 0, 0, logItem );

				}


				// I return the current UTC milliseconds.
				function now() {

					return( ( new Date() ).getTime() );

				}

			}
		);


	</script>

</body>
</html>

To frame this conversation, it's important to understand how often $digests run in AngularJS. They run a lot. Probably far more than you think or expect. As such, it's important to make your watchers as light weight as possible. And, understanding how the various watchers work is the first step in choosing the right one for your particular context.

Want to use code from this post? Check out the license.

https://bennadel.com/2566


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4