Using Controllers and Scopes with AngularJS: Organizing Controllers

In Listing 13-4, I have a single controller that supports all of the content in the body element. This is perfectly reasonable for small applications, but it gets unwieldy as the complexity of the project grows, and it can make using some features difficult, such as partial views (which I touched on with the ng-include directive in Chapter 10 and return to in more depth in Chapter 22). There are several different ways in which you can organize the controllers in your application, and I describe them in the sections that follow.

Tip It can be difficult to decide which approach to take in an application, so I provide some general guidance about when each technique should be applied. But, as with so much in AngularJS development, I suggest you start with my recommendations but also try each approach and see what suits you and your project best.

1. Using a Monolithic Controller

The first approach is the one that I used in Listing 13-4: one controller that is applied to all HTML elements in the application by using the ng-controller directive on the body element (or at least on some element that encompasses all of your data bindings and directives).

There are some benefits to this approach: It is simple, you don’t have to worry about communication between scopes (a topic I introduce later in this chapter), and your behaviors are available for use throughout your HTML. When you use a monolithic controller, you effectively create one view for the entire application, as illustrated by Figure 13-4.

There are drawbacks to this approach: It is fine for simple applications—such as the examples I have been using to demonstrate AngularJS features—but you can easily end up with a mass of code as you add the behaviors required to deliver your application functionality. This makes maintaining the project more difficult and can complicate the testing process. It is also counter to the broad AngularJS philosophy of lots of small, focused building blocks, but that’s just a matter of style rather than a technical requirement. In Listing 13-5, you can see an example of a monolithic controller and a single view used to obtain simple shipping and billing details. I have included only a single data field for each address in this example because my focus is on the relationship between the controller and the view. I rework this example in the sections that follow as I describe the different kinds of relationship you can create.

Listing 13-5. Creating a Monolithic Controller in the controllers.html File

<!DOCTYPE html>

<html ng-app=”exampleApp”>

<head>

<title>Controllers</title>

<script src=”angular.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

<script>

angular.module(“exampleApp”, [])

.controller(“simpleCtrl”, function ($scope) {

$scope.addresses = {};

$scope.setAddress = function (type, zip) {

console.log(“Type: ” + type + ” ” + zip);

$scope.addresses[type] = zip;

}

$scope.copyAddress = function () {

$scope.shippingZip = $scope.billingZip;

}

});

</script>

</head>

<body ng-controller=”simpleCtrl”>

<div class=”well”>

<h4>Billing Zip Code</h4>

<div class=”form-group”>

<input class=”form-control” ng-model=”billingZip”>

</div>

<button class=”btn btn-primary” ng-click=”setAddress(‘billingZip’, billingZip)”>

Save Billing

</button>

</div>

<div class=”well”>

<h4>Shipping Zip Code</h4>

<div class=”form-group”>

<input class=”form-control” ng-model=”shippingZip”>

</div>

<button class=”btn btn-primary” ng-click=”copyAddress()”>

Use Billing

</button>

<button class=”btn btn-primary”

ng-click=”setAddress(‘shippingZip’, shippingZip)”>

Save Shipping

</button>

</div>

</body>

</html>

The controller in this example defines an addresses object that I will use to gather my ZIP codes and behaviors called setAddress and copyAddress. The setAddress behavior prints out one of the ZIP code values, and the copyAddress copies one implicitly defined ZIP code behavior to another. The data and the behavior are wired up to the HTML elements using standard AngularJS directives and model bindings. You can see how the example is displayed in the browser in Figure 13-5.

You can enter ZIP codes directly into the input elements, and you can copy the billing ZIP code to the shipping input element by clicking the Use Billing button. Copying data values like this is simple because there is only one scope to worry about and every data value is immediately available.

This is the controller organization you should start with if you are new to AngularJS, if you are creating a simple application, or if you don’t have a clear design in mind when you start development. You can get up and running quickly, and you can adopt one of the other approaches I describe as you progress.

2. Reusing a Controller

You can reuse a controller to create several views in the same application. AngularJS will call the factory function each time you apply the controller, with the result that each instance will have its own scope. This may seem like an odd thing to do, but this approach allows for simpler controllers because it only has to manage a subset of the data values that a monolithic controller has to deal with. This works because the way that the MVC pattern separates functionality means that different views can present the same data and functionality in different ways. In Listing 13-6, you can see how I have reworked the example to simplify the controller and consume it through two different views.

Listing 13-6. Reusing a Controller in the controllers.html File

<!DOCTYPE html>

<html ng-app=”exampleApp”>

<head>

<title>Controllers</title>

<script src=”angular.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

<script>

angular.module(“exampleApp”, [])

.controller(“simpleCtrl”, function ($scope) {

$scope.setAddress = function (type, zip) {

console.log(“Type: ” + type + ” ” + zip);

}

$scope.copyAddress = function () {

$scope.shippingZip = $scope.billingZip;

}

});

</script>

</head>

<body>

<div class=”well” ng-controller=”simpleCtrl”>

<h4>Billing Zip Code</h4>

<div class=”form-group”>

<input class=”form-control” ng-model=”zip”>

</div>

<button class=”btn btn-primary” ng-click=”setAddress(‘billingZip’, zip)”>

Save Billing

</button>

</div>

<div class=”well” ng-controller=”simpleCtrl”>

<h4>Shipping Zip Code</h4>

<div class=”form-group”>

<input class=”form-control” ng-model=”zip”>

</div>

<button class=”btn btn-primary” ng-click=”copyAddress()”>

Use Billing

</button>

<button class=”btn btn-primary” ng-click=”setAddress(‘shippingZip’, zip)”>

Save Shipping

</button>

</div>

</body>

</html>

In this example, I have removed the ng-controller directive from the body element and instead applied it to two identical regions of content, each supported by the simpleCtrl controller. This has the effect of creating two controllers and two views. AngularJS calls the controller factory function for each of the views, which has the effect of giving each view its own scope. You can see the effect of this technique in Figure 13-6.

The data and behaviors provided by each controller to its scope are independent from the other scope in the application, and this allows me to simplify the controllers and the views. Each controller has to worry only about gathering a single ZIP code, and this allows me to simplify the code (although only slightly because this is already a simple example—the effect is more pronounced in a real application).

2.1. Communicating Between Scopes

The downside of this approach is that my copyAddress behavior no longer works because each ZIP code is stored in a variable called zip in a different scope. Fortunately, AngularJS provides mechanisms for sharing data between scopes. First, however, I have to confess that Figure 13-6 contains a simplification of the way that scopes work.

Scopes are really organized in a hierarchy, starting with the root scope. Each controller is given a new scope that is a child of the root scope, which means that Figure 13-7 presents a more accurate representation of how multiple controllers work.

The root scope provides the means for sending events between scopes and, by implication, allowing communication between controllers. You can see how I have used the root scope in Listing 13-7.

Listing 13-7. Using the Root Scope to Communicate Between Controllers

<script>

angular.module(“exampleApp”, [])

.controller(“simpleCtrl”, function ($scope, $rootScope) {

$scope.$on(“zipCodeUpdated”, function (event, args) {

$scope[args.type] = args.zipCode;

});

$scope.setAddress = function (type, zip) {

$rootScope.$broadcast(“zipCodeUpdated”, {

type: type, zipCode: zip

});

console.log(“Type: ” + type + ” ” + zip);

}

$scope.copyAddress = function () {

$scope.zip = $scope.billingZip;

}

});

</script>

The root scope is available as a service, so I declare a dependency on it on my controller using the name $rootScope (this is one of the built-in AngularJS services, and I describe the others in Chapter 18). All scopes, including the $rootScope service, define a number of methods that are used to send and receive events, as described in Table 13-3.

The $broadcast and $emit events are directional and send an event up through the scope hierarchy until it reaches the root scope or down through the hierarchy to each of the child scopes. This seems like overkill at the moment, but as you’ll see, different arrangements of controllers can result in more complex scope hierarchies, which I describe later in this chapter.

In the example, I call the $on method on the current scope to set up a handler function for an event called zipCodeUpdated. The handler function for scope events receives an Event object and an argument’s object—my argument object will define type and zipCode properties, and I use them to define a property on the local scope, like this:

$scope.$on(“zipCodeUpdated”, function (event, args) {

$scope[args.type] = args.zipCode;

});

Tip I defined the property on the $scope object using the array-style notation. The name of the $scope property is set to the value of the args.type property from the method argument. Placing args.type between the [ and ] characters causes the args.type property to be evaluated, and its value is used as the name of the scope property.

I will use this event to keep both scopes in sync so that each has both ZIP codes that the user has provided.

I achieve the other side of this synchronization by calling the $broadcast method on the $rootScope object, passing in an object with the type and zipCode properties that my event handler function expects:

$rootScope.$broadcast(“zipCodeUpdated”, {

type: type, zipCode: zip

});

To summarize, when the Save Billing button is clicked, the $broadcast method is called on the root scope, which sends a zipCodeUpdated event down through the scope hierarchy. This has the effect of triggering my handler for the event, ensuring that the scope associated with the controller collecting the shipping ZIP code knows what the billing ZIP code is. This allows me to reinstate the Use Billing button, like this:

$scope.copyAddress = function () {

$scope.zip = $scope.billingZip;

}

Setting the value of $scope.zip updates the input element, which has the binding to the property through the ng-model directive.

2.2. Using a Service to Mediate Scope Events

The convention in AngularJS is to use a service to mediate communication between scopes. I don’t cover services in depth until Chapter 18, but I wanted to show you the convention in context so you can decide whether to adopt it.

The convention doesn’t have a huge impact in this example because I am working with only a single controller, but it can reduce duplication if there are multiple controllers, all of which need to send the same kind of event. In Listing 13-8 you can see how I have used the Module.service method to create a service object that my controllers use to send and receive events, without directly interacting with the scope event methods.

Listing 13-8. Using a Service to Mediate Scope Events

<script>

angular.module(“exampleApp”, [])

.service(“ZipCodes”, function($rootScope) {

return {

setZipCode: function(type, zip) {

this[type] = zip;

$rootScope.$broadcast(“zipCodeUpdated”, {

type: type, zipCode: zip

});

}

}

})

.controller(“simpleCtrl”, function ($scope, ZipCodes) {

$scope.$on(“zipCodeUpdated”, function (event, args) {

$scope[args.type] = args.zipCode;

});

$scope.setAddress = function (type, zip) {

ZipCodes.setZipCode(type, zip);

console.log(“Type: ” + type + ” ” + zip);

}

$scope.copyAddress = function () {

$scope.zip = $scope.billingZip;

}

});

</script>

The ZipCodes service declares a dependency on the $rootScope service and uses it within the setZipCode method to call the $broadcast event. The controllers declare a dependency on the ZipCodes service and call the setZipCode method rather than operate directly on $rootScope. There is no change in the functionality—this convention is about reducing duplication by putting code that is likely to be required by different controllers in a single location.

3. Using Controller Inheritance

The ng-controller directive can nested in HTML elements to create an effect known as controller inheritance. This is a feature that aims to reduce code duplication by letting you define common functionality in a parent controller and use it in one or more child controllers. The best way to explain is with an example, such as the one in Listing 13-9.

Listing 13-9. Using Controller Inheritance in the controllers.html File

<!DOCTYPE html>

<html ng-app=”exampleApp”>

<head>

<title>Controllers</title>

<script src=”angular.js”></script>

<script src=”controllers.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

</head>

<body ng-controller=”topLevelCtrl”>

<div class=”well”>

<h4>Top Level Controller</h4>

<div class=”input-group”>

<span class=”input-group-btn”>

<button class=”btn btn-default” type=”button”

ng-click=”reverseText()”>Reverse</button>

<button class=”btn btn-default” type=”button”

ng-click=”changeCase()”>Case</button>

</span>

<input class=”form-control” ng-model=”dataValue”>

</div>

</div>

<div class=”well” ng-controller=”firstChildCtrl”>

<h4>First Child Controller</h4>

<div class=”input-group”>

<span class=”input-group-btn”>

<button class=”btn btn-default” type=”button”

ng-click=”reverseText()”>Reverse</button>

<button class=”btn btn-default” type=”button”

ng-click=”changeCase()”>Case</button>

</span>

<input class=”form-control” ng-model=”dataValue”> </div>

</div>

<div class=”well” ng-controller=”secondChildCtrl”>

<h4>Second Child Controller</h4>

<div class=”input-group”>

<span class=”input-group-btn”>

<button class=”btn btn-default” type=”button”

ng-click=”reverseText()”>Reverse</button>

<button class=”btn btn-default” type=”button”

ng-click=”changeCase()”>Case</button>

<button class=”btn btn-default” type=”button”

ng-click=”shiftFour()”>Shift</button>

</span>

<input class=”form-control” ng-model=”dataValue”>

</div>

</div>

</body>

</html>

There are three controllers at work in this example, each of which has been applied to a region of markup using the ng-controller directive. The controller called topLevelCtrl is applied to the body element, and two child controllers, firstChildCtrl and secondChildCtrl, are nested within it. In addition to the child controllers, the top-level controller contains its own elements, and all three controllers present an input element with some inline buttons that invoke controller behaviors.

To reduce the amount of markup and code I have to duplicate for this example, I have moved the contents of the script element to a separate file called controllers.js. This file contains the code to set up the AngularJS application and define the controllers, as shown in Listing 13-10.

Listing 13-10. The Contents of the controllers.js File

var app = angular.module(“exampleApp”, []);

app.controller(“topLevelCtrl”, function ($scope) {

$scope.dataValue = “Hello, Adam”;

$scope.reverseText = function () {

$scope.dataValue = $scope.dataValue.split(‘”‘).reverse().join(‘”1);

}

$scope.changeCase = function () {

var result = [];

angular.forEach($scope.dataValue.split(“”), function (char, index) {

result.push(index % 2 == 1

? char.toString().toUpperCase() : char.toString().toLowerCase());

});

$scope.dataValue = result.join(“”);

};

});

app.controller(“firstChildCtrl”, function ($scope) {

$scope.changeCase = function () {

$scope.dataValue = $scope.dataValue.toUpperCase();

};

});

app.controller(“secondChildCtrl”, function ($scope) {

$scope.changeCase = function () {

$scope.dataValue = $scope.dataValue.toLowerCase();

};

$scope.shiftFour = function () {

var result = [];

angular.forEach($scope.dataValue.split(“”), function (char, index) {

result.push(index < 4 ? char.toUpperCase() : char);

});

$scope.dataValue = result.join(“”);

}

});

You can see how the browser displays the example in Figure 13-8. I have applied header elements and Bootstrap styles to emphasize each controller. All three controllers present a Reverse button that reverses the order of the characters in the input element.

When you nest controllers through the ng-controller directive, the scopes of the child controllers inherit the data and behaviors of the parent controller scope. (I have shown only one level of parent-child relationship in this example, but you can nest controllers as deeply as you want.) Each controller in this example has its own scope, but the scopes for the child controllers contain the data values and behaviors of the parent controller, as shown in Figure 13-9.

You can see how this works you click the Reverse button. The input elements are all wired up to manage the dataValue property, and the Reverse buttons all call the reverseText behavior, both of which are defined by the top-level controller. The child controllers inherit the data value and the behavior, which is why all of the input elements change when you click any of the Reverse buttons, even those implemented by the child controllers.

3.1. Adding to the Inherited Data and Behaviors

The main benefit of using controller inheritance is the ability to mix functionality that is inherited from the parent scope with locally defined additions. You can see an example of this in the secondChildCtrl controller, which defines a behavior called shiftFour that makes the first four characters of the dataValue property uppercase, as follows:

$scope.shiftFour = function () {

var result = [];

angular.forEach($scope.dataValue.split(“”), function (char, index) {

result.push(index < 4 ? char.toUpperCase() : char);

});

$scope.dataValue = result.join(“”);

}

This behavior is available only in the scope of the secondChildCtrl controller—but notice that even here, I am able to use an inherited feature as I perform my changes on the dataValue property defined by the parent scope. You use this feature to build on the functionality of existing controllers, without having to duplicate behaviors and data.

3.2. Overriding Inherited Data and Behaviors

Child controllers can override the data and behaviors of their parents, which means that data values and behaviors can be replaced with local versions that have the same name. In Listing 13-9 you can see that each of the child controllers defines a behavior called changeCase on its scope. Each implementation of this behavior is different and changes the dataValue property in a different way, but they are all invoked in the same way through the ng-click directive:

<button class=”btn btn-default” type=”button” ng-click=”changeCase()“>Case</button>

When looking for a behavior, AngularJS starts with the scope of the controller in which the directive has been applied. If such a behavior exists, then it will be executed. If not, AngularJS moves up to the next level in the scope hierarchy and continues looking until a behavior with the specified name is found.

You use this feature to use most of the functionality provided by a parent controller, overriding just the parts you need to customize. This allows you to build controllers that are tailored to different parts of your application without needing to duplicate the code and data in the parent controller.

3.3. Understanding Data Inheritance

I included a common trap in Listing 13-10 that affects just about everyone who uses controller inheritance for the first time. To see the problem, load the controllers.html file into the browser and click each of the Reverse buttons in turn (it doesn’t matter which order you click them in).

This is the behavior that you would expect given my description so far. The Reverse button invokes the reverseText behavior, which operates on the dataValue property. The behavior and data are defined by the parent controller and inherited by the children, which is why the contents of all three input elements change together.

Now change the contents of the input element associated with the second child controller. It doesn’t matter what you enter, just as long as you change the text. Now click all three Reverse buttons in turn again, and you’ll see a different behavior. All three buttons operate on the first two input elements, and the input element that you edited remains unchanged. To dig deeper into the problem, click the Case and Shift buttons for the second child controller; these do change the final input element.

Before I explain why this happens, I am going to show you the solution. Listing 13-11 shows the changes I have made to the controllers.js file.

Listing 13-11. Solving the Inheritance Problem in the controllers.js File

var app = angular.module(“exampleApp”, []);

app.controller(“topLevelCtrl”, function ($scope) {

$scope.data = {

dataValue: “Hello, Adam”

}

$scope.reverseText = function () {

$scope.data.dataValue = $scope.data.dataValue.split(“”).reverse().join(“”);

}

$scope.changeCase = function () {

var result = [];

angular.forEach($scope.data.dataValue.split(“”), function (char, index) {

result.push(index % 2 == 1

? char.toString().toUpperCase() : char.toString().toLowerCase());

});

$scope.data.dataValue = result.join(“”);

};

});

app.controller(“firstChildCtrl”, function ($scope) {

$scope.changeCase = function () {

$scope.data.dataValue = $scope.data.dataValue.toUpperCase();

};

});

app.controller(“secondChildCtrl”, function ($scope) {

$scope.changeCase = function () {

$scope.data.dataValue = $scope.data.dataValue.toLowerCase();

};

$scope.shiftFour = function () {

var result = [];

angular.forEach($scope.data.dataValue.split(“”), function (char, index) {

result.push(index < 4 ? char.toUpperCase() : char);

});

$scope.data.dataValue = result.join(“”);

}

});

Instead of defining dataValue as a property directly on the scope of the parent controller, I have defined it as a property of an object called data. The other changes in this file update the reference to the dataValue property to access it via the data object. In Listing 13-12, you can see how I have reflected this change in the ng-model directives that link the input elements with the dataValue property in the controllers.html file.

Listing 13-12. Solving the Inheritance Problem in the controllers.html File

<!DOCTYPE html>

<html ng-app=”exampleApp”>

<head>

<title>Controllers</title>

<script src=”angular.js”></script>

<script src=”controllers.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

</head>

<body ng-controller=”topLevelCtrl”>

<div class=”well”>

<h4>Top Level Controller</h4>

<div class=”input-group”>

<span class=”input-group-btn”>

<button class=”btn btn-default” type=”button”

ng-click=”reverseText()”>Reverse </button>

<button class=”btn btn-default” type=”button”

ng-click=”changeCase()”>Case</button>

</span>

<input class=”form-control” ng-model=”data.dataValue”>

</div>

</div>

<div class=”well” ng-controller=”firstChildCtrl”>

<h4>First Child Controller</h4>

<div class=”input-group”>

<span class=”input-group-btn”>

<button class=”btn btn-default” type=”button”

ng-click=”reverseText()”>Reverse</button>

<button class=”btn btn-default” type=”button”

ng-click=”changeCase()”>Case</button>

</span>

<input class=”form-control” ng-model=”data.dataValue”>

</div>

</div>

<div class=”well” ng-controller=”secondChildCtrl”>

<h4>Second Child Controller</h4>

<div class=”input-group”>

<span class=”input-group-btn”>

<button class=”btn btn-default” type=”button”

ng-click=”reverseText()”>Reverse</button>

<button class=”btn btn-default” type=”button”

ng-click=”changeCase()”>Case</button>

<button class=”btn btn-default” type=”button”

ng-click=”shiftFour()”>Shift</button>

</span>

<input class=”form-control” ng-model=”data.dataValue”>

</div>

</div>

</body>

</html>

If you load the new version of the controllers.html file into the browser, you will see that all of the buttons affect the contents of the input elements and that editing the input element content doesn’t stop subsequent changes from taking effect.

To understand what’s happening, we need to look at the way that AngularJS deals with inheritances of data values in scopes and how this is affected by the ng-model directive.

When you read the value of a property that is defined directly on the scope, AngularJS checks to see whether there is a local property in the controller’s scope and, if not, starts working its way up the scope hierarchy to see whether it has inherited one. However, when you use the ng-model directive to modify such a property, AngularJS checks to see whether the scope has a property of the right name and, if not, assumes you want to implicitly define it. The effect is to override the property value, much as I did with the behavior in the previous section. The reason that editing the contents of a child input element prevents the Reverse button from working is that there are now two dataValue properties—one defined by the top-level controller and one by the child you edited. The reverseText behavior is defined by the top-level controller, and it operates on the dataValue defined in the top-level scope, leaving the child’s dataValue property unaltered.

This doesn’t happen when you assign an object to the scope and then define your data properties on that object. This is because JavaScript implements what is known as prototype inheritance—a topic so dry and confusing that I am not going to attempt to explain it here, although I describe the basics in Chapter 18. What is important is the knowledge that defining properties directly on the scope like this:

$scope.dataValue = “Hello, Adam”;

means that using the ng-model directive will create local variables, while using an object as an intermediary, like this:

$scope.data = {

dataValue: “Hello, Adam”

}

ensures that ng-model will update the data values defined in the parent scope. This is not a bug. It is a deliberate feature that allows you to decide how your controller and its scope will work, and you can mix and match both techniques in the same scope. If you want a value that is initially shared but will be copied when modified, then define your data properties directly on the scope. To ensure that there is only one value, then define your data properties via an object.

Note The controller behaviors that I used to demonstrate inheritance all operate directly on values defined on their scopes. I did this to make the problems that can arise with inheritance more obvious, but the convention in AngularJS development is to have behaviors receive arguments. This doesn’t change the way that inheritance works—or the confusion that it can cause—because AngularJS has to perform the same sequence of steps to locate values whether they are accessed directly from the behavior or passed in as an argument.

4. Using Multiple Controllers

An application can contain as many controllers as you need. Don’t worry about figuring out the right number of controllers when you start working with AngularJS. You’ll know it is time to split up your monolithic controllers when you struggle to find particular data values or behaviors in the code file.

My approach—which is pretty common and far from unique to me—is to create a new controller for each major view in the application, although this is only a rule of thumb, and I often reuse controllers or rely on controller inheritance. There are no hard-and-fast rules, and you will naturally develop you own set of techniques. In Listing 13-13, you can see how I have reworked the example so that there are two distinct controllers—one for each of the ZIP codes that I want to capture.

Listing 13-13. Creating Multiple Distinct Controllers in the controllers.html File

<!DOCTYPE html>

<html ng-app=”exampleApp”>

<head>

<title>Controllers</title>

<script src=”angular.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

<script>

var app = angular.module(“exampleApp”, []);

app.controller(“firstController”, function ($scope) {

$scope.dataValue = “Hello, Adam”;

$scope.reverseText = function () {

$scope.dataValue = $scope.dataValue.split(“”).reverse().join(“”);

}

});

app.controller(“secondController”, function ($scope) {

$scope.dataValue = “Hello, Jacqui”;

$scope.changeCase = function () {

$scope.dataValue = $scope.dataValue.toUpperCase();

};

});

</script>

</head>

<body>

<div class=”well” ng-controller=”firstController”>

<h4>First Controller</h4>

<div class=”input-group”>

<span class=”input-group-btn”>

<button class=”btn btn-default” type=”button”

ng-click=”reverseText()”>Reverse</button>

</span>

<input class=”form-control” ng-model=”dataValue”>

</div>

</div>

<div class=”well” ng-controller=”secondController”>

<h4>Second Controller</h4>

<div class=”input-group”>

<span class=”input-group-btn”>

<button class=”btn btn-default” type=”button”

ng-click=”changeCase()”>

Case

</button>

</span>

<input class=”form-control” ng-model=”dataValue”>

</div>

</div>

</body>

</html>

There are two controllers defined in this example, and each has been applied to separate HTML elements.

This means that the controllers operate independently and don’t share scopes or inherit data or behaviors, as shown in Figure 13-10.

You will notice that this is the same arrangement I showed you in Figure 13-6 when describing the use of multiple instances of the same controller; the only difference is that I have since revealed the existence of the root scope, which must be used if you want to communicate between the scopes set up by each controller.

Source: Freeman Adam (2014), Pro AngularJS (Expert’s Voice in Web Development), Apress; 1st ed. edition.

Leave a Reply

Your email address will not be published. Required fields are marked *