Angualr Concepts
Angualr Concepts
This tutorial serves as an ultimate resource point to begin learning AngularJS, the
concepts and APIs behind it and help you deliver fantastic web applications the
modern way.
If you're used to building static websites, you're probably familiar with manually
creating HTML piece by piece, integrating your "data" and printing the same
HTML over and over again. This could be columns for a grid, a navigation
structure, a list of links or images and so on. In this instance, you're used to the
pain of manually updating HTML for a template when one little thing changes,
you've got to update all further uses of the template to keep things consistent.
You've also got to stamp the same chunk of HTML for each navigation item, for
example.
The difference between MVC and MVVM, is that MVVM is specifically targeted
at user interface development. The View consists of the presentation layer, the
ViewModel contains the presentation logic and the Model contains our business
logic and data. MVVM was designed to make two-way data binding easier,
and frameworks such as AngularJS thrive from it. We'll be focusing on an MVVM
path as Angular has been leaning more towards this design in recent years.
Angular use plain old JavaScript Objects for synchronising Model and View
data-bindings, which makes updating both a breeze. Angular parses back to
JSON and communicates best with a REST endpoint. This approach makes
building front-end applications seamless as all the application state is held on
the browser, not delivered in pieces from a server and state becomes lost.
The way we bind these values is through Angular expressions, which take shape
as handlebar templates. We can also bind Models using an attribute called ng-
model. Angular uses custom attributes for various APIs that feed back into the
Angular core, we'll learn more about these ng-* prefixed attributes as we
continue.
2.4 Single Page Applications (SPAs), managing state and Ajax (HTTP)
In a Single Page Application (SPA), either all necessary code (HTML, CSS and
JavaScript) is retrieved with a single page load, or the appropriate resources are
dynamically loaded and added to the page as necessary, usually in response to
user actions. The page does not reload at any point in the process, nor does
control transfer to another page, although modern web technologies (such as
those included in HTML5) can provide the perception and navigability of
separate logical pages in the application. Interaction with the single page
application often involves dynamic communication with the web server behind
the scenes.
In "older" applications where the server typically managed state, there were
disconnections between data the user was seeing and data synchronised on
the server. There was also a severe lack of application state in a model, as all
data was merged in with HTML templates and very far from dynamic. Servers
would render a static template, the user would fill in some information and the
browser would post it back, a full page refresh would occur and the backend
would update. Any state that wasn't captured was lost, and the browser then
has to download all the assets again as the page refreshes.
Times have changed, and the browser is maintaining the state of the
application, complex logic and client-side MVW frameworks have fast and
furiously increased in popularity. It turned out these server-side practices for
managing data fit really well in the browser. AngularJS (as well as other
JavaScript frameworks) manage state entirely in the browser and communicate
changes when we want them to via Ajax (HTTP) using GET, POST, PUT and DELETE
methods, typically talking to a REST endpoint backend. The beauty of this is REST
endpoints can be front-end independent, and the front-end can be back-end
independent. We can serve the same endpoints to a mobile application which
may have a different front-end to a web application. Being back-end
independent gives us massive flexibility as to the backend, we only care about
the JSON data coming back, be it from Java, .NET, PHP or any other server-side
language.
3 Modules
Every application in Angular is created using modules. A module can have
dependencies of other modules, or be a single module all by itself. These
modules act as containers for different sections of an application, making code
more reusable and testable. To create a module, we hit the
global angular Object, the framework's namespace, and use
the module method.
3.1 Setters
Our application will always have a single app module as we can import other
modules, so we'll call the main module app when we set it (create it).
angular.module('app', []);
You'll notice the [] as the second argument to the module method, this Array
contains (or will contain) all other module dependencies we wish to include in
this module. Modules can have other modules as dependencies, which have
modules that pull in other dependencies. For now, we'll leave it empty.
3.2 Getters
From here we could reference the app variable to add methods to build the rest
of our app, however we should stick to the previously seen chaining methods.
To declare where our application sits in the DOM, typically the <html> element,
we need to bind an ng-app attribute with the value of our module. This tells
Angular where to bootstrap our application.
<html ng-app="app">
<head></head>
<body></body>
</html>
4 Understanding $scope
One of the most common concepts you'll come across in any programming
language is scope. For instance block scope and function scope. Angular has a
concept of scope which sits as one of the main Objects that powers the two
way data-binding cycles to maintain application state. $scope is a very clever
Object that not only sits in our JavaScript to access data and values, it
represents those values out in the DOM once Angular renders our app.
We only use $scope inside Controllers, where we bind data from the Controller
to the View.
To render it out in the DOM, we need to connect a Controller to some HTML and
tell Angular where to bind the value.
<div ng-controller="AppCtrl">
{{ someValue }}
</div>
We can bind any values to $scope that exist as types in JavaScript. This is how
we transfer data from a service which talks to the server and pass it on to our
View, the presentational layer.
The more Controllers and data-bindings in Angular we create, the more scopes
are created. Understanding the $scope hierarchy is also worth noting, this is
where the $rootScope comes in.
4.1 $rootScope
The $rootScope isn't all that different from $scope, except it is the very top
level $scope Object from which all further scopes are created. Once Angular
starts rendering your application, it creates a $rootScope Object, and all further
bindings and logic in your application create new $scope Objects which then
all become children of the $rootScope.
Generally, we don't touch the $rootScope that often, but we can keep it in
mind for communicating between scopes with data.
5 Controllers
Before using a Controller, we need to know its purpose. An Angular Controller
allows us to interact with a View and Model, it's the place where presentational
logic can take place to keep the UI bindings in sync with the Model. A
Controller's purpose is to drive Model and View changes, in Angular it's a
meeting place between our business logic and our presentational logic.
});
angular
.module('app', [])
.controller('MainCtrl', MainCtrl);
This makes things look a lot clearer in terms of visibility and readability, it's
important not to get hooked up in Angular's syntax and just focus on writing
JavaScript. We can then pass Angular the pieces we need, just like above.
You'll see I've named a function MainCtrl, this also helps us with stack traces
when debugging, something an anonymous function can't provide us unless we
name them - but it's often less clear and easily forgotten.
All further examples will assume a module has been created, so we'll use the
getter syntax from now on when showing examples.
All the Controller does is talk to a Service, and pass the data in either the same
or a different format across to our View, using the $scope Object. Once a View
is updated, the Controller logic is also updated and can be passed back to the
server using a Service. Before we dive into Services, we'll create some Objects in
the Controller and bind them to the $scope to get a feel how Controllers really
work. We wouldn't actually declare business logic or or data in a Controller, but
for brevity we'll add it below, we'll later fetch data from a Service.
function MainCtrl ($scope) {
$scope.items = [{
name: 'Scuba Diving Kit',
id: 7297510
},{
name: 'Snorkel',
id: 0278916
},{
name: 'Wet Suit',
id: 2389017
},{
name: 'Beach Towel',
id: 1000983
}];
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
Using the $scope Object we bind an Array. This Array is then available in the
DOM, where we can pass it off to one of Angular's built-in Directives, ng-
repeat to loop over the data and create DOM based on the template and
data.
<div ng-controller="MainCtrl">
<ul>
<li ng-repeat="item in items">
{{ item.name }}
</li>
</ul>
</div>
Controllers are classe-like and Angular developers feel using the $scope Object
didn't treat them very class-like. They advocated the use of the this keyword
instead of $scope. The Angular team introduced this as part of the
new controllerAs syntax, of which a Controller becomes instantiated as an
instance under a variable - much like you could using the new keyword against
a variable to create a new Object.
The first change for controllerAs is dropping $scope references for data
bound inside the Controller and using this instead.
function MainCtrl () {
this.items = [{
name: 'Scuba Diving Kit',
id: 7297510
},{
name: 'Snorkel',
id: 0278916
},{
name: 'Wet Suit',
id: 2389017
},{
name: 'Beach Towel',
id: 1000983
}];
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
The next change is adding the as part to where we instantiate the Controller in
the DOM, let's use MainCtrl as main to create a main variable.
This means any data we reference inside the Controller sits under
the main variable, so items in the previous example becomes main.items.
<div ng-controller="MainCtrl as main">
<ul>
<li ng-repeat="item in main.items">
{{ item.name }}
</li>
</ul>
</div>
Angular gives us a service method for creating a new Object with, this could
talk to a backend or provide utilities to handle business logic. A service is just
a constructorObject that gets called with the new keyword, which means we'll
be using the thiskeyword to bind our logic to the Service. The service creates
a singleton Object created by a service factory.
function UserService () {
this.sayHello = function (name) {
return 'Hello there ' + name;
};
}
angular
.module('app')
.service('UserService', UserService);
angular
.module('app')
.controller('MainCtrl', MainCtrl);
A Service means we can't run any code before it as all methods are the Object
instantiated. Things are different when it comes to a Factory.
6.2 Factory method
We'll recreate the UserService above using the factory method for
comparison.
function UserService () {
var UserService = {};
function greeting (name) {
return 'Hello there ' + name;
}
UserService.sayHello = function (name) {
return greeting(name);
};
return UserService;
}
angular
.module('app')
.factory('UserService', UserService);
I've split the greeting function out to show how we can emulate private scope
through closures. We could have done something similar inside
the service method constructor, but this shows what gets returned and what
stays inside the Service scope. We could create private functions such as
helpers that remain in the scope after the function has returned, but are still
available for use by the public methods. We use Services created by
the factory method in the exact same way inside a Controller.
function MainCtrl (UserService) {
this.sayHello = function (name) {
UserService.sayHello(name);
};
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
Services are generally used for non-presentational logic, which we call the
business logic layer. This tends to be things like communicating with a backend
via REST endpoints over Ajax (HTTP).
Further Reading:
7.1 Expressions
Using the above operators, we can create powerful templates that respond to
the data presented, which Angular will re-evaluate for us each $digest cycle.
This means we can have rich user interaction and feedback, such as updating
values as they change on a page for a user without requerying or reloads.
angular
.module('app')
.controller('MainCtrl', MainCtrl);
We can then retrieve the length of the Array by declaring an expression that
makes use of the length property of an Array. This might indicate to the user
how many items are in stock.
<div ng-controller="MainCtrl as main">
{{ main.items.length }} in stock
</div>
Angular will render out the length property, so we'll see "4 items in stock" in the
DOM.
Angular gives us a whole heap of built in ng-* prefixed Directives, let's start
with ng-click and bind a function to a new piece of HTML template. We're not
limited to using an expression once, $scope is just an Object so we can reuse it
as much as we like. Here I'm going to iterate over the items Array and show
how many items are in stock.
<div ng-controller="MainCtrl as main">
<div>
{{ main.items.length }} items in stock
</div>
<ul>
<li ng-repeat="item in main.items" ng-click="main.removeFromStock(item, $index)">
{{ item.name }}
</li>
</ul>
</div>
If you take a close look at the above, I'm binding an ng-click attribute with a
function inside called main.removeFromStock(). I'm passing in the item, which
is the current iterated Object from item in main.items, and passing
in $index. The $indexproperty is extremely handy for removing items from an
Array without us manually calculating the index of the element.
Now the function is setup, we can add it to the Controller, I'll keep
the items Array brief for visibility. I pass in the $index and the item, and on
the removeFromStockmethod
function MainCtrl () {
this.removeFromStock = function (item, index) {
this.items.splice(index, 1);
};
this.items = [...];
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
As we create methods, the this value may change depending on how we use
it due to the function's execution context. With the Controller being a
glorified ViewModel, I usually create a reference to the Controller using a
variable var vm = this;, with "vm" standing for "ViewModel". This way we don't
lose any lexical this references and avoid using Function.prototype.bind to
keep changing context, or Angular's own angular.bind.
angular
.module('app')
.controller('MainCtrl', MainCtrl);
These methods help us build up the presentational logic that handles our
application's UI layer. We are missing a step, however, and that step is updated
the Model. Typically before we remove an item from the vm.items Array, we
would make a DELETE request to the backend and then on successful callback
remove it from the Array. This way we only update the DOM upon success so the
user doesn't think it's succeeded. The above gives a simple glimpse at how you
can create templates with Angular, with plain HTML and sprinkles of Angular
syntax.
8 Directives (Core)
There are two types of Directives, the ones that power Angular's bindings, and
the ones we can create ourselves. A Directive can be anything, it can either
provide powerful logic to an existing specific element, or be an element itself
and provide an injected template with powerful logic inside. The idea behind
them is around extending HTML's capabilities, as if it were made for building
compelling data-driven web applications.
Let's look over some of the most powerful Angular Directives built into the core
and what they do, then we'll move onto creating our own custom Directives
and the concepts behind them.
8.1 ng-repeat
We've already looked at the ng-repeat, so we'll just show a simple example to
recap.
<ul>
<li ng-repeat="item in main.items">
{{ item }}
</li>
</ul>
8.2 ng-model
When we bind a Model onto our HTML, we're tying Model data to elements. To
initialise a new Model, if it doesn't exist yet, or bind an existing Model we simply
bind to ng-model.
<input type="text" ng-model="main.message">
<p>Message: {{ main.message }}</p>
If the $scope property main.message holds a value, the input's value attribute
and property are both set to that value. If main.message doesn't exist inside
your $scope, Angular will just initialise it. We might pass these values to other
Angular Directives, such as ng-click.
8.3 ng-click
The beauty of ng-click is that we don't have to manually bind event listeners to
multiple elements, Angular will evaluate the expression(s) inside the ng-
click for us and bind the relevant listeners. This makes our development much
faster, imagine binding event listeners and callbacks to DOM elements
manually, adding and removing them whilst the DOM is destroyed and
recreated on the fly. Unlike the old onclick=" attribute in HTML, Angular
directives such as ng-click are scoped, and therefore are not global (which
functions would need to be to be available for onclick).
<input type="text" ng-model="main.message">
<a href=" ng-click="main.showMessage(main.message);">Show my message</a>
8.4 ng-href/ng-src
8.5 ng-class
It's often clearer to see what's happening inside the ng-class Directive if we
indent it like a proper Object, like above. You can see how Angular will keep
evaluating main.response to see if the status returned is error or success, the
class it will add it the Object's property name, in these cases warning and ok.
If main.response == 'error' evalutes to true, Angular will add
a warning class.
8.6 ng-show/ng-hide
Using ng-show and ng-hide are often quite common to use within Angular, it's a
fantastic way to show and hide data based on a $scope property's value.
To show an element, we might use an ng-click to bind a value to, and toggle
it to show and hide the element.
<a href=" ng-click="showMenu = !showMenu">Toggle menu!</a>
<ul ng-show="showMenu">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
8.7 ng-if
8.8 ng-switch
Think a typical switch case, but in the DOM or better yet, an ng-if on steroids.
Provide Angular multiple chunks of HTML for it to swap in and out based on a
single $scope value. This allows us to provide different HTML for different users for
example,
<div ng-switch on="main.user.access">
<div ng-switch-when="admin">
<!-- code for admins -->
</div>
<div ng-switch-when="user">
<!-- code for users -->
</div>
<div ng-switch-when="author">
<!-- code for authors -->
</div>
</div>
8.9 ng-bind
We're using to rendering values in the DOM using {{ value }} bindings, but
there is another alternative called ng-bind which you can see the difference in
syntax here.
<p>{{ main.name }}</p>
<p ng-bind="main.name"></p>
We would use ng-bind when we don't want to see any flicker whilst Angular is
loading and parsing data. Angular automatically shields any content that's
dynamically created with Directives or inside a dynamic view (ng-view). If your
bindings are outside of these, we might see {{ and }} text in the document,
which is bad. To get around this, we use ng-bind which is invisible as Angular
resolves the value once it's ready and removes the need for {{ }} bindings.
8.10 ng-view
In single page applications (SPAs), the concept is just having one page that
dynamically updates. Angular delivers this to use using ng-view attribute on an
empty <div></div> (or other element), which sits as a container for all
dynamically injected HTML views, which are fetched via XMLHttpRequest.
Angular's ideas around Directives are "extending HTML", and it does it brilliantly,
creating dynamic HTML and responsive updates when Model data changes.
There might come a time though where we want to create our own HTML
extensions, our own Directives. Angular provides us the same API it uses for
creating its own Directives.
9 Directives (Custom)
Custom Directives are possibly one of the hardest concepts and APIs to grasp in
Angular as they don't adhere to any software engineering concept. They are,
however, Angular's way to start using the rapidly approaching (but future) Web
Components standard, which introduces Custom Elements, Shadow DOM,
Templating and HTML imports. Angular Directives give us all of these and a
seamless API for managing them. The easiest way to learn Directives is to break
them down into their intended layers and the meaning behind them.
They're not in full flux or supported by all browser vendors, so Angular created
this custom implementation.
Custom Elements in Angular are less strict than the Web Components
specification as Angular supports older versions of Internet Explorer which
Custom Elements don't play as nicely with without manually creating references
for them.
Angular provides us four ways to use its Directives, through Custom Elements,
Custom Attributes, class names and comments. The last two I generally avoid as
it can become confusing which is a comment and which is a class name -
comments also have IE issues. The safest route for cross-browser compatibility is
using a Custom Attribute. Let's explore the four options and their syntax in the
following order: Element, Attribute, Class, Comment.
<my-element></my-element>
<div my-element></div>
<div class="my-element"></div>
<!-- directive: my-element -->
Angular Directives provide us with a restrict property, so we can tie down the
usage to a particular implementation. By default, all Directives are set
to 'EA' which means Element, Attribute. The other options are C for class
name and M for comment.
Shadow DOM allows DOM to be nested inside DOM, which typically means
there is one main document and small pockets of other documents nested
inside it. Shadow DOM offers both HTML, CSS and JavaScript scoping and
encapsulation, which requires browser support itself. Instead of creating Shadow
DOM, Angular injects DOM normally as if it were Shadow DOM to emulate it. In
other words, it renders hidden parts of your template for you and they get
injected where you instruct.
Shadow DOM allows us to specify the bare bones of the HTML, and also content
to be "imported" into the Shadow DOM. This means we could put text inside a
Custom Element:
<my-element>
Hi there!
</my-element>
And the Hi there! text could be made available in the Shadow DOM. Angular
has an option called transclude, which allows us to pull through existing
content into Directive templates before being compiled and sent back to the
DOM. It acts in a similar fashion to Shadow DOM, but doesn't provide the
powerful scoping.
Interestingly there are three different ways to use templates inside a Directive.
Two look identical and the other uses a String to hold the template. Let's
check out the template and templateUrl properties to understand what they
give us.
Template does exactly what it says, it declares the template we want to use. This
is the String formatted template that Angular then compiles into live DOM.
An example:
{
template: '<div>' +
'<ul>' +
'<li ng-repeat="item in vm.items">' +
'{{ item }}' +
'</li>' +
'</ul>' +
'</div>'
}
If I'm using this setup (which can be nice as we can write JavaScript logic in
between the Strings), I use [].join('') to make the template clearer to read
and easier to indent, comma separation is also much nicer to look at.
{
template: [
'<div>',
'<ul>',
'<li ng-repeat="item in vm.items">',
'{{ item }}',
'</li>',
'</ul>',
'</div>'
].join('')
}
If we specified:
{
templateUrl: 'items.html'
}
Angular would first scan the DOM for a <script> element with the id that
matches, if it doesn't exist it'll attempt a HTTP GET.
First, we specify the type as text/ng-template, changing the MIME type for
the JavaScript engine, which makes the compiler ignore it to prevent a tonne of
JavaScript errors thrown from inserting HTML. The id is the important piece,
which pretends to be the *.html file, meaning we can use an
inline <script> tag instead of making a GET request for each template file - a
performance benefit.
The performance benefit also lives in the template property, whereby the
template is stored as a String, this also means no GET request. Once a
Directive's template has been loaded, Angular stores it internally so the
template can be used by ng-includeand ng-view as well.
If you're not using the inline <script> method, Angular won't find it and fire
off GETrequest to fetch it, which it then imports the HTML and compiles into the
Directive as live DOM.
All templates that are loaded by Angular, however, are loaded straight into
the $templateCache and fetched from there for the lifetime of the application.
Now we've learned the concepts behind custom Directives, we can begin to
create our own. Let's checkout more of the API, which begins with
a return statement which returns an Object. This is all we need to get a custom
Directive up and running.
function someDirective () {
return {
};
}
angular
.module('app')
.controller('someDirective', someDirective);
Let's look at some of the most commonly used Object properties for Directives.
We've covered the restrict property, next we'll
add replace, controllerAs, controller, link and template, this will give us
an idea of how things hang together.
function someDirective () {
return {
restrict: 'EA',
replace: true,
scope: true,
controllerAs: 'something',
controller: function () {
},
link: function ($scope, $element, $attrs) {
},
template: [
'<div class="some-directive">',
'My directive!',
'</div>'
].join('')
};
}
angular
.module('app')
.controller('someDirective', someDirective);
9.4.1 restrict
We've already covered the restrict property, but to recap - it allows you to
restrict the Directive's usage for developers. If the Directive needs to be as an
attribute, often when we want to bolt into an existing element and not have
root template control, we can restrict to 'A'. To restrict to just an element we
use 'E', for comments we use 'M' and for class names we use 'C'.
9.4.2 replace
9.4.3 scope
Allows us to inherit $scope from the current or parent context where the
Directive is nested, or we can create isolate scope and pass
specific $scope values in, typically done via custom attributes.
9.4.4 controllerAs
We've experimented with the controllerAs syntax, this is just how we declare
the Controller's assignment name within a Directive. Assuming we
set controllerAs: 'something', any Controller references inside our
template would use something.myMethod() for anything declared.
9.4.5 controller
9.4.6 link
The link function is called after the element is compiled and injected into the
DOM, which means it's the perfect place to do "post-compile" logic, as well as
non-Angular logic.
Let’s run through a quick example of how to create our own Directive, we’ll
create a simple Directive that allows us to inject an compose email component,
which has To, Subject and Message fields.
},
template: [
'<div class="compose-email">',
'<input type="text" placeholder="To..." ng-model="compose.to">',
'<input type="text" placeholder="Subject..." ng-model="compose.subject">',
'<textarea placeholder="Message..." ng-model="compose.message"></textarea>',
'</div>'
].join('')
};
}
angular
.module('app')
.controller('composeEmail', composeEmail);
We can now use the composeEmail Directive in multiple places without having
to create the same markup each time, Angular will cleverly inject it where we
declare it. It’s important to remember that Angular will parse the name of your
Directive, and anywhere that an uppercase character exists Angular will
hyphenate it. This means that composeEmail will become <compose-
email></compose-email> when used as a custom element in our HTML
templates.
Further Reading:
10 Filters (Core)
Angular filters are a way of processing data and returning a specific set of it,
against some kind of logic. This could be absolutely anything, from processing a
date stamp into a formatted time, to a list of names that begin with a specific
letter. Let's look at some of the most common core Filters.
Filters can be used in two different ways, either in the DOM via a
pipe | character inside our expressions, which Angular parses out for us. The
second way is using the $filter Service, which we can dependency inject
and use within our JavaScript instead of our HTML.
JavaScript syntax.
$filter('filter')(array, expression, comparator);
Usually, we'll use the HTML style expression as it's easy and concise. Let's look at a
few of Angular's built-in filters.
Working with dates can often be a time consuming and logic-heavy process,
with Angular things get really easy thanks to the built-in date filter. Let's use a
milliseconds time example (something like 1408466687250) and bind it
using $scope.timeNow = new Date().getTime();.
<p>
Today's date: {{ timeNow | date:'dd-MM-yyyy' }}
</p>
The above would render out as today's date, in dd-MM-yyyy format, for
instance 19-08-2014.
The built-in JSON filter converts a JavaScript Object to a JSON String, this is really
helpful for outputting Model values in the DOM during development to see
values update. It's also a great help when debugging as all values will be
updated. To get the best pretty printed JSON Object, wrap any JSON filters
in <pre> tags.
<pre>
{{ myObject | json }}
</pre>
The previous Filter examples were rather basic, we pass one value in, and get
one out. How does Angular handle sets of data?
Using limitTo and orderBy are two fantastic Filters that have many common
use cases in our applications.
Using limitTo, we can put a cap on the amounts of data that's presented to
the View at a given time, this would generally be used inside an ng-repeat -
Filters work here too!
<ul>
<li ng-repeat="user in users | limitTo:10">
{{ user.name }}
</li>
</ul>
This would render out a maximum of 10 users, note how we use | limitTo:
10alongside the ng-repeat.
Using orderBy allows us to specify which order our Array will render based on a
property inside one of the Objects. For instance, if we had a user Object:
{
name: 'Todd Motto',
}
We might want to render the list alphabetically, this is where the Filter comes in
handy.
<ul>
<li ng-repeat=" user in users | orderBy:'name' ">
{{ user.name }}
</li>
</ul>
These are just some of the basics when using Angular's internal Filters, the real
power comes from using their API to create our own.
11 Filters (Custom)
We've all written Filters of some kind before, typically done inside Objects and
Arrays. To make them reusable and available as part of Angular, Angular
provides us with its API. This means we get two way data-binding and all the
power behind our Filters without us doing a great deal. Any Filters we plug into
Angular are automatically called during the $digest cycle.
With the Angular's built-in Filters, we covered a single value Filter, such as a date
or Object, and Filters that handled data sets such as Arrays of Objects. Let's look
at the differences in API on how to create our own custom Filters.
Single value filters accept a single input, and returns a "filtered" output. We've
seen an example previously using the date filter, let's look at how to create our
own Filter using Angular's fantastic API, the .filter() method. Any Filters we
create using .filter() are globally available in any scope, which means we
can reuse them throughout the entire app.
Here's a look at an empty Filter setup which we can then build from.
function myFilter () {
return function () {
// return filtered output
};
}
angular
.module('app')
.filter('myFilter', myFilter);
The Filter API uses a function closure which gets returned and called each time
Angular needs to run the Filter. Angular automatically passes the arguments we
need into Filters, which makes our life a lot easier when it comes to using them -
it's the exact same as Angular's built-in Filters.
Let's pass in an argument and create a basic Filter. Angular already has a "make
lowercase" Filter, but it makes for a really simple example so we'll create our own
too but call it something different.
function toLowercase () {
return function (item) {
return item.toLowerCase();
};
}
angular
.module('app')
.filter('toLowercase', toLowercase);
You see I pass in item as a local variable to the Filter, this isn't dependency
injected, it's just the data Angular passes to us. Inside this Filter closure, I
then return item.toLowerCase(); which Angular sets as the updated value
instead of the initial value.
We often need to iterate over a set of data and return a Filtered set of that
data, whatever it may be. Let's take a real world example, our application
might want to filter names in an address book by the first letter of their name.
Let's create a Filter that looks for users whose name starts with 'a'.
function namesStartingWithA () {
return function (items) {
return items.filter(function (item) {
return /$a/i.test(item.name);
});
};
}
angular
.module('app')
.filter('namesStartingWithA', namesStartingWithA);
We can also pass items into Filters, which become available as further
arguments in the closure. This would allow us to pass a specific letter into a Filter
function to make it dynamic. The syntax for passing in arguments is separated
with a colon :.
<ul>
<li ng-repeat="item in items | namesStartingWithA:something">
{{ item }}
</li>
</ul>
We can create Filters outside of the .filter() method, and just pass in a
function inside a Controller that acts as a Filter. This function inside
the $scope acts as the closure we're used to.
};
}
angular
.module('app')
.controller('SomeCtrl', SomeCtrl);
Remember, these Filters we create using functions are scoped to the Controller
they were created in. Typically, we'd create all Filters using
the .filter() method, but it's great to know what other methods we can use if
the use case is there.
Further Reading:
Angular used to package the router inside the core download, but realised not
everybody needed it or used a third party router, and as such made it an
optional drop-in module.
Now the ngRoute module is available in our application, we can configure the
routes by injecting $routeProvider and setting it up inside
Angular's .config() method.
angular
.module('app')
.config(router);
Let's populate our first route to see how easy it is to configure. The first choice we
need to make is typically the template we want to use with a particular view,
let's assume we want the inbox.html template, whatever it may look like. We
can use the standard template property we get with Directives, but this would
be insane for full pages of view templates, so we'll use
the templateUrl property and decouple our template from router config.
$routeProvider
.when('/inbox', {
templateUrl: 'views/inbox.html'
})
.otherwise({
redirectTo: '/inbox'
});
This is great for setting up a simple and single route, but what about if we were
to click and read an email in our pseudo email app? We need dynamic routing
as all our emails and associated IDs will be different, and thankfully Angular's
router provides us this out of the box. Dynamic routing is where we can pass in a
particular property that is dynamic, such as the aforementioned email ID. We do
this by using a colon :before the dynamic groups name. We can set this up to
use '/inbox/email/:id' as the route, which might be something
like /inbox/email/173921938 in our app.
Putting this together, we'll end up with something like the following.
function router ($routeProvider) {
$routeProvider
.when('/inbox', {
templateUrl: 'views/inbox.html',
controller: 'InboxCtrl',
controllerAs: 'inbox'
})
.when('/inbox/email/:id', {
templateUrl: 'views/email.html',
controller: 'EmailCtrl',
controllerAs: 'email'
})
.otherwise({
redirectTo: '/inbox'
});
});
angular
.module('app')
.config(router);
This is the basis of what you need to get routes for a single page application up
and running with Angular, but there is one more thing, ng-view. We've touched
on it before, but now we need to connect the router with the ng-
view container to let Angular know where to inject our View templates.
The ng-view attribute sits in the "middle" of our app, and any changes to the
window's location, Angular will reference the router to see if it needs to inject a
new template. This is really all there is to getting Ajax views working.
12.1 $routeParams
The access the $routeParams Object, simply inject it like any other
dependency and fire it off to the backend, a simple example inside a Controller
to show how to pass $routeParams around using the properties - with a
dummy EmailService.
function EmailCtrl ($routeParams, EmailService) {
// $routeParams { id: 20999851 }
EmailService
.get($routeParams.id) // pass the Object in
.success(function (response) {})
.error(function (reason) {});
}
angular
.module('app')
.('EmailCtrl', EmailCtrl);
Further Reading:
13 Form Validation
Created to supersede HTML5 validation, Angular's built-in validation is superb for
creating forms that respond to user input much like Model changes do. It does a
few powerful things, from checking Model changes against binding rules, to
manipulating the DOM to provide user feedback.
To get our form validation setup, we need a form with a name attribute to
namespace the form scope.
<form name="myForm"></form>
Angular will recognise this as soon as the page has rendered and check things
as the user fills them out, constantly watching the state and any rules we give
inputs, such as required attributes to ensure users fill them in.
In HTML5 we've seen the addition of the pattern attribute, which allows the
browser to validate against a custom Regular Expression (RegExp), this is just one
example of how Angular ties these new features into its framework. Angular also
rebuilt the required checking and tied it into the framework under a Directive
called ng-required which is also continuously being evaluated against Model
changes. Let's look at how some of these features are expressed by Angular
form states.
13.2 $pristine
Once your page has rendered, Angular will declare your form $pristine. This
means that the user hasn't touched it yet. This can be really useful for making
sure we don't display any validation messages before the user has even typed
anything. Angular will add an ng-pristine class to elements that are pristine.
13.4 $dirty
13.5 $valid
Alongside $pristine and $dirty values, each input inside our form will also be
validated against being $valid. This means that if we have an ng-
required attribute and the user enters information, Angular will automatically
make the input $valid, adding an ng-valid class name to the element.
13.6 $invalid
The opposite of $valid comes the $invalid flag. By default our forms are
always $invalid, which means we get an ng-invalid class name added to all
inputs on page render. These states can change back and forth as the user
types (and maybe removes) information.
If the user.name.length is truthy, the button becomes enabled. This state will
be constantly watched as the user interacts with the form.
Further Reading:
If you've used jQuery's $.ajax method then you're going to be right at home.
Angular keeps $http very light, minimal and flexible which makes it a great
option for making calls to the backend.
I favour the simplicity of the shorthand methods usually so we'll use these for
demonstrations. Here's a simple HTTP GET.
$http.get('/url')
.success(function (data, status, headers, config) {
})
.error(function (data, status, headers, config) {
});
Success and error callbacks are invoked asynchronously, and Angular also
passes us data, status, headers, config back in the response. We don't
usually need all of them, so we could always aim for something a little simpler:
$http.get('/url')
.success(function (response) {
})
.error(function (reason) {
});
I like to adopt a more native Promise like pattern using the .then() method,
which Angular supports:
$http.get('/url')
.then(function (response) {
// success
}, function (reason) {
// error
});
Unlike Ajax libraries such as jQuery's $.ajax, Angular wraps its $http calls inside
a $scope.$apply(), which forces a digest cycle and our bindings will update.
14.2 $resource
We could dependency inject this simple Factory called Movies into our
Controllers to obtain the data we need.
function MovieCtrl (MovieService) {
var movies = new MovieService();
// have something update a movie
movies.update(/* some data */);
}
angular
.module('app')
.controller('MovieCtrl', MovieCtrl);
This section briefly touches on all of the important parts of AngularJS using a simple example. For a
more in-depth explanation, see the tutorial.
Concept Description
Model the data shown to the user in the view and with which the user interacts
Scope context where the model is stored so that controllers, directives and expressions can access
Data Binding sync data between the model and the view
Module a container for the different parts of an app including controllers, services, filters, directives w
Injector
index.html
This looks like normal HTML, with some new markup. In AngularJS, a file like this is called
a template. When AngularJS starts your application, it parses and processes this new markup from
the template using the compiler. The loaded, transformed and rendered DOM is then called the view.
The first kind of new markup are the directives. They apply special behavior to attributes or elements
in the HTML. In the example above we use the ng-app attribute, which is linked to a directive that
automatically initializes our application. AngularJS also defines a directive for the input element that
adds extra behavior to the element. The ng-model directive stores/updates the value of the input field
into/from a variable.
Custom directives to access the DOM: In AngularJS, the only place where an application should
access the DOM is within directives. This is important because artifacts that access the DOM are
hard to test. If you need to access the DOM directly you should write a custom directive for this.
The directives guide explains how to do this.
The second kind of new markup are the double curly braces {{ expression | filter }}: When the
compiler encounters this markup, it will replace it with the evaluated value of the markup.
An expression in a template is a JavaScript-like code snippet that allows AngularJS to read and write
variables. Note that those variables are not global variables. Just like variables in a JavaScript
function live in a scope, AngularJS provides a scope for the variables accessible to expressions. The
values that are stored in variables on the scope are referred to as the model in the rest of the
documentation. Applied to the example above, the markup directs AngularJS to "take the data we
got from the input widgets and multiply them together".
The example above also contains a filter. A filter formats the value of an expression for display to the
user. In the example above, the filter currency formats a number into an output that looks like
money.
The important thing in the example is that AngularJS provides live bindings: Whenever the input
values change, the value of the expressions are automatically recalculated and the DOM is updated
with their values. The concept behind this is two-way data binding.
Adding UI logic: Controllers
Let's add some more logic to the example that allows us to enter and calculate the costs in different
currencies and also pay the invoice.
Edit in Plunker
invoice1.jsindex.html
angular.module('invoice1', [])
.controller('InvoiceController', function InvoiceController() {
this.qty = 1;
this.cost = 2;
this.inCurr = 'EUR';
this.currencies = ['USD', 'EUR', 'CNY'];
this.usdToForeignRates = {
USD: 1,
EUR: 0.74,
CNY: 6.09
};
What changed?
First, there is a new JavaScript file that contains a controller. More accurately, the file specifies a
constructor function that will be used to create the actual controller instance. The purpose of
controllers is to expose variables and functionality to expressions and directives.
Besides the new file that contains the controller code, we also added an ng-controller directive to
the HTML. This directive tells AngularJS that the new InvoiceController is responsible for the
element with the directive and all of the element's children. The
syntax InvoiceController as invoice tells AngularJS to instantiate the controller and save it in the
variable invoice in the current scope.
We also changed all expressions in the page to read and write variables within that controller
instance by prefixing them with invoice. . The possible currencies are defined in the controller and
added to the template using ng-repeat. As the controller contains a totalfunction we are also able
to bind the result of that function to the DOM using {{ invoice.total(...) }}.
Again, this binding is live, i.e. the DOM will be automatically updated whenever the result of the
function changes. The button to pay the invoice uses the directive ngClick. This will evaluate the
corresponding expression whenever the button is clicked.
In the new JavaScript file we are also creating a module at which we register the controller. We will
talk about modules in the next section.
The following graphic shows how everything works together after we introduced the controller:
angular.module('finance2', [])
.factory('currencyConverter', function() {
var currencies = ['USD', 'EUR', 'CNY'];
var usdToForeignRates = {
USD: 1,
EUR: 0.74,
CNY: 6.09
};
var convert = function(amount, inCurr, outCurr) {
return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
};
return {
currencies: currencies,
convert: convert
};
});
What changed?
We moved the convertCurrency function and the definition of the existing currencies into the new
file finance2.js. But how does the controller get a hold of the now separated function?
This is where Dependency Injection comes into play. Dependency Injection (DI) is a software design
pattern that deals with how objects and functions get created and how they get a hold of their
dependencies. Everything within AngularJS (directives, filters, controllers, services, ...) is created
and wired using dependency injection. Within AngularJS, the DI container is called the injector.
To use DI, there needs to be a place where all the things that should work together are registered. In
AngularJS, this is the purpose of the modules. When AngularJS starts, it will use the configuration of
the module with the name defined by the ng-app directive, including the configuration of all modules
that this module depends on.
In the example above: The template contains the directive ng-app="invoice2". This tells AngularJS
to use the invoice2module as the main module for the application. The code
snippet angular.module('invoice2', ['finance2']) specifies that the invoice2 module depends on
the finance2 module. By this, AngularJS uses the InvoiceController as well as
the currencyConverter service.
Now that AngularJS knows of all the parts of the application, it needs to create them. In the previous
section we saw that controllers are created using a constructor function. For services, there are
multiple ways to specify how they are created (see the service guide). In the example above, we are
using an anonymous function as the factory function for the currencyConverter service. This
function should return the currencyConverter service instance.
Back to the initial question: How does the InvoiceController get a reference to
the currencyConverter function? In AngularJS, this is done by simply defining arguments on the
constructor function. With this, the injector is able to create the objects in the right order and pass
the previously created objects into the factories of the objects that depend on them. In our example,
the InvoiceController has an argument named currencyConverter. By this, AngularJS knows
about the dependency between the controller and the service and calls the controller with the service
instance as argument.
The last thing that changed in the example between the previous section and this section is that we
now pass an array to the module.controller function, instead of a plain function. The array first
contains the names of the service dependencies that the controller needs. The last entry in the array
is the controller constructor function. AngularJS uses this array syntax to define the dependencies so
that the DI also works after minifying the code, which will most probably rename the argument name
of the controller constructor function to something shorter like a.
invoice3.jsfinance3.jsindex.html
angular.module('invoice3', ['finance3'])
.controller('InvoiceController', ['currencyConverter', function
InvoiceController(currencyConverter) {
this.qty = 1;
this.cost = 2;
this.inCurr = 'EUR';
this.currencies = currencyConverter.currencies;
What changed? Our currencyConverter service of the finance module now uses the $http, a built-in
service provided by AngularJS for accessing a server backend. $http is a wrapper
around XMLHttpRequest and JSONP transports.
AngularJs Keywords
http://www.angularjsexample.com/p/all-angularjs-
keywords.html