Angular - Understanding Modules and Controllers
Angular - Understanding Modules and Controllers
Angular - Understanding Modules and Controllers
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
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.
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.
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".
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:
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.
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.
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 $scopeas 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.
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.
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.
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.
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.
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.
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.
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.
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:
(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:
(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.
(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.
(function () {
angular.module('myAngularApplication').controller('booksController',
booksController);
}());
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 $scopebut 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:
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.
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:
(function () {
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:
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.
(function () {
$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:
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:
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.