Understanding controllers

As we develop the presentation layer using HTML and JavaScript, the presentation layer becomes more complex, especially when we wire up the JavaScript code with HTML elements on the page. For example, to fire an event in order to call the JavaScript function, the business logic is not directly involved with the page and implements data access layer code using an Ajax call to the data access layer code. AngularJS controllers are the fundamental entry points to the presentation layer. The controllers contain all the functions that we need to call a page. We can call an AngularJS controller in the DOM using the ng-controller directive, which is much simpler than calling the traditional JavaScript.

In order to create an AngularJS controller, we need to first create a module. In the AngularJS framework, a module is like a namespace in C# or Java. When using AngularJS, controllers, services, filters, and directives are all created within the module, like a namespace is in other languages. They are also created in the same way as angular.module('myApp', []). The module takes two parameters, the name of the module and an array. We can pass a string, instead of an array, which refers to other module names. The difference between the concept of namespace in other languages and a module in the AngularJS framework is that the module has all functions available for initialization and to create controllers, services, and so on.

A controller is a JavaScript contractor function in AngularJS, which is used to enhance the AngularJS scope object. When a controller is implemented in the DOM using the ng-controller directive, a new controller object is instantiated with the indicated controller's contractor function. A new child scope is offered as an injectable parameter to the controller's function as $scope, as seen in the following code:

var app = angular.module('myApp', []);

app.controller("ctrlName", function ($scope) {

});

In the preceding code, an AngularJS module is created using the app variable, which uses this variable to create a controller. In the controller function, the first parameter is the controller name. In this example, we have used ctrlName. The second parameter is the function that injects the $scope, which will create a new child scope for this controller.

The AngularJS framework uses this controller to create the initial state of the $scope object and to add behavior to the $scope object, as we discussed in the Adding behavior to the AngularJS scope section earlier in this chapter. For best practice, do not use an AngularJS controller for the following:

  • Presentation logic, as it will negatively affect the testability if we add the presentation logic in the controller. A controller should contain only the application logic.
  • To format input instead of formatting in the controller, use AngularJS form controls instead.
  • As an AngularJS filter, which we will discuss in detail in the Understanding filter section.
  • To share code or states within controllers used in AngularJS services.

The following illustration shows how controller components work in AngularJS. This example has two buttons and a message. A model contains a string named car and the controller has two functions that set the values of car. The message in HTML is bound to the car property using the <span> element. The car property is set to Ford by default. By clicking on the button, the model is set to Toyota or to Mercedes, depending upon which button is clicked. The message in <span> is automatically updated by AngularJS data binding:

var app = angular.module('myApp', []); 

app.controller('ctrlExample ', function($scope) {

$scope.car = 'This is Ford'; 

$scope.Toyota = function () { 
$scope.car = 'This is Toyota'; 
}; 

$scope.Mercedes = function () { 
$scope.car = 'This is Mercedes'; 
};

});

We can render the HTML elements as follows:

<div ng-app="myApp" ng-controller=" ctrlExample ">

<button ng-click=" Toyota()">Toyota</button>

<button ng-click=" Mercedes()">Mercedes</button>

<br /><br />

<span>{{car}}</span>

</div>

In the preceding code, we used the ng-controller directive inside the <div> element to initiate the ctrlExample controller. If you notice, we also used the ng-click directive in the <button> element to initiate the functions inside the controller. We used the <span> element to bind the car property of the controller. Initially, we assigned the value of the car property to This is Ford. This value will get changed as the user clicks on the button.

Note

AngularJS controllers only contain the business logic, which is needed for the view. It is best practice to condense work that does not belong to a controller into services and then to implement these services in the controller.

Controller inheritance

It is common practice to call different controllers at different levels of a page. The AngularJS ng-controller directive creates a new child scope for each controller's scope. We can use the hierarchy of scopes that inherit from each other. Each controller has access to its properties and functions along with the controller's properties and functions higher up the hierarchy. The following code shows controller inheritance:

var app = angular.module('myApp', []);

app.controller('ctrlParent', function ($scope) {
    $scope.year = '2014';
    $scope.car = 'Toyota';
});

app.controller('ctrlChild',function ($scope) {
    $scope.car = 'Ford';
});

app.controller('ctrlGrandChild', function ($scope) {
    $scope.year = '2015';
    $scope.car = 'Mercedes';
});

We can render the HTML elements as follows:

<div ng-app="myApp" ng-controller="ctrlParent">

<p>{{year}}, {{car}}</p>

<div ng-controller="ctrlChild">

<p>{{year}}, {{car}}</p>

</div>

<div ng-controller="ctrlGrandChild">

<p>{{year}}, {{car}}</p>

</div>

</div>

In the preceding code snippet, we created three controllers: ctrlParent, ctrlChild, and ctrlGrandChild. The ctrlParent controller has two properties $scope.year ='2014' and $scope.car='Toyota'. The ctrlChild controller has one property, $scope.car='Ford', and the ctrlGrandChild controller has two properties, $scope.year = '2015' and $scope.car='Mercedes'. In order to use inheritance, it is important to know how the ng-controller directives are nested in HTML. In this example, the ctrlChild and ctrlGrandChild controllers are nested inside the ctrlParent controller. In this way, both properties of the parent controller can be exposed in the child and grandchild controllers.

The output of the preceding code is shown in the following figure:

Controller inheritance

In the preceding figure, the 2014, Toyota value is displayed by binding the ctrlParent controller properties: $scope.year and $scope.car. The second string 2014, Ford is displayed by binding the ctrlChild controller property, $scope.car, and the ctrlParent controller property, $scope.year, because the ctrlChild controller is nested inside the ctrlParent controller and can expose the parent $scope.year property. Although the ctrlGrandChild controller is nested inside the ctrlParent controller, it displays the value of its own properties because each controller creates its own child scope.

The AngularJS dot representation

Inheritance is normally straightforward, as we discussed in the previous section, until we apply two-way data binding from an HTML element or the ng-model directive to a primitive, such as a number, string, or Boolean. This can be done especially when the primitive is defined on the parent controller scope from the inside of the child controller's scope. It will not work the way we expect. The child controller gets its own property because whenever we call the child controller in HTML using ng-controller, it creates a new scope and it hides the parent controller's property that has a similar name. This problem with the primitive can be resolved using . in ng-model. The following code example explains the AngularJS dot representation:

var app = angular.module('myApp', []);

app.controller('ctrlFirst', function ($scope) {

});

app.controller('ctrlSecond', function ($scope) {

});

We can render the HTML elements as follows:

<div ng-app="myApp">

<h3>AngularJS dot presentation</h3><br />
<input type="text" ng-model="data.message">
<span>{{ data.message }}</span>

<div ng-controller="ctrlFirst">
<input type="text" class="form-control" ng-model="data.message">
<span>{{ data.message }}</span>
</div>

<div ng-controller="ctrlSecond">
<input type="text" class="form-control" ng-model="data.message">
<span>{{ data.message }}</span>
</div>

<!--WITHOUT DOT PRESENTATION-->
<h3>AngularJS dot presentation</h3><br />
<input type="text" class="form-control" ng-model="message">
<span>{{ message }}</span>
<div ng-controller="ctrlFirst">
<input type="text" class="form-control" ng-model="message">
<span>{{ message }}</span>
</div>

<div ng-controller="ctrlSecond">
<input type="text" class="form-control" ng-model="message">
<span>{{ message }}</span>
</div>
</div>

In the preceding code example, we used the dot representation to bind all three instances of data.message with each other, which will be the scope of the entire application. It is noticeable that we have assigned the data.message value to ng-model. Second, we assigned the message value to ng-model. The advantage of this is that it will break the scope inheritance. Then, each ng-model will create a new child scope of the message instance so that the model becomes an unbound instance.

The following screenshot explains the concept of AngularJS dot representation:

The AngularJS dot representation

In the preceding figure, if you type in any textbox of the AngularJS dot presentation, it will update the value of all the three corresponding <span> elements because the ng-model value will be the scope of the entire application. On the other hand, if you change the value of any textbox in AngularJS without the dot presentation, it will update all three <span> elements and after that, it will only change the value of the corresponding <span> element because they are not bound to the instance of the scope.

Controller without scope

AngularJS introduced support for controller as syntax in its 1.1.5 Version. This new syntax changes the impact of an AngularJS controller. Using this new syntax, the controller scope can be defined within the ng-controller directive, for example, ctrlCarLists as cars where cars is the named scope instead of declaring $scope in the controller itself. The following code demonstrates this new syntax concept:

var app = angular.module('myApp', []);

app.controller("ctrlCarLists", function () {

this.showCars = function () {

        alert("Ford, Toyata, Mercedes");

    };
});

We can render the HTML elements as follows:

<div ng-app="myApp" ng-controller="ctrlCarLists as cars">

<button ng-click="cars.showCars()">
        Cars
</button>

</div>

As you can see, ctrlCarList has no scope. Instead of defining a function of $scope, we will define the showCars function using this. Inside the DOM, we will create a cars scope, such as ctrlCarLists as cars in ng-controller. Inside the <button> element, we will, using the car scope, call the showCars function, which will fire the ng-click event and show a jQuery dialog box with the list of cars, as shown in the following screenshot:

Controller without scope
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset