A great feature of scopes is the ability to emit and broadcast events within the scope hierarchy. Events allow you to send notification to different levels in the scope that an event has occurred. Events can be anything you choose, such as a value changed or threshold reached. This is extremely useful in many situations, such as letting child scopes know that a value has changed in a parent scope or vice versa.
To emit an event from a scope, you use the $emit()
method. This method sends an event upward through the parent scope hierarchy. Any ancestor scopes that have registered for the event are notified. The $emit()
method uses the following syntax, where name
is the event name and args
is zero or more arguments to pass to the event handler functions:
scope.$emit(name, [args, . . .])
You can also broadcast an event downward through the child scope hierarchy by using the $broadcast()
method. Any descendent scopes that have registered for the event are notified. The $broadcast()
method uses the following syntax, where name
is the event name and args
is zero or more arguments to pass to the event handler functions:
scope.$broadcast(name, [args, . . .])
To handle an event that is emitted or broadcasted, you use the $on()
method. The $on()
method uses the following syntax, where name
is the name of the event to listen for:
scope.$on(name, listener)
The listener
parameter is a function that accepts the event as the first parameter and any arguments passed by the $emit()
or $broadcast()
method as subsequent parameters. The event
object has the following properties:
targetScope: The scope from which $emit()
or $broadcast()
was called.
currentScope: The scope that is currently handling the event.
name: The name of the event.
stopPropagation(): A function that stops the event from being propagated up or down the scope hierarchy.
preventDefault(): A function that prevents default behavior in a browser event but only executes your own custom code.
defaultPrevented: A Boolean that is true
if event.preventDefault()
has been called.
Listings 22.6 and 22.7 illustrate the use of $emit()
, $broadcast()
, and $on()
to send and handle events up and down the scope hierarchy. In Listing 22.6, lines 2–15 implement a parent scope called Characters
, and lines 16–28 define a child scope named Character
.
Also in Listing 22.6, changeName()
function changes the currentName
value and then broadcasts a CharacterChanged
event. The CharacterChanged
event is handled in lines 22–24, using the $on()
method, and sets the currentInfo
value in the scope, which will update the page elements.
Notice that line 6 of Listing 22.6 uses the this
keyword to access the name
property. The name
property actually comes from a dynamic child scope that was created because the following directives were used to generate multiple elements in Listing 22.7. The child scope can be accessed from the changeName()
method in the scope by using the this
keyword:
ng-repeat="name in names"
ng-click="changeName()"
Lines 9–14 of Listing 22.6 implement a handler for the CharacterDeleted
event that removes the character name
from the names
property. The child controller in line 27 sends this event via $emit()
.
The AngularJS template code in Listing 22.7 implements the nested ng-controller
statements, which generates the scope hierarchy and displays scope values for the characters. This code also includes some very basic CSS styling to make spans look like buttons and to position elements on the page. Figure 22.3 shows the resulting webpage. As you click a character name, information about that character is displayed, and when you click the Delete button, the character is deleted from the buttons and the Info section.
01 angular.module('myApp', []).
02 controller('Characters', function($scope) {
03 $scope.names = ['Frodo', 'Aragorn', 'Legolas', 'Gimli'];
04 $scope.currentName = $scope.names[0];
05 $scope.changeName = function() {
06 $scope.currentName = this.name;
07 $scope.$broadcast('CharacterChanged', this.name);
08 };
09 $scope.$on('CharacterDeleted', function(event, removeName){
10 var i = $scope.names.indexOf(removeName);
11 $scope.names.splice(i, 1);
12 $scope.currentName = $scope.names[0];
13 $scope.$broadcast('CharacterChanged', $scope.currentName);
14 });
15 }).
16 controller('Character', function($scope) {
17 $scope.info = {'Frodo':{weapon:'Sting', race:'Hobbit'},
18 'Aragorn':{weapon:'Sword', race:'Man'},
19 'Legolas':{weapon:'Bow', race:'Elf'},
20 'Gimli':{weapon:'Axe', race:'Dwarf'}};
21 $scope.currentInfo = $scope.info['Frodo'];
22 $scope.$on('CharacterChanged', function(event, newCharacter){
23 $scope.currentInfo = $scope.info[newCharacter];
24 });
25 $scope.deleteChar = function() {
26 delete $scope.info[$scope.currentName];
27 $scope.$emit('CharacterDeleted', $scope.currentName);
28 };
29 });
01 <!doctype html>
02 <html ng-app="myApp">
03 <head>
04 <title>AngularJS Scope Events</title>
05 <style>
06 div{padding:5px; font: 18px bold;}
07 span{padding:3px; margin:12px; border:5px ridge;
08 cursor:pointer;}
09 label{padding:2px; margin:5px; font: 15px bold;}
10 p{padding-left:22px; margin:5px; }
11 </style>
12 </head>
13 <body>
14 <div ng-controller="Characters">
15 <span ng-repeat="name in names"
16 ng-click="changeName()">{{name}}</span>
17 <div ng-controller="Character"><hr>
18 <label>Name: </label><p>{{currentName}}</p>
19 <label>Race: </label><p>{{currentInfo.race}}</p>
20 <label>Weapon: </label><p>{{currentInfo.weapon}}</p>
21 <span ng-click="deleteChar()">Delete</span>
22 </div>
23 </div>
24 <script src="http://code.angularjs.org/1.2.9/angular.min.js"></script>
25 <script src="/js/scope_events.js"></script>
26 </body>
27 </html>