Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Angular Tutorial - Part 2: Understanding Modules and Controllers

4.80/5 (46 votes)
6 Jul 2015CPOL13 min read 63K   1.6K  
Understanding and implementing modules and controller in Angular.js

Introduction

In this article, we will try to understand and implement modules and controller in Angular.js. We will start with Angular module and see why and how it is useful. We will then define a simple Angular module for a test application. Next, we will look at the concept of controllers in Angular and try to implement a simple controller to understand the concepts. We will also look at the JavaScript design pattern called IIFE (Immediately Invoked Function Expression) as it is a very useful pattern to avoid globals when creating Angular applications.

Link to complete series:

  1. Angular Tutorial - Part 1: Introduction to Angular.js
  2. Angular Tutorial - Part 2: Understanding Modules and Controllers
  3. Angular Tutorial - Part 3: Understanding and using Directives
  4. Angular Tutorial - Part 4: Understanding and Implementing Filters
  5. Angular Tutorial - Part 5: Understanding and Implementing Services
  6. Angular Tutorial - Part 6: Building and Validating Data Entry Forms
  7. Angular Tutorial - Part 7: Understanding Single Page Applications and Angular Routing

Background

In the previous article of this series, we looked at why Angular.js is in vogue and how JavaScript frameworks can help in creating client side code Single page applications in a structured manner. We also looked at a small sample where we tried to write the "Hello World" sample in Angular.js. We created a simple module, controller and placed some directives on our view to create our sample. In this article, we will dive into the details of the Angular modules and controllers and in the next article, we will look at the significance of directives in details.

Modules in Angular.Js

Let us start the discussion with the Angular modules. Modules in Angular can be thought of as a container where all the other parts of our application like controllers, services and directives will be contained in. We have been using the word module in software development as a logical piece of application that can be developed independently of other modules and which can be used/reused by other modules. Angular has literally taken this concept for Angular modules. If we could think of a logical piece of functionality that contains tightly coupled components (controllers, services, directives and filters), then we can pack them as an Angular module.

For small applications, perhaps one module is enough. In this case, the module can be thought of as something that defines our application. For larger applications, we might need to create multiple modules. The best part about module is that a module can easily be passed to another module and its functionality can easily be used by other modules.

Let's see how we can create a simple module in Angular. Typically, we create a separate .js file and define our module in it. This file will be responsible for creating the configuring the module for our application.

Image 1

In the above image, I have created a simple website with only index.html. We are using the domain style to organize our code. I have also added the angular.js file in the libs folder. Let us now create an app.js file in the app folder and include angular and our app.js reference in the index.html.

Image 2

Now that we have a structure in place, let us start creating an Angular module. To create a module, we need to use the angular.module function. Let's create a simple module named "myAngularApplication".

JavaScript
var myModule = angular.module('myAngularApplication', []);

What the above code will do is that, it will create an Angular module with the name of "myAngularApplication". Most people often ask what is the significance of the empty array that we are passing as a second argument. Remember we talked about modules being able to use other modules. The second array argument is to pass the dependencies to other modules. So let's say we have another module called "myAnotherApplication" which would want to use the above defined module. We will have to pass the myAngularApplication in the array argument as shown in the following code:

JavaScript
var myAnotherModule = angular.module('myAnotherApplication', ["myAngularApplication"]);

We can pass any number of modules in this array and it will enable the other modules to access the passed(injected) modules from within the module components.

Now here is an interesting part, if we forget to pass the second array argument, it will change the meaning of our code completely. The function with 2 arguments, i.e., the one with second argument as an array (empty or otherwise) is a setter function. It will create an Angular module for us. Whereas if we omit the second array argument, the function is a getter function, i.e., it will return the module that is created with the name passed. So it's important to understand this subtle but very important difference.

  • Module Setter: var myModule = angular.module('myAngularApplication', []);
  • Module Getter: var myModule = angular.module('myAngularApplication');

Now once we have the module defined, we will have to create all the components inside of this module. We will use a code like myModule.controller(........) to create controllers inside this module. The other components are also created with modules in a similar manner.

Last but not the least, if we want any view to use this Angular module, we need to put the ng-app="myAngularApplication" in that view. Let's do that in the HTML tag of our index page so that the complete page will have access to this Angular module.

Image 3

Now that we are all set with our application created, how can we quickly check whether what we did is working or not. We can test our application by writing a simple Angular expression inside our HTML body. An expression is the Angular's way to putting JavaScript code embedded inside the HTML views. Expressions can be put on views using double curly braces as {{ }}. To to test our application, let's put {{ 2 + 2 }} in our body. It we open the HTML and see 4 on the page, we can assure that the Angular module is created and is now working. If not, we will get error on the browser console.

Controllers in Angular.Js

Now that we have our module ready, let's move our focus to the controller. In simple words, controller is a JavaScript object that lets us control the data that is to be rendered on the views. It also lets us handle the data coming from the views and act upon it based on the application requirements. Behind every view, there is a controller which takes care of all the core business logic and is responsible to control the flow of data in the application.

The way Angular facilitates the control of view data via the controller class is by a magical object called $scope. $scope itself is a very vast topic - this is definetly not the time to start talking about scope of $scope and its details like inheritance. We will discuss the details of $scope in a later article, but for now, let's think of $scope as an object that is shared between the controller and the view. We can think of it as a common box where the view and controller will put all their belongings and perhaps pick up the required one whenever needed.

Image 4

Now that we know the basic definition and purpose of controller and $scope, let's define a simple controller in our application. Let's create a controller that will show the list of books on the view. Let's call it a booksController. Since we are using the domain style for organization, let's first add the .js file for the booksController, i.e., booksController.js. Let's add the reference to this file in our index.html after the app.js.

Image 5

Now that we have setup our code, let's see how we can create a controller function. A controller is a simple function that we need to create on the Angular module object. Now since we already have the module created and assigned in a global variable myModule, let's create our controller function on that only.

JavaScript
var booksController = function ($scope) {
    $scope.message = "Hello from booksController";
}

myModule.controller('booksController', booksController);

In the above code, we are first creating a function named booksController. We will use this function as our controller class and this we are passing the instance of $scope. Then, we are associating this function with our module using the myModule.controller function. Once this is done, our controller class is ready to be used with the view.

Note: The code that we just wrote is mainly for explanation. There are few issues with it which I will talk in a moment when we will discuss the minification safety and IIFE in the next section.

Now that we have our controller ready, let's see how we can attach it to a view. To associate a controller with a view, we need to use ng-controller attribute on the html element where we want to use this controller. Let's add the ng-controller on the body element of the view. Also, since we know that the controller is adding a property on $scope object called message, we can use Angular expression to access this property.

Image 6

Now with this code if we run the application, we can see the message being passed from controller to the view using the $scope object.

Image 7

Note: We have talked about one way and two way binding a little in the previous article and we will talk about it in detail in a later article but for now, using the Angular expression in view to access the controller property is enough.

Writing More Robust Code

We have created our first Angular module and controller. Are we ready to get started with some serious Angular development. The answer to this question is both yes and no. Yes because we have understood the concepts involved behind the module and controller, and No because our code is not good from a best practices perspective and believe it or not, it will not even work if we minify this code and start using it. Let's see what problems we have and how we can solve them.

Understanding IIFE (Immediately Invoked Function Expression)

So far, we have been using the free code to create our Angular module. The main reason for this was that we wanted our code to get executed as soon as the file is loaded. We are also putting our created module in a global variable which is also not a good idea. So let's see how we can fix these issues with the help of IIFEs.

First, let's move our free code into a function and then call this function. This will effectively be the same as our current code.

JavaScript
var myModule = null;

function CreateModule(){
    myModule = angular.module('myAngularApplication', []);
}

CreateModule();

Next, instead of explicitly calling this function, why not call the function at its definition itself. This can be done in the following manner:

JavasScript
var myModule = null;

(function CreateModule(){
    myModule = angular.module('myAngularApplication', []);
}());

Now since no one else will be calling this function as we are calling it at the definition itself, we can do away with the function name too. The resulting code will look like the following:

JavaScript
var myModule = null;

(function(){
    myModule = angular.module('myAngularApplication', []);
}());

This expression above is known as Immediately invoked function expression(IIFE) since the function definition will immediately invoke itself whenever the .js file is loaded.

The main reason the IIFE is effective is that we can have all the code immediately executing without needing to have global variables and functions. We still have the myModule variable declared as global so let's do away with it too.

JavaScript
(function(){
    myModule = angular.module('myAngularApplication', []);
}());

Now when we do this, our controller creation will fail as we were using the global variable to create controller with the module. To circumvent this problem, let's use the getter function angular.module to associate the controller with the module. And while we are at it, why not put the controller in an IIFE too.

JavaScript
(function () {

    var booksController = function ($scope) {
        $scope.message = "Hello from booksController";
    }

    angular.module('myAngularApplication').controller('booksController', booksController);

}());

Making Our Controllers Minification Safe

Now our controller and module are looking good and we are not using any global variables. Now when we are ready to push this code into production, we will first minify the code and then send it to production (for various optimization and other reasons). What minification will do is that it will change shorten the variable names. Which would mean that the $scope that we are passing to the booksController will no longer be $scope but some short variable name.

The problem here is that the Angular is still expecting a $scope variable to wire up the controller to view data passing. But the $scope variable is not present in the minified file thus resulting in the errors. Same problem will come with all the parameters that are being injected into controller function. So we need some way to indicate the Angular that the variable that is being passed to the controller is in fact a $scope so that it can be used after minification too.

To do this, we need to write our controller creation code a little differently. What we need to do is to pass the second argument as array where we can put the controller arguments as string literals. The resulting controller code will look like the following:

JavaScript
angular.module('myAngularApplication').controller('booksController', ["$scope", booksController]);

On the same lines, we can pass all the controller parameters as string literals in this function call and Angular will know that even after minification how these parameters should be treated.

Using the Controller as Syntax

Before we end this article, let us also look at an alternate way of passing data from controller to view. If our controller has some member variable defined that is not attached to scope that can be accessed from the view by using controller as syntax. Let's say our controller has a property greeting defined as:

JavaScript
(function () {

    var booksController = function ($scope) {
        $scope.message = "Hello from booksController";

        this.greeting = "This is a greeting message using controller as syntax";
    }

    angular.module('myAngularApplication').controller('booksController', ["$scope", booksController]);

}());

If we want to access this property from view, we can use the controller as syntax as shown below:

Image 8

Note: The benefit of using this approach is that our code is decoupled from the $scope object. Comparing $scope vs controller as syntax could be a little confusing at this stage so let's do that in a later article and keep this article free from digression.

Using the Code

Let's not end this article without writing a substantial functionality. Let's change our controller to keep track of a list of books and our view will show this list of books. Let's define an array of books on the $scope which will initially be empty. We will then create a function fetchBooks which will populate this list with some dummy books data. In real world applications, this data will be retrieved from the server. Let's now look at the controller code with these changes:

JavaScript
(function () {

    var booksController = function ($scope) {
        $scope.message = "Hello from booksController";

        this.greeting = "This is a greeting message using controller as syntax";

        $scope.books = [];

        $scope.fetchBooks = function () {
            $scope.books = [
                { ID: 1, BookName: "Test Books 1", AuthorName: "Test Author 1", ISBN: "TEST1" },
                { ID: 2, BookName: "Test Books 2", AuthorName: "Test Author 2", ISBN: "TEST2"},
                { ID: 3, BookName: "Test Books 3", AuthorName: "Test Author 3", ISBN: "TEST3"},
                { ID: 4, BookName: "Test Books 4", AuthorName: "Test Author 4", ISBN: "TEST4"},
                { ID: 5, BookName: "Test Books 5", AuthorName: "Test Author 5", ISBN: "TEST5"}
            ];
        }
    }

    angular.module('myAngularApplication').controller('booksController', ["$scope", booksController]);

}());

Now on the view part. I have added a little bootstrap to the view to make it look pretty so the view code now looks like the following:

Image 9

Except for the bootstrap classes, what we have done here is that we have created a button that is calling the fetchBooks function on ng-click directive. ng-click directive can be associated with the HTML elements and act on click event to invoke a controller function.

Below that, we have a table that is rendering rows using ng-repeat directive. ng-repeat is a directive that can iterate over items of a collection and render the specified html elements for all the items. We are rendering row for each book in the books collection.

When we run the code and look click on the fetch button, we can see the result on screen as:

Image 10

Note: We will look at the ng-click and ng-repeat directives in detail in further articles, here we are just giving a sneak peak to see how things get wired up and how easily we got our view to render the list of books.

And now, we have a basic Angular application ready that is capable of rendering a list of hard coded books.

Point of Interest

In this article, we looked at the concept of modules and controllers in Angular. We looked at how we can use IIFEs to better manage our code and how to make our controllers minification safe. This has been written from a beginners' perspective and I hope this has been informative.

History

  • 27th May, 2015: First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)