Modules

Exclusive offer: get 50% off this eBook here
Instant AngularJS Starter [Instant]

Instant AngularJS Starter [Instant] — Save 50%

A concise guide to start building dynamic web applications with AngularJS, one of the Web's most innovative JavaScript frameworks with this book and ebook.

$12.99    $6.50
by Dan Menard | March 2013 | Web Development

Every framework has a set of features that differentiates it from the other frameworks out there. In this article by Dan Menard, the author of Instant AngularJS Starter, we will discover the strengths of AngularJS by exploring one of its most prominent features—Modules.

(For more resources related to this topic, see here.)

We are going to try mastering modules which will help us break up our application into sensible components.

Our configuration file for modules is given as follows:

var guidebookConfig = function($routeProvider) { $routeProvider .when('/', { controller: 'ChaptersController', templateUrl: 'view/chapters.html' }) .when('/chapter/:chapterId', { controller: 'ChaptersController', templateUrl: 'view/chapters.html' }) .when('/addNote/:chapterId', { controller: 'AddNoteController', templateUrl: 'view/addNote.html' }) .when('/deleteNote/:chapterId/:noteId', { controller: 'DeleteNoteController', templateUrl: 'view/addNote.html' }) ; }; var Guidebook = angular.module('Guidebook', []). config(guidebookConfig);

The last line is where we define our Guidebook module. This creates a namespace for our application.

Look at how we defined the add and delete note controllers:

Guidebook.controller('AddNoteController', function ($scope, $location, $routeParams, NoteModel) { var chapterId = $routeParams.chapterId; $scope.cancel = function() { $location.path('/chapter/' + chapterId); } $scope.createNote = function() { NoteModel.addNote(chapterId, $scope.note.content); $location.path('/chapter/' + chapterId); } } ); Guidebook.controller('DeleteNoteController', function ($scope, $location, $routeParams, NoteModel) { var chapterId = $routeParams.chapterId; NoteModel.deleteNote(chapterId, $routeParams.noteId); $location.path('/chapter/' + chapterId); } );

Since Guidebook is a module, we're really making a call to module.controller() to define our controller. We also called Guidebook.service(), which is really just module.service(), to define our models.

One advantage of defining controllers, models, and other components as part of a module is that it groups them together under a common name. This way, if we have multiple applications on the same page, or content from another framework, it's easy to remember which components belong to which application.

The second advantage of defining components as part of a module is that AngularJS will do some heavy lifting for us.

Take our NoteModel, for instance. We defined it as a service by calling module.service(). AngularJS provides some extra functionality to services within a module. One example of that extra functionality is dependency injection. This is why in our DeleteNoteController, we can include our note model simply by asking for it in the controller's function:

Guidebook.controller('DeleteNoteController', function ($scope, $location, $routeParams, NoteModel) { var chapterId = $routeParams.chapterId; NoteModel.deleteNote(chapterId, $routeParams.noteId); $location.path('/chapter/' + chapterId); } );

This only works because we registered both DeleteNoteController and NoteModel as parts of the same module.

Let's talk a little more about NoteModel. When we define controllers on our module, we call module.controller(). For our models, we called module.service(). Why isn't there a module.model()?

Well, it turns out models are a good bit more complicated than controllers. In our Guidebook application, our models were relatively simple, but what if we were mapping our models to some external API, or a full- fledged relational database?

Because there are so many different types of models with vastly different levels of complexity, AngularJS provides three ways to define a model: as a service, as a factory, and as a provider.

We'll now see how we define a service in our NoteModel:

Guidebook.service('NoteModel', function() { this.getNotesForChapter = function(chapterId) { ... }; this.addNote = function(chapterId, noteContent) { ... }; this.deleteNote = function(chapterId, noteId) { ... }; });

It turns out this is the simplest type of model. We're simply defining an object with a number of functions that our controllers can call. This is all we needed in our case, but what if we were doing something a little more complicated?

Let's pretend that instead of using HTML5 local storage to store and retrieve our note data, we're getting it from a web server somewhere. We'll need to add some logic to set up and manage the connection with that server.

The service definition can help us a little here with the setup logic; we're returning a function, so we could easily add some initialization logic.

When we get into managing the connection, though, things get a little messy. We'll probably want a way to refresh the connection, in case it drops. Where would that go? With our service definition, our only option is to add it as a function like we have for getNotesForChapter(). This is really hacky; we're now exposing how the model is implemented to the controller, and worse, we would be allowing the controller to refresh the connection.

In this case, we would be better off using a factory. Here's what our NoteModel would look like if we had defined it as a factory:

Guidebook.factory('NoteModel', function() { return { getNotesForChapter: function(chapterId) { ... }, addNote: function(chapterId, noteContent) { ... }, deleteNote: function(chapterId, noteId) { ... } }; });

Now we can add more complex initialization logic to our model. We could define a few functions for managing the connection privately in our factory initialization, and give our data methods references to them. The following is what that might look like:

Guidebook.factory('NoteModel', function() { var refreshConnection = function() { ... }; return { getNotesForChapter: function(chapterId) { ... refreshConnection(); ... }, addNote: function(chapterId, noteContent) { ... refreshConnection(); ... }, deleteNote: function(chapterId, noteId) { ... refreshConnection(); ... } }; });

Isn't that so much cleaner? We've once again kept our model's internal workings completely separate from our controller.

Let's get even crazier. What if we backed our models with a complex database server with multiple endpoints? How could we configure which endpoint to use?

With a service or a factory, we could initialize this in the model. But what if we have a whole bunch of models? Do we really want to hardcode that logic into each one individually?

At this point, the model shouldn't be making this decision. We want to choose our endpoints in a configuration file somewhere.

Reading from a configuration file in either a service or a factory will be awkward. Models should focus solely on storing and retrieving data, not messing around with configuration updates.

Provider to the rescue! If we define our NoteModel as a provider, we can do all kinds of configuration from some neatly abstracted configuration file.

Here's how our NoteModel looks if we convert it into a provider:

Guidebook.provider('NoteModel', function() { this.endpoint = 'defaultEndpoint'; this.setEndpoint = function(newEndpoint) { this.endpoint = newEndpoint; }; this.$get = function() { var endpoint = this.endpoint; var refreshConnection = function() { // reconnect to endpoint }; return { getNotesForChapter: function(chapterId) { ... refreshConnection(); ... }, addNote: function(chapterId, noteContent) { ... refreshConnection(); ... }, deleteNote: function(chapterId, noteId) { ... refreshConnection(); ... } }; }; });

Now if we add a configuration file to our application, we can configure the endpoint from outside the model:

Guidebook.config(function(NoteModelProvider) { NoteModelProvider.setEndpoint('anEndpoint'); });

Providers give us a very powerful architecture. If we have several models, and we have several endpoints, we can configure all of them in one single configuration file. This is ideal, as our models remain single-purpose and our controllers still have no knowledge of the internals of the models they depend on.

Summary

Thus we saw in this article modules provide an easy, flexible way to create components in AngularJS. We've seen three different ways to define a model, and we've discussed the benefits of using a module to create an application namespace.

Resources for Article :


Further resources on this subject:


Instant AngularJS Starter [Instant] A concise guide to start building dynamic web applications with AngularJS, one of the Web's most innovative JavaScript frameworks with this book and ebook.
Published: February 2013
eBook Price: $12.99
See more
Select your format and quantity:

About the Author :


Dan Menard

Dan Menard is a web developer, originally from Canada and now living in California. He has many years of consulting work under his belt, which has taught him a great deal about ramping up on new technologies and the value of a good framework. He currently works at Netflix on the 10’ UI team.

He has spoken at a number of conferences, most recently OSCON 2012. He also maintains a blog, and occasionally hangs around Twitter and Google+. If you’re in the bay area, you may be able to catch him at the Mountain View AngularJS meet-ups.

Books From Packt


Sencha Touch Mobile JavaScript Framework
Sencha Touch Mobile JavaScript Framework

Learning JavaScriptMVC
Learning JavaScriptMVC

Getting Started with Meteor.js JavaScript Framework
Getting Started with Meteor.js JavaScript Framework

Backbase 4 RIA Development
Backbase 4 RIA Development

jQuery 1.4 Reference Guide
jQuery 1.4 Reference Guide

jQuery UI 1.7: The User Interface Library for jQuery
jQuery UI 1.7: The User Interface Library for jQuery

 Instant Galleria How-to [Instant]
Instant Galleria How-to [Instant]

Spring Persistence with Hibernate
Spring Persistence with Hibernate


Your rating: None Average: 1 (1 vote)

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
1
m
w
5
x
b
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software