Using Controllers and Scopes with AngularJS: Understanding the Basics

You can get a lot done in AngularJS by sticking to some basic controller and scope techniques, and it is only as the complexity of your application grows that you will need more advanced features. In this section, I describe the fundamentals. I show you how to create and apply a simple controller, how to define data and logic in a scope, and how to modify the scope. Later in the chapter, I’ll move on to more advanced techniques and features.

1. Creating and Applying Controllers

Controllers are created through the controller method provided by the AngularJS Module object. The arguments to the controller method are the name for the new controller and a function that will create the controller. The function is properly known as the constructor, but I will refer to it as the factory junction because many of the method calls required to create AngularJS components are expressed as one function (the factory) that is used to create another function (the worker junctions). The factory/worker function approach is a little odd at first, but you’ll soon get used to it.

The factory function can use the dependency injection feature to declare dependencies on AngularJS services. Almost every controller will request the $scope service, which is used to provide the view with its scope, defining the data and logic that can be used in the view. You can see how I have created a simple controller in Listing 13-2.

Listing 13-2. Creating a Simple 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) {

});

</script>

</head>

<body>

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

Content will go here.

</div>

</body>

</html>

Tip Strictly speaking, $scope isn’t a service but is an object provided by a service called $rootScope, which I introduce later in this chapter. For all practical purposes, $scope is used like a service, so I am going to refer to it as though it were one for simplicity.

Not only do you have to create the controller, but you also have to demark the views that the controller will support, which is done through the application of the ng-controller directive. The value of the directive must match the name used to create the controller, which is simpleCtrl in this example. The convention when using AngularJS is to use the suffix Ctrl for the name of the controller, but this is not a requirement. At the moment, the controller I have defined doesn’t do anything; it doesn’t provide the view with any data or logic. I’ll populate the controller in the sections that follow.

2. Setting Up the Scope

Controllers provide capabilities to their views through their scope, which is what the controller in Listing 13-2 asked AngularJS to provide when it declared its dependency on the $scope service. Scopes not only define the relationship between controllers and views but also provide the mechanism for many of the most important AngularJS features, such as data binding.

There are two ways to use a scope within a controller. You can define data, and you can define behaviors, which are JavaScript functions that can be called from binding expressions or directives in the view.

Creating the initial data and setting up behaviors is simple. You just create properties on the $scope object that is passed to the controller factory function and assign them data values or functions. Listing 13-3 provides a demonstration.

Listing 13-3. Adding Data and Logic to a Scope in the controller.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.city = “London”;

$scope.getCountry = function (city) {

switch (city) {

case “London”:

return “UK”;

case “New York”:

return “USA”;

}

}

});

</script>

</head>

<body>

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

<p>The city is: {{city}}</p>

<p>The country is: {{getCountry(city) || “Unknown”}}</p>

</div>

</body>

</html>

I have set up the controller’s scope by defining a city property, to which I have assigned a string value, and by defining a getCountry behavior, which is a simple function that takes a city and returns the country in which it can be found. I use the data value and the behavior through data bindings. I can access any data variable directly by its name and call any behavior just as I would a regular JavaScript function. You can see the effect of these changes in Figure 13-2.

3. Modifying the Scope

The most important aspect of scopes is that changes ripple through, automatically updating all of the dependent data values even when they are produced through a behavior. Listing 13-4 shows how a modification made using the ng-model directive causes the data bindings to be updated.

Listing 13-4. Making an Update to the Scope 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.cities = [“London”, “New York”, “Paris”];

$scope.city = “London”;

$scope.getCountry = function (city) {

switch (city) {

case “London”:

return “UK”;

case “New York”:

return “USA”;

}

}

});

</script>

</head>

<body ng-controller=”simpleCtrl”>

<div class=”well”>

<label>Select a City:</label>

<select ng-options=”city for city in cities” ng-model=”city”>

</select>

</div>

<div class=”well”>

<p>The city is: {{city}}</p>

<p>The country is: {{getCountry(city) || “Unknown”}}</p>

</div>

</body>

</html>

I have added an array of city names and used them with the ng-options attribute on the select element to generate a set of option elements. The ng-model directive means that the city model property in the scope is changed when the user picks a value from the select element.

Notice that I have changed where I apply the ng-controller directive so that it contains both the select elements and the data bindings. Each instance of a controller has its own scope, and by ensuring all of the directives and bindings are in the same view (meaning applied to children of the element to which the ng-controller directive is applied to), I ensure that I am working with a single set of data values. This one-scope-per-controller-instance concept is important, and I’ll return to it later in this chapter.

The result of adding the select element is exactly what you would expect from having seen earlier examples: Picking a value with the select element causes the updated value to be used in the data bindings, as shown in Figure 13-3.

Of particular note, it isn’t just the data binding that displays the selected city name that has been changed.

The data binding that displays the result from invoking the controller behavior has also been updated. This is one of the great AngularJS strengths and—as you will learn—the cause of a lot of potential problems.

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 *