Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

July 23, 2014

AngularJS: 101* Ways to Create a Controller (* actually 8)

Filed under: AngularJS @ 6:53 pm

OK, there aren’t really 101 ways to create an AngularJS controller. But when you are first learning and looking at examples, it may seem that there are 101 ways!

This post walks through the most common ways to create an Angular controller. The goal here is to put the different techniques in one place for you to compare and contrast.

NOTE: In all of these examples, the JavaScript code for the controller is added directly to the HTML file so it is easy for you to try it out. In a “real” application, you would add the code for the controller to a separate .js file (as covered in a future post). You can use the techniques presented below regardless of where the controller code resides.

The UI used in all of these examples is a movie hunter application that displays a list of movies. In all cases, the result appears like this:

image

The html is:

<!DOCTYPE html>
<html>
<head lang=”en”>
<meta charset=”UTF-8″>
<title>InStep Movie Hunter</title>

<!– Use the Content Delivery Network (CDN) –>
<script
 
src=”http://ajax.googleapis.com/ajax/libs/angularjs/1.2.17/angular.min.js”>
</script>
<link
href=
http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css
rel=”stylesheet” />
</head>

<body ng-app>

    <div class=”row  bg-primary”>
        <div class=”col-md-6″>Title</div>
        <div class=”col-md-2″>Director</div>
        <div class=”col-md-2″>Release Date</div>
        <div class=”col-md-2″>Rating</div>
    </div>

    <div class=”row” ng-controller=”SearchByMovieCtrl>
        <div class=”col-md-6″>{{movie.title}}</div>
        <div class=”col-md-2″>{{movie.director}}</div>
        <div class=”col-md-2″>{{movie.date}}</div>
        <div class=”col-md-2″>{{movie.mpaa}}</div>

    </div>

    <script>
        <!—The controller code goes here –>
    </script>

</body>
</html>

Notice two things:

  1. The ng-app attribute on the body tag in the HTML.
    • This attribute is required to identify the page as an Angular application.
    • This attribute currently has no value assigned to it.
    • This attribute is normally defined on the html or body tag.
  2. The ng-controller attribute on the div tag in the HTML.
    • This attribute defines the name of the controller function associated with a piece of the html.
    • This attribute is set to “SearchByMovieCtrl”, which is the name of the controller function.
    • The html tags that are child tags of this div tag (highlighted in green) have access to any of the controller function’s objects and methods. In this example, the controller provides a movie object.

NOTE: The elements and attributes in the html that begin with “ng-” are called Angular directives.

The code for any of the following controllers can be inserted within the script tag as per the highlighted comment above.

#1: Controller with a Function Declaration

A controller is simply a function. In this example, a function is declared within the script tags shown in the html above:

function SearchByMovieCtrl($scope) {
        $scope.movie = {
            title: ‘The Fellowship of the Ring’,
            director: ‘Jackson’,
            date: ‘2001-12-19′,
            mpaa: ‘PG-13′
        }
    }

The browser determines which controller function to call based on the ng-controller attribute defined within the html. So the name defined in the ng-controller attribute must match the name of the function. The name is “SearchByMovieCtrl” in this case.

When this function is called, Angular passes in a parameter called $scope. Anything that the controller function adds to the $scope is accessible to all of the html elements that are within the element containing the ng-controller attribute. (These are highlighted in green above.)

This controller function simply adds a movie object to the $scope. That movie object is then available to the html elements within the div tag containing the ng-controller attribute. The movie object properties can be displayed in the html using the Angular expression syntax {{}} as shown in the html.

To see the effect of the ng-controller attribute, try moving the {{movie.mpaa}} line outside of the outer div element and see what happens.

(The html will then display {{movie.mpaa}} instead of PG-13.)

This style of controller is quick to create, but is problematic in that it adds the function to the global namespace. This means it is available to any other code executing within the browser window. As the page becomes more complex, you could have problems with name collisions. (One function x overrides another function x because they have the same name.)

#2: Controller with a Function Expression

This technique is similar to the prior example, but defines the controller in a function expression instead of a function declaration. (If you are not sure of the difference between a function declaration and function expression, check out this post.)

    var SearchByMovieCtrl = function ($scope) {
        $scope.movie = {
            title: ‘The Fellowship of the Ring’,
            director: ‘Jackson’,
            date: ‘2001-12-19′,
            mpaa: ‘PG-13′
        }
    }

Notice that this code assigns the function to a variable called “SearchByMovieCtrl”. In this case, the function itself has no name, so it’s referred to as an anonymous function. But it could have a name without impacting how this controller works.

This technique has the same pros and cons as the prior technique. But now instead of putting a function on the global namespace, it is defining a variable on the global namespace.

#3: Controller with a Module

If you have been learning Angular, you know that Angular prefers that you define a module for your Angular application. The module is the component that ties all of the other Angular application components together.

After defining a module, you define a controller and register it with the module. That way the html can find the controller without needing to put the controller function in the global namespace of the application.

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

    app.controller(“SearchByMovieCtrl”,
            function ($scope) {
                $scope.movie = {
                    title: ‘The Fellowship of the Ring’,
                    director: ‘Jackson’,
                    date: ‘2001-12-19′,
                    mpaa: ‘PG-13′
                }
            });

The first line of this example defines an Angular module named “movieHunter”. The module setter has two parameters:

  • The first parameter is the name of the module.
  • The second parameter is an array of dependencies to other modules/components. For a demo application as simple as this one, there are not dependencies so the array is empty.

The second line then registers the controller with that module. The registration method takes two parameters:

  • The string name of the controller (SearchByMoveCtrl)
  • The function that is the controller. In this case, it is an anonymous function.

This example (and all of the examples that follow) requires one additional change to the original HTML. The body tag needs to be changed as follows:

<body ng-app=”movieHunter>

When the ng-app directive is not associated with a module, Angular looks for the controller in the global namespace for the page.

When the ng-app directive is assigned to a module name, Angular will then look for the controller registered with the module when it sees the ng-controller tag.

This technique is a much better than the prior two in that it uses the module. Angular much prefers to use a module to manage the components of the application. Plus it keeps the function out of the global namespace.

#4 Controller with a Module and Function Declaration

Some developers don’t like to put an entire function inside of a parameter of a method call. So you can instead define a function and pass a reference to it to the registration function as shown below.

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

function SearchByMovieCtrl($scope) {
    $scope.movie = {
        title: ‘The Fellowship of the Ring’,
        director: ‘Jackson’,
        date: ‘2001-12-19′,
        mpaa: ‘PG-13′
    }
};

app.controller(“SearchByMovieCtrl”,
        SearchByMovieCtrl);

The first line of this example defines the Angular module called “movieHunter” as in the prior example. The second line defines the controller function. The last line registers the controller with the module by referencing the function in the second parameter.

The problem with this approach is that we again have the SearchByMovieCtrl in the global namespace.

#5 Controller with a Module and Function Expression

This technique is similar to #4 above, but with a function expression. (If you are not sure of the difference between a function declaration and function expression, check out this post.)

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

var SearchByMovieCtrl = function ($scope) {
    $scope.movie = {
        title: ‘The Fellowship of the Ring’,
        director: ‘Jackson’,
        date: ‘2001-12-19′,
        mpaa: ‘PG-13′
    }
};

app.controller(“SearchByMovieCtrl”,
        SearchByMovieCtrl);

The first line of this example defines the Angular module called “movieHunter” as in the prior two examples. The second line defines the controller function and assigns it to a variable. The last line registers the controller with the module by referencing the function in the second parameter.

This approach puts the SearchByMovieCtrl variable in the global namespace.

#6 Controller with a Module and Minification-Safe Array

To improve performance when an application first starts up, many developers minify their files. This minification process removes excess spaces and changes function parameter names to a single character. This makes the files smaller so they download more quickly from the Web server to the client browser.

The problem with minifying an Angular application is that Angular expects the parameter names to be a specific name, not a minified name. For example, if you replaced the $scope variable in any of these examples with another variable name the code would not work.

To prevent the minification process from shortening the parameter names, you can provide the list of parameter names as strings as the second parameter to the controller registration function as shown below.

When you change the second parameter to a list (or technically speaking an array), you still need to pass the name of the controller (or the entire controller  function) as the last entry in the array. This is shown below.

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

function SearchByMovieCtrl($scope) {
    $scope.movie = {
        title: ‘The Fellowship of the Ring’,
        director: ‘Jackson’,
        date: ‘2001-12-19′,
        mpaa: ‘PG-13′
    }
};

app.controller(“SearchByMovieCtrl”,
        ["$scope",SearchByMovieCtrl]);

The second parameter of the controller registration is now an array containing:

  • The string name of each controller function parameter.
  • The reference to the function (or the function itself) as the last entry in the array.

This approach works the same as the prior approaches. The only difference is that the code will continue to work correctly after it has been minified. If you never plan to minify the files (and expect no one else on the team or in operations to minify the files) then this array is not needed.

#7 “Controller as” Syntax

This option is new in version 1.2 of Angular, so you won’t see it in any older posts.

The “Controller as” syntax requires several changes both to the original html and to the controller function itself:

  • First, the ng-controller attribute value in the html is changed to include the “as” keyword:

<div class=”row” ng-controller=”SearchByMovieCtrl as vm>

  • Second, the references to the controller objects in the html need to be changed to use the “controller as” alias.

    <div class=”row” ng-controller=”SearchByMovieCtrl as vm“>
        <div class=”col-md-6″>{{vm.movie.title}}</div>
        <div class=”col-md-2″>{{vm.movie.director}}</div>
        <div class=”col-md-2″>{{vm.movie.date}}</div>
        <div class=”col-md-2″>{{vm.movie.mpaa}}</div>
    </div>

NOTE: vm in this example stands for “view/model”. This is commonly what is used here because the controller is thought of as the connection between the view and the model or “view/model”.

  • Lastly, the $scope is no longer required as a parameter to the controller function. It is still available to the function, but implied. Instead of using “$scope”, the code instead adds objects and methods to “this”.

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

var SearchByMovieCtrl = function () {
    this.movie = {
        title: ‘The Fellowship of the Ring’,
        director: ‘Jackson’,
        date: ‘2001-12-19′,
        mpaa: ‘PG-13′
    }
};

app.controller(“SearchByMovieCtrl”,
        SearchByMovieCtrl);

The “controller as” syntax is clean and easy. But the explicit $scope syntax may be easier to understand when first learning.

#8 Controller with IIFE

A key issue with several of the above techniques was concern about the global namespace. Any variable or function declared external to a function becomes part of the global namespace of the application. This can be a problem, especially as the application gets larger.

One way to mitigate this problem is to use an Immediately Invoked Function Expression or IIFE (pronounced “iffy”). The idea of an IIFE is that if variables and functions are declared within a function, then the variables and functions are local to that function and not part of the global namespace.

You can wrap *any* of the above examples in an IIFE. Below is the code for example #7 wrapped in an IIFE:

(function () {
    var app = angular.module(“movieHunter”, []);

    var SearchByMovieCtrl = function () {
        this.movie = {
            title: ‘The Fellowship of the Ring’,
            director: ‘Jackson’,
            date: ‘2001-12-19′,
            mpaa: ‘PG-13′
        }
    };

    app.controller(“SearchByMovieCtrl”,
            SearchByMovieCtrl);
})();

For more information on IIFE by the person that coined the term, see this post.

You can use an IIFE with any of the above techniques to minimize pollution of your global namespace.

There you have it … 101 …  ok … 8 … ways to create a controller.

Enjoy!

PS circleCheck out my Pluralsight courses!

P.S. I am currently working on an Angular course for Pluralsight entitled “AngularJS Line of Business Applications”. Please comment below if you have suggestions for content that is not covered well elsewhere. Thanks!

RSS feed for comments on this post. TrackBack URI

Leave a comment

© 2014 Deborah's Developer MindScape   Provided by WPMU DEV -The WordPress Experts   Hosted by Microsoft MVPs