Listings 8.3 and 8.4 illustrate the use of $emit()
, $broadcast()
, and $on()
to send and handle events up and down the scope hierarchy. In Listing 8.3, lines 2–15 implement a parent scope controller called Characters
, and lines 16–32 define a child scope controller named Character
.
Also in Listing 8.3, the changeName()
function changes the currentName
value and then broadcasts a CharacterChanged
event. The CharacterChanged
event is handled in lines 26–28, using the $on()
method, and sets the currentInfo
value in the scope, which will update the page elements.
Notice that line 6 of Listing 8.3 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 8.4. 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 8.3 implement a handler for the CharacterDeleted
event that removes the character name
from the names
property. The child controller in line 27 broadcasts this event via $broadcast()
.
The AngularJS template code in Listing 8.4 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 8.2 shows the resulting web page. 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',
18 race: 'Hobbit'},
19 'Aragorn': { weapon: 'Sword',
20 race: 'Man'},
21 'Legolas': { weapon: 'Bow',
22 race: 'Elf'},
23 'Gimli': { weapon: 'Axe',
24 race: 'Dwarf'}};
25 $scope.currentInfo = $scope.info['Frodo'];
26 $scope.$on('CharacterChanged', function(event, newCharacter){
27 $scope.currentInfo = $scope.info[newCharacter];
28 });
29 $scope.deleteChar = function() {
30 delete $scope.info[$scope.currentName];
31 $scope.$emit('CharacterDeleted', $scope.currentName);
32 };
33 });
01 <!doctype html>
02 <html ng-app="myApp">
03 <head>
04 <title>AngularJS Scope Events</title>
05 <style>
06 span{
07 padding: 3px; border: 3px ridge;
08 cursor: pointer; width: 100px; display: inline-block;
09 font: bold 18px/22px Georgia; text-align: center;
10 color: white; background-color: blue }
11 label{
12 padding: 2px; margin: 5px 10px; font: 15px bold;
13 display: inline-block; width: 50px; text-align: right; }
14 .lList {
15 vertical-align: top;
16 display: inline-block; width: 130px; }
17 .cInfo {
18 display: inline-block; width: 175px;
19 border: 3px blue ridge; padding: 3px; }
20 </style>
21 </head>
22 <body>
23 <h2>Custom Events in Nested Controllers</h2>
24 <div ng-controller="Characters">
25 <div class="lList">
26 <span ng-repeat="name in names"
27 ng-click="changeName()">{{name}}
28 </span>
29 </div>
30 <div class="cInfo">
31 <div ng-controller="Character">
32 <label>Name: </label>{{currentName}}<br>
33 <label>Race: </label>{{currentInfo.race}}<br>
34 <label>Weapon: </label>{{currentInfo.weapon}}<br>
35 <span ng-click="deleteChar()">Delete</span>
36 </div>
37 </div>
38 </div>
39 <script src="http://code.angularjs.org/1.3.0/angular.min.js"></script>
40 <script src="js/scope_events.js"></script>
41 </body>
42 </html>