In the previous part, I covered how to get started with Angular, as well as the basics of how it works “internally”. This part will build on that knowledge, and add modules and controllers to the mix.
If you haven’t read the previous part, I suggest at least skimming through it. The first part might not be necessary to understand this part, but the basics covered in the previous will be…
To start off, we need a clean “solution”, containing only an index.htm file, and Angular… The index.htm file should look something like this
<!DOCTYPE html>
<html data-ng-app>
<head>
<title>A simple introduction to AngularJS - Part 3</title>
<script src="Scripts/angular.min.js"></script>
</head>
<body>
</body>
</html“>
As you can see, it is pretty similar to the one from the last part. It includes the angular.min.js file to enable Angular, and a data-ng-app attribute to define the page as an Angular app. Other than that, it is just a skeleton page. But this is where we go in a different direction. It is time to add some functionality, and thus, time to add a controller. However, controllers don’t just float around on their own in Angular. To be honest, nothing “floats around” in Angular. Pretty much every little bit of code you write when building Angular applications will be grouped together in what is called modules…
So what are modules? Well, modules are basically just groups of functionality clumped together into a “unit”. They can contain a lot of different entity types, such as controllers, services and directives. They can also depend on other modules. That way, you can build a set of functionality into one module, but demand another set of functionality, another module, be available as well. Angular will then figure out how to load the the modules in the correct order to get all dependencies fulfilled.
If you look at samples on the web on how to build up Angular applications, they tend to group functionality into modules based on their type. They will add a “controllers” module, a “directives” module, a “services” module and so on. This is, or at least used to be, the way that the “default” Angular starter kit, angular-seed was built.
Side note: angular-seed is a great way to get started and see how and Angular project work. However, I might not look at it too much…at least not from an solution set up kind of way…
The problem with building your modules based on this idea is that you are likely going to end up loading in a whole lot of stuff that isn’t actually used. At least if you aren’t building a true SINGLE page application where all parts of the application is used. If you spread out the SPA goodness over several different parts, or pages, of an application, having to load a bunch of controllers or services or directives that aren’t actually going to be used on the current part/page, is just a waste or bandwidth and time…
A better suggestion is to split your modules into the type of functionality they offer. For example splitting it into a “user management” module, and a “billing” module and so on. More of a service oriented kind of split. That way, you only load the parts that you need at the moment in each scenario. And the things that are “cross-cutting” and used by several modules are put in some common module or modules…
Anyhow…let’s not get too bogged down with that right now. How to structure your application is up to the developer building it, and the solution being built. There is no magic bullet that will suit everyone, so I’ll leave it up to you to find a solution that works for you. Let’s instead focus on how you build modules.
In this VERY simple demo, I will add a single new JavaScript file called app.js. “App.js” seems to be a very common name for the “entry point” of the application when building really simple, and non-modularized application.
I add a reference to the app.js file in the index.htm file AFTER the reference to Angular. The app.js file will assume that Angular is already available, so make sure it is added AFTER Angular.
<!DOCTYPE html>
<html data-ng-app>
<head>
<title>A simple introduction to AngularJS - Part 3</title>
<script src="Scripts/angular.min.js"></script>
<script src="Scripts/app.js"></script>
</head>
<body>
</body>
</html>
Ok, let’s create a new module…
This is done by adding the following line in the app.js file
var appModule = angular.module('demoApplication',[]);
Let’s look at that in detail. This line of code declares a variable to keep track of the module. This is optional as I will show in a little while, but a very common way of doing it. The appModule variable is assigned the return value of a call to a function called module() on the global variable angular. The return value is a reference to the module that has just been created.
The variable angular is defined in angular.min.js, which is why it needs to be referenced first in the HTML. It is a global variable that is used to “access” the functionality offered by Angular…
The module() function takes 2 parameters, the name of the module, and an array of names of other modules that it depends on. The second parameter is an empty array in this case, as it doesn’t depend on any other module. However, an array has to be passed in. Calling module() without passing in the 2nd parameter will return an existing module with that name, as long as it has already been defined…otherwise, Angular throws an exception telling you that it can’t find a module with that name…
To add a new controller to the newly created module, you call the controller() function on the module. Like this
var appModule = angular.module('demoApplication',[]);
appModule.controller('demoCtrl', function() { });
Before we go into creating a “proper” controller with actual functionality, there are a few more things to cover in regards to modules, and module definitions.
First of all, the module instance returned from the call to the module() method supports a fluent syntax. All calls to it returns the same instance again. So instead of storing a reference to the module in a variable and repeatedly calling methods on it, you can shorten the syntax like this
angular.module('demoApplication',[])
.controller('demoCtrl', function() { })
// Any other registration you might need
;
That way, you don’t have an extra global variable hanging around…and global variables are less than ideal, and should be avoided as much as possible. Why? Well, because they have the ability to introduce some weird issues in your applications. Some that can be very hard to find and solve.
A lot of Angular introductions define the extra variable. I don’t know why they do so, and I am not a fan of it. The answer to why might be that they want to spread out the module across several physical files, and by declaring the module in one file, with a global reference to it, you can add more things to it by referencing the global variable.
That potentially became a little hard to follow…
Imagine this
1. The application loads in file1.js, which declares the variable module and assigns it to a new module called myModule.
2. The application then loads in file2.js, which uses the global variable module to add a controller to it by calling the controller() function.
/*------ file1.js -------*/
var module = angular.module('myModule', []);
/*--- end of file1.js ---*/
/*------ file2.js -------*/
module.controller('myCtrl', function() { });
/*--- end of file2.js ---*/
As long as file1 is referenced before file2, you will end up with a single module called myModule, containing a single controller, but having it split into multiple files. However, you will also have a global variable called module hanging around, and potentially being overwritten or changed or whatever.
If you do the same thing in several modules, and you happen to load them in the wrong order, you will likely end up with really messed up modules containing bits and pieces from other entities.
However, if you do it right, it works, and seems to be a pretty common practice…but I personally find a little “brittle”. And the fact that it requires you to get globally unique names for you module variables makes me take a different route in most cases…
That “other route” is to use the fact that calling module() without passing a second argument will give you access to the, hopefully, already registered module instance…
Like this
1. The application loads in file1.js, which declares a module called myModule. It does not hold a reference to the returned module instance. It just declares it in this case. You could of course also use the fluent syntax to add some of its contained entity types.
2. The application then loads in file2.js, which calls angular.module(‘myModule’), getting back a reference to the module that was created in file1.js. It then uses the fluent syntax to add whetever it needs to add to the module. In this case a controller.
/*------ file1.js -------*/
angular.module('myModule', []);
/*--- end of file1.js ---*/
/*------ file2.js -------*/
angular.module('myModule')
.controller('myCtrl', function() { });
/*--- end of file2.js ---*/
This will give the same result as the first version. A single module in 2 different files. But with no global variable hanging around…
Ok…long excursion into splitting up modules into several files. Maybe a little off topic, but I find that feature VERY useful. It is easy to put your whole module in a single file as long as you are doing demo applications, and is what you will see in a lot of examples on the web. But as the modules get more and more complicated, and larger and larger, splitting them into several files makes for a more manageable solution structure… And if it gets to unwieldy, you might consider splitting your module into two or more smaller modules. Just as “God objects” are less than ideal, putting everything in one module can cause some problems as well…
The last detail to mention before talking about the controller side of this post, is the array of dependencies that you pass in when adding a module... As I mentioned, I sent in an empty array as the second parameter when defining my new module. If the module did depend on other modules, I would just pass in an array containing names of those modules. Angular would then figure out to load the modules in the necessary order to get all dependencies sorted.
Imagine that we were creating a module that use routing. Routing is defined in a separate module in Angular, as well as in a separate JavaScript file.
Declaring this new module would look like this
angular.module('navApplication',['ngRoute']);
And as long as the angular-route.min.js file, which contains the ngRoute module, is referenced in the HTML, it would get loaded before the navApplication module. And by loading it before the navApplication, any services and things it adds to the system would be available for use in the navApplication module.
That’s it for modules. At least for now. I promise!!! Let’s go ahead with the controller aspect of this post.
So far, I have, among all the module information, shown you how to add a new controller. If you missed it, it is done like this
angular.module('demoApplication',[])
.controller('demoCtrl', function() { });
This will declare a controller called demoCtrl. It has yet to get some form of functionality, but at least it is defined… As you can see, a controller is actually nothing more then a function that is executed at the appropriate time.
Unfortunately, this specific controller doesn’t actually do very much. And to be honest, it can’t actually do very much in its current shape. But let’s make sure it loads up properly before we go and add some form of functionality to it.
The easiest way to confirm that it is being “loaded” is to go completely old school on it. And by “old school”, I mean using an alert()… We could obviously use console.log if we wanted a less intrusive notification, but in this case, I don’t care. The louder the better…
angular.module('demoApplication',[])
.controller('demoCtrl', function() {
alert('I loaded fine!');
});
Ok, that will make sure a message box is shown as soon as the controller is “loaded”. And by “loaded”, I mean executed… As mentioned before, the controller is just a method invoked at the appropriate time.
Next, we need to tell angular that this is the controller we want. But first, we need to tell Angular that the demoApplication module is the “main” module for out application. And by “main” module, I mean the first one to load, the “entry point”. It doesn’t necessarily need to contain the first controller to use or anything, but it is the one that Angular should load first. Any dependencies will then be loaded as well if needed…
So how do we define this module as being the “main module”, “root module”, “application module”, “entry point” or whatever you want to call it? Well, you use the data-ng-app attribute. Just change it from just being present, to have a value corresponding to the name of the module to load. Like this
<!DOCTYPE html>
<html data-ng-app="demoApplication">
<head>
...
</head>
<body>
</body>
</html>
This will tell Angular to load up the demoApplication module as soon as possible. However, it won’t tell Angular that we want to use the demoCtrl controller. This requires the use of another Angular directive. In this case, the ngController directive. And as you hopefully remember, ngController translates into data-ng-controller when turned into an attribute. And the value for it is…wait for it…wait for it…the name of the controller you want to use. Like so
<!DOCTYPE html>
<html data-ng-app="demoApplication" data-ng-controller="demoCtrl">
<head>
...
</head>
<body>
</body>
</html>
In this case, I put the controller on the html element. It doesn’t quite matter where you put it, and you can even nest controllers, but the important thing is that the controller will be “connected” to anything “below” the declaring element in the HTML DOM. Actually it will be “connected” to the scope that is “connected” to the elements…but let’s leave that for now…
Loading this up in a browser gives off a familiar sound, and shows up a message box saying that the controller has loaded fine.
Ok…I guess it is time to do something useful…or at least semi useful…
However, right now, the controller can’t actually do anything at all. At least not something that interacts with the DOM.
Side note: Actually it can, because it has full access to the window variable and so on. However, that should never be used to manipulate the DOM. Manipulating the DOM is the responsibility of directives. Doing it straight from the controller couples the controller to the view, and adds a dependency that is very hard to test for example…
Instead, Angular offers, as previously mentioned, dependency injection. Almost everything you do in your controllers, is done through services that are injected into the controller function. Either built in Angular services, or services you build on your own. This is a very powerful layer of abstraction that enables good testing capabilities, low coupling and so on.
One of the services that Angular offers is called $scope. This is a service that gives us access to the scope object used for databinding. So let’s request that through injection, and set a property on it…
angular.module('demoApplication',[])
.controller('demoCtrl', function($scope) {
$scope.prop = 'Hello World';
});
By just adding it as a parameter to the controller function, Angular will make sure it gets injected properly…
Now that we have a scope with a defined property, let’s change the HTML to output the value of that property
<!DOCTYPE html>
<html data-ng-app="demoApplication" data-ng-controller="demoCtrl">
<head>
...
</head>
<body>
{{prop}}
</body>
</html>
And the result? Well
Just as expected. The module is loaded, the controller is executed, and the controller gets the $scope services injected. The controller function sets the value of the property prop, and Angular makes sure it is shown in the UI where the Mustache markup is placed. Very cool!
This works nice and well, until you decide that you want to minify your JavaScript, which you will at some point. For example, if you are working with ASP.NET and use bundles to add your JavaScript to the page, as you should, then everything will work fine until you decide to build your solution in Release mode… When you do that, everything falls apart and stops working… Why? Well, the minification process will rename all variables to as short variables as possible to make the code as compact as possible. As the $root parameter is renamed, the dependency injector in Angular won’t know what is expected to be injected as the name is wrong, and everything falls apart. Because of this, you should never declare your module items using the currently shown syntax.
Instead of passing in a function as the second parameter to, for example, the controller() function, you should pass in an array. The array should contain a string version of each of the parameter names used by the function, as well as the function as the last entry in the array. In this case, it would look like this
angular.module('demoApplication',[])
.controller('demoCtrl', ['$scope', function($scope) {
$scope.prop = 'Hello World';
}]);
As you can see, the second parameter is now array with two items instead of a function. The first entry in the array is a string containing the word “$scope”, and the second is a function that takes one parameter called “$scope”. When minification is done, strings are never changed, so the string version of $scope will stay the same. This is then used by the injector to figure out what to no matter what the parameter is named… And if you have more than one parameter, you just add more entries to the array…
That’s it! It isn’t more complicated than that… But I want to mention 2 naming conventions before we carry on, one important, and one not so important. The first one is that services that are prefixed with $ are part of Angular. Never prefix your service with $. The second one is that controllers are generally postfixed with “Ctrl” to identify them as controllers. This is a general recommendation I think…so adhere to it if you want to…
Ok, let’s look at one final thing before we end this post. I want to introduce another Angular directive, as well as some functionality in the controller.
The directive I want to introduce is called ngClick. It handles “connecting” the click event from an HTML element/control, to a function on the scope.
To show it off, I will add a button to my view, and hook up the click event to a method called changeValue()
<!DOCTYPE html>
<html data-ng-app="demoApplication" data-ng-controller="demoCtrl">
<head>
...
</head>
<body>
<button data-ng-click="changeValue()">Click me</button>
{{prop}}
</body>
</html>
And then I will add a changeValue() function to my scope.
Remember, you are not binding your view to you controller, but to the scope. So anything you want the view to access has to be added to the scope in the controller. Like this
angular.module('demoApplication',[])
.controller('demoCtrl', ['$scope', function($scope) {
$scope.prop = 'Hello World';
$scope.changeValue = function() {
$scope.prop = 'Hello World Again';
};
}]);
Running the “application” in the browser will show the text “Hello World”. But as soon as the button is clicked, the value changes to “Hello World Again”.
Ok, that will have to do for this time! Next part will contain more information about controllers, scope and scope inheritance…
Cheers for now!