Skip to main content
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Doug Hughes and Ezra Parker and Dan Wilson and John Mason and Jason Dean and Luis Majano and Mark Mandel and Brian Kotek and Wil Genovese and Rob Brooks-Bilson and Andy Matthews and Simeon Bateman and Ray Camden and Chris Rockett and Joe Bernard and Dan Skaggs and Byron Raines and Barney Boisvert and Simon Free and Steve 'Cutter' Blades and Seth Bienek and Katie Bienek and Jeff Coughlin
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Doug Hughes Ezra Parker Dan Wilson John Mason Jason Dean Luis Majano Mark Mandel Brian Kotek Wil Genovese Rob Brooks-Bilson Andy Matthews Simeon Bateman Ray Camden Chris Rockett Joe Bernard Dan Skaggs Byron Raines Barney Boisvert Simon Free Steve 'Cutter' Blades Seth Bienek Katie Bienek Jeff Coughlin

Unbinding Scope.$on() Event Handlers In AngularJS

By
Published in Comments (1)

Most of the time, we don't have to think about unbinding Scope.$on() event handlers in AngularJS because they are implicitly unbound when the current scope is destroyed. But, if you think about the scope tree as a built-in pub/sub (Publish and Subscribe) mechanism, you can imagine scenarios in which it would be useful to explicitly unbind an event handler. To do so, we use the same approach that we needed in order to unbind Scope.$watch() event handlers - we store a reference to the deregistration function.

Run this demo in my JavaScript Demos project on GitHub.

When you call the Scope.$on() method, in AngularJS, the return value is a deregistration function for the bound event handler. When you invoke that deregistration function, it's basically like calling .off() on that event handler - it unbinds it (even though there is no .off() method).

To see this in action, I have two instances of a Controller that are listening for a "ping" event that is being continuously $broadcast() from the parent $scope. When you click on either of the relevant Views, the Controller will toggle the event binding and stop updating the view-model:

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

	<title>
		Unbinding Scope.$on() Event Handlers In AngularJS
	</title>

	<link rel="stylesheet" type="text/css" href="./demo.css"></link>
</head>
<body ng-controller="AppController">

	<h1>
		Unbinding Scope.$on() Event Handlers In AngularJS
	</h1>

	<div
		ng-controller="EventController"
		ng-click="toggleListener()"
		class="event-target left"
		ng-class="{ active: isWatchingEvent }">

		{{ eventCount }}

	</div>

	<div
		ng-controller="EventController"
		ng-click="toggleListener()"
		class="event-target right"
		ng-class="{ active: isWatchingEvent }">

		{{ eventCount }}

	</div>


	<!-- Load scripts. -->
	<script type="text/javascript" src="../../vendor/jquery/jquery-2.1.0.min.js"></script>
	<script type="text/javascript" src="../../vendor/angularjs/angular-1.2.26.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, $interval ) {

				// Continuously broadcast an event down the scope tree.
				$interval(
					function handleInterval() {

						$scope.$broadcast( "ping" );

					},
					200
				);

			}
		);


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


		// I control the event targets.
		app.controller(
			"EventController",
			function( $scope ) {

				// I keep track of the number of times we've handled a given event.
				$scope.eventCount = 0;

				// I keep track of whether or not this controller is currently listening
				// for the broadcast event.
				$scope.isWatchingEvent = false;

				// I am the deregistration method for the event handler. This is the
				// closest thing we have to a scope.$off() method.
				var unbindHandler = null;

				// When the controller loads, start listening for broadcast events.
				startWatchingEvent();


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


				// I turn on / off event listening depending on the current state.
				$scope.toggleListener = function() {

					unbindHandler
						? stopWatchingEvent()
						: startWatchingEvent()
					;

				};


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


				// I respond to the "ping" event on the scope.
				function handlePingEvent( event ) {

					$scope.eventCount++;

				}


				// I start watching for the "ping" event on the scope.
				function startWatchingEvent() {

					// When we bind the $on() event, the return value is the
					// deregistration method for the event handler. This is the way we
					// can handle unbind event handlers without destroying the scope.
					// --
					// NOTE: When a scope is $destroy()'d, it will automatically unbind
					// all of your event handlers.
					unbindHandler = $scope.$on( "ping", handlePingEvent );

					$scope.isWatchingEvent = true;

				}


				// I stop watching for the "ping" event on the scope.
				function stopWatchingEvent() {

					// Invoke the deregistration method in order to unbind the event
					// handler. Set to null so we know how to handle the "toggle" method.
					unbindHandler();
					unbindHandler = null;

					$scope.isWatchingEvent = false;

				}

			}
		);

	</script>

</body>
</html>

As you can see, when the event is bound, the return value - the deregistration function - is stored. And, when we need to unbind the event, all we do is invoke the deregistration function.

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

Reader Comments

1 Comments

Hi,

I've taken this a bit further to create a "run once" event handler:

var deregisterRunOnceHandler = $scope.$on('eventName', function() {
[...]
deregisterRunOnceHandler();
deregisterRunOnceHandler = null;
});

Can be useful for others as well.

Regards,

Dirk

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel
Managed hosting services provided by:
xByte Cloud Logo