This publication aims to provide a more theoretical overview of some of the AngularJS components in order to show you how the things you are already familiar with (like different Object-Oriented Design Patterns) fit in the picture.
Link to the first part of the series. Link to the second part of the series.
This is the last blog post of the series “AngularJS in Patterns”. You can find the original GitHub repository, which contains all the information at github.com/mgechev/angularjs-in-patterns. If you want to be aware of my up-coming work, you can follow me at github.com/mgechev.
Directives
Composite
The composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects are to be treated in the same way as a single instance of an object. The intent of a composite is to “compose” objects into tree structures to represent part-whole hierarchies.
According to the Gang of Four, MVC is nothing more than combination of:
- Strategy
- Composite
- Observer
They state that the view is composition of components. In AngularJS the situation is similar. Our views are formed by a composition of directives and DOM elements, on which these directives could be applied.
Lets look at the following example:
<!doctype html>
<html>
<head>
</head>
<body>
<zippy title="zippy">
zippy!
</zippy>
</body>
</html>
myModule.directive('zippy', function () {
return {
restrict: 'E',
template: '<div><div class="header"></div><div class="content" ng-transclude></div></div>',
link: function (scope, el) {
el.find('.header').click(function () {
el.find('.content').toggle();
});
}
}
});
This example defines a simple directive, which is a UI component. The defined component (called “zippy”) has header and content. Click on its header toggles the visibility of the content.
From the first example we can note that the whole DOM tree is a composition of elements. The root component is the html
element, directly followed by the nested elements head
and body
and so on…
In the second, JavaScript, example we see that the template
property of the directive, contains markup with ng-transclude
directive inside it. So this means that inside the directive zippy
we have another directive called ng-transclude
, i.e. composition of directives. Theoretically we can nest the components infinitely until we reach a leaf node.
Interpreter
In computer programming, the interpreter pattern is a design pattern that specifies how to evaluate sentences in a language. The basic idea is to have a class for each symbol (terminal or nonterminal) in a specialized computer language. The syntax tree of a sentence in the language is an instance of the composite pattern and is used to evaluate (interpret) the sentence.
Behind its $parse
service, AngularJS provides its own implementation of interpreter of a DSL (Domain Specific Language). The used DSL is simplified and modified version of JavaScript.
The main differences between the JavaScript expressions and AngularJS expressions are that AngularJS expressions:
- may contain filters with UNIX like pipe syntax
- don’t throw any errors
- don’t have any control flow statements (exceptions, loops, if statements although you can use the ternary operator)
- are evaluated in given context (the context of the current
$scope
)
Inside the $parse
service are defined two main components:
//Responsible for converting given string into tokens
var Lexer;
//Responsible for parsing the tokens and evaluating the expression
var Parser;
Once given expression has been tokenized it is cached internally, because of performance concerns.
The terminal expressions in the AngularJS DSL are defined as follows:
var OPERATORS = {
/* jshint bitwise : false */
'null':function(){return null;},
'true':function(){return true;},
'false':function(){return false;},
undefined:noop,
'+':function(self, locals, a,b){
//...
},
'*':function(self, locals, a,b){return a(self, locals)*b(self, locals);},
'/':function(self, locals, a,b){return a(self, locals)/b(self, locals);},
'%':function(self, locals, a,b){return a(self, locals)%b(self, locals);},
'^':function(self, locals, a,b){return a(self, locals)^b(self, locals);},
'=':noop,
'===':function(self, locals, a, b){return a(self, locals)===b(self, locals);},
'!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);},
'==':function(self, locals, a,b){return a(self, locals)==b(self, locals);},
'!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);},
'<':function(self, locals, a,b){return a(self, locals)<b(self, locals);},
'>':function(self, locals, a,b){return a(self, locals)>b(self, locals);},
'<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);},
'>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);},
'&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);},
'||':function(self, locals, a,b){return a(self, locals)||b(self, locals);},
'&':function(self, locals, a,b){return a(self, locals)&b(self, locals);},
'|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));},
'!':function(self, locals, a){return !a(self, locals);}
};
We can think of the function associated with each terminal as implementation of the AbstractExpression
’s interface.
Each Client
interprets given AngularJS expression in a specific context - specific scope.
Few sample AngularJS expressions are:
// toUpperCase filter is applied to the result of the expression
// (foo) ? bar : baz
(foo) ? bar : baz | toUpperCase
Template View
Renders information into HTML by embedding markers in an HTML page.
The dynamic page rendering is not that trivial thing. It is connected with a lot of string concatenations, manipulations and frustration. Far easier way to build your dynamic page is to write your markup and embed little expressions inside it, which are later evaluated in given context and so the whole template is being compiled to its end format. In our case this format is going to be HTML (or even DOM). This is exactly what the template engines do - they take given DSL, evaluate it in the appropriate context and then turn it into its end format.
Templates are very commonly used especially in the back-end. For example, you can embed PHP code inside HTML and create a dynamic page, you can use Smarty or you can use eRuby with Ruby in order to embed Ruby code inside your static pages.
For JavaScript there are plenty of template engines, such as mustache.js, handlebars, etc. Most of these engines manipulate the template as a string. The template could be located in different places - as static file, which is fetched with AJAX, as script
embedded inside your view or even inlined into your JavaScript.
For example:
<script type="template/mustache">
<h2>Names</h2>
{{#names}}
<strong>{{name}}</strong>
{{/names}}
</script>
The template engine turns this string into DOM elements by compiling it within a given context. This way all the expressions embedded in the markup are evaluated and replaced by their value.
For example if we evaluate the template above in the context of the following object: { names: ['foo', 'bar', 'baz'] }
, so we will get:
<h2>Names</h2>
<strong>foo</strong>
<strong>bar</strong>
<strong>baz</strong>
AngularJS templates are actually HTML, they are not in an intermediate format like the traditional templates are. What AngularJS compiler does is to traverse the DOM tree and look for already known directives (elements, attributes, classes or even comments). When AngularJS finds any of these directives it invokes the logic associated with them, which may involve evaluation of different expressions in the context of the current scope.
For example:
<ul ng-repeat="name in names">
<li>{{name}}</li>
</ul>
in the context of the scope:
$scope.names = ['foo', 'bar', 'baz'];
will produce the same result as the one above. The main difference here is that the template is not wrapped inside a script
tag, it is HTML instead.
Scope
Observer
The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement distributed event handling systems.
There are two basic ways of communication between the scopes in an AngularJS application. The first one is calling methods of parent scope by a child scope. This is possible since the child scope inherits prototypically by its parent, as mentioned above (see Scope). This allows communication in a single direction - child to parent. Some times it is necessary to call method of given child scope or notify it about a triggered event in the context of the parent scope. AngularJS provides built-in observer pattern, which allows this. Another possible use case, of the observer pattern, is when multiple scopes are interested in given event but the scope, in which context the event is triggered, is not aware of them. This allows decoupling between the different scopes, non of the scopes should be aware of the rest of the scopes.
Each AngularJS scope has public methods called $on
, $emit
and $broadcast
. The method $on
accepts topic as first argument and callback as second. We can think of the callback as an observer - an object, which implements the Observer
interface (in JavaScript the functions are first-class, so we can provide only implementation of the notify
method):
function ExampleCtrl($scope) {
$scope.$on('event-name', function handler() {
//body
});
}
In this way the current scope “subscribes” to events of type event-name
. When event-name
is triggered in any parent or child scope of the given one, handler
would be called.
The methods $emit
and $broadcast
are used for triggering events respectively upwards and downwards through the scope chain.
For example:
function ExampleCtrl($scope) {
$scope.$emit('event-name', { foo: 'bar' });
}
The scope in the example above, triggers the event event-name
to all scopes upwards. This means that each of the parent scopes of the given one, which are subscribed to the event event-name
, would be notified and their handler callback will be invoked.
Analogical is the case when the method $broadcast
is called. The only difference is that the event would be transmitted downwards - to all children scopes.
Each scope can subscribe to any event with multiple callbacks (i.e. it can associate multiple observers to given event).
In the JavaScript community this pattern is better known as publish/subscribe.
Chain of Responsibilities
The chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain. A mechanism also exists for adding new processing objects to the end of this chain.
As stated above the scopes in an AngularJS application form a hierarchy known as the scope chain. Some of the scopes are “isolated”, which means that they don’t inherit prototypically by their parent scope, but are connected to it via their $parent
property.
When $emit
or $broadcast
are called we can think of the scope chain as event bus, or even more accurately - chain of responsibilities. Once the event is triggered it is emitted downwards or upwards (depending on the method, which was called). Each subsequent scope may:
- Handle the event and pass it to the next scope in the chain
- Handle the event and stop its propagation
- Pass the event to the next scope in the chain without handling it
- Stop the event propagation without handling it
In the example below you can see an example in which ChildCtrl
triggers an event, which is propagated upwards through the scope chain. In the case above each of the parent scopes (the one used in ParentCtrl
and the one used in MainCtrl
) are going to handle the event by logging into the console: "foo received"
. If any of the scopes should be considered as final destination it can call the method stopPropagation
of the event object, passed to the callback.
myModule.controller('MainCtrl', function ($scope) {
$scope.$on('foo', function () {
console.log('foo received');
});
});
myModule.controller('ParentCtrl', function ($scope) {
$scope.$on('foo', function (e) {
console.log('foo received');
});
});
myModule.controller('ChildCtrl', function ($scope) {
$scope.$emit('foo');
});
The different handlers from the UML diagram above are the different scopes, injected to the controllers.
Command
In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to represent and encapsulate all the information needed to call a method at a later time. This information includes the method name, the object that owns the method and values for the method parameters.
Before continuing with the application of the command pattern lets describe how AngularJS implements data binding.
When we want to bind our model to the view we use the directives ng-bind
(for single-way data binding) and ng-model
(for two-way data binding). For example, if we want each change in the model foo
to reflect the view we can:
<span ng-bind="foo"></span>
Now each time we change the value of foo
the inner text of the span will be changed. We can achieve the same effect with more complex AngularJS expressions, like:
<span ng-bind="foo + " " + bar | uppercase"></span>
In the example above the value of the span will be the concatenated uppercased value of foo
and bar
. What happens behind the scenes?
Each $scope
has method called $watch
. When the AngularJS compiler finds the directive ng-bind
it creates a new watcher of the expression foo + ' ' + bar | uppercase
, i.e. $scope.$watch("foo + ' ' + bar | uppercase", function () { /* body */ });
. The callback will be triggered each time the value of the expression is changed. In the current case the callback will update the value of the span.
Here are the first couple of lines of the implementation of $watch
:
$watch: function(watchExp, listener, objectEquality) {
var scope = this,
get = compileToFn(watchExp, 'watch'),
array = scope.$$watchers,
watcher = {
fn: listener,
last: initWatchVal,
get: get,
exp: watchExp,
eq: !!objectEquality
};
//...
We can think of the watcher
object as a command. The expression of the command is being evaluated on each "$digest"
loop. Once AngularJS detects change in the expression, it invokes the listener
function. The watcher
command encapsulates all the information required for watching given expression and delegates the execution of the command to the listener
(the actual receiver). We can think of the $scope
as the command’s Client
and the $digest
loop as the command’s Invoker
.
Controllers
Page Controller
An object that handles a request for a specific page or action on a Web site. Martin Fowler
According to 4 the page controller:
Page Controller pattern accept input from the page request, invoke the requested actions on the model, and determine the correct view to use for the resulting page. Separate the dispatching logic from any view-related code
Since there is a lot of duplicate behavior between the different pages (like rendering footers, headers, taking care of the user’s session, etc.) page controllers can form a hierarchy. In AngularJS we have controllers, which are with more limited scope of responsibilities. They don’t accept user requests, since this is responsibility of the $route
or $state
services and the page rendering is responsibility of the directives ng-view
/ui-view
.
Similarly to the page controllers, AngularJS controllers handle user interactions, provide and update the models. The model is exposed to the view when it is being attached to the scope, all methods invoked by the view, in result of user actions, are ones, which are already attached to the scope. Another similarity between the page controllers and the AngularJS controllers is the hierarchy, which they form. It corresponds to the scope hierarchy. That way common actions can be isolated to the base controllers.
The controllers in AngularJS are quite similar to the code-behind in ASP.NET WebForms, since their responsibilities almost overlap. Here is an example hierarchy between few controllers:
<!doctype html>
<html>
<head>
</head>
<body ng-controller="MainCtrl">
<div ng-controller="ChildCtrl">
<span>{{user.name}}</span>
<button ng-click="click()">Click</button>
</div>
</body>
</html>
function MainCtrl($scope, $location, User) {
if (!User.isAuthenticated()) {
$location.path('/unauthenticated');
}
}
function ChildCtrl($scope, User) {
$scope.click = function () {
alert('You clicked me!');
};
$scope.user = User.get(0);
}
This example aims to illustrate the most trivial way to reuse logic by using a base controller, anyway in production applications I don’t recommend you to put your authorization logic in the controllers. The access to the different routes could be determined on a higher level of abstraction.
The ChildCtrl
is responsible for handling actions such as clicking the button with label "Click"
and exposing the model to the view, by attaching it to the scope.
Others
Module Pattern
This is actually not a design pattern from Gang of Four, neither one from P of EAA. This is a traditional JavaScript pattern, the main goal of which is to provide encapsulation and privacy.
Using the module pattern you can achieve privacy based on the JavaScript’s functional lexical scope. Each module may have zero or more private members, which are hidden in the local scope of a function. This function returns an object, which exports the public API of the given module:
var Page = (function () {
var title;
function setTitle(t) {
document.title = t;
title = t;
}
function getTitle() {
return title;
}
return {
setTitle: setTitle,
getTitle: getTitle
};
}());
In the example above we have IIFE (Immediately-Invoked Function Expression), which after being called returns an object, with two methods (setTitle
and getTitle
). The returned object is being assigned to the Page
variable.
In this case the user of the Page
object doesn’t have direct access to the title
variable, which is defined inside the local scope of the IIFE.
The module pattern is very useful when defining services in AngularJS. Using this pattern we can simulate (and actually achieve) privacy:
app.factory('foo', function () {
function privateMember() {
//body...
}
function publicMember() {
//body...
privateMember();
//body
}
return {
publicMember: publicMember
};
});
Once we want to inject foo
inside any other component we won’t be able to use the private methods, but only the public ones. This solution is extremely powerful especially when one is building a reusable library.
Data Mapper
A Data Mapper is a Data Access Layer that performs bidirectional transfer of data between a persistent data store (often a relational database) and an in memory data representation (the domain layer). The goal of the pattern is to keep the in memory representation and the persistent data store independent of each other and the data mapper itself.
As the description above states, the data mapper is used for bidirectional transfer of data between a persistent data store and an in memory data representation. Usually our AngularJS application communicates with API server, which is written in any server-side language (Ruby, PHP, Java, JavaScript, etc.).
Usually, if we have RESTful API $resource
will help us communicate with the server in Active Record like fashion. Although, in some applications the data entities returned by the server are not in the most appropriate format, which we want to use in the front-end.
For instance, lets assume we have application in which each user has:
- name
- address
- list of friends
And our API has the methods:
GET /user/:id
- returns the user’s name and the address of given userGET /friends/:id
- returns the list of friends of given user
Possible solution is to have two different services, one for the first method and one for the second one. Probably more useful solution would be if we have a single service called User
, which loads the user’s friends when we request the user:
app.factory('User', function ($q) {
function User(name, address, friends) {
this.name = name;
this.address = address;
this.friends = friends;
}
User.get = function (params) {
var user = $http.get('/user/' + params.id),
friends = $http.get('/friends/' + params.id);
$q.all([user, friends])
.then(function (user, friends) {
return new User(user.name, user.address, friends);
});
};
return User;
});
This way we create pseudo-data mapper, which adapts our API according to the SPA requirements.
We can use the User
service by:
function MainCtrl($scope, User) {
User.get({ id: 1 })
.then(function (data) {
$scope.user = data;
});
}
And the following partial:
<div>
<div>
Name: {{user.name}}
</div>
<div>
Address: {{user.address}}
</div>
<div>
Friends with ids:
<ul>
<li ng-repeat="friend in user.friends">{{friend}}</li>
</ul>
</div>
</div>