Next steps (Part 7 of the AngularJS - from beginner to expert in 7 steps series)

This is the seventh and final post of the AngularJS - from beginner to expert in 7 steps series.

We started our first several entries demonstrating the core components of our application and how to set up an AngularJS app. In parts 4 and 5, we covered the internals of directives in AngularJS, and in last week’s post we discussed the power of services.

In this section, we’re covering several key topics we didn’t have a chance to introduce earlier in the series. We also offer a list of great resources and the latest tools, and we’re proud to announce the impending release of our next book.

Throughout this tutorial series, we are building an NPR audio player that will show us the current stories on the show Morning Edition and play them in our browser. To see the fully finished demo, head over here.

7. Routing

In a single-page app, navigating from one page view to another is crucial. As our app grows increasingly complex, we need a way to manage the different screens a user will visit throughout the app.

We can already support different page views by including template code in line in the main HTML, but doing that has drawbacks, among which is the fact that such in-line code will grow to be unmanageable.

Rather than including multiple templates in the view (which we could do with the ng-include directive), we can break out the view into a layout and template views and only show a particular view based upon the URL the user is currently accessing.

We’ll break these “partials” into views to be composed inside of a layout template. AngularJS allows us to do that by declaring routes on the $routeProvider, a provider of the $route service.

Using the $routeProvider, we can take advantage of the browser’s history API and enable users to bookmark and share specific pages, as it uses the current URL location in the browser.

To set up routing in our app, we need to do two things: First, we need to specify where in our layout template we want to place the content of the new page. For instance, if we want to have a header and a footer on every single page, we can set up our layout template like:

1
2
3
4
5
6
7
8
9
<header> <h1>Header</h1> </header> <div class="content"> <div ng-view></div> </div> <footer> <h5>Footer</h5> </footer>

The ng-view directive will inform our $routeProvider where to place the rendered template.

Secondly, we need to configure our routes. To configure our routes, we’ll configure the $routeProvider in our app.

There are two main methods that the $routeProvider provides for us to handle routing: when and otherwise. The when method takes two parameters, the first of which is a string to match against the $location.path(). Trailing or double slashes will still work.

The second parameter of the when method is a configuration object. This configuration object can take different keys. We briefly touch upon a few here:

controller

1
2
3
4
5
controller: 'MyController' // or controller: function($scope) { // ... }

If the controller property is set in the configuration object, the controller will be instantiated when the route loads up. This property can either be a string that matches to a registered controller on the module or it can be a function that is the controller function for the route.

template

1
template: '<div><h2>Route</h2></div>'

If we pass a template property into the configuration object, then template we indicate will be rendered in the place of the ng-view DOM element.

templateUrl

1
templateUrl: 'views/template_name.html'

If the templateUrl property is set in the configuration object, then AngularJS will attempt to fetch the view over XHR. If it finds the template and can load it, Angular will render the template’s contents in the ng-view DOM element.

It’s worth noting that the templateUrl property goes through the same process that all of the rest of AngularJS XHR requests go through; thus, this request utilizes the $templateCache. Even if your user navigates away from this page and then comes back, there will not be a subsequent request as the template will have been cached.

Making some routes

1
2
3
4
5
6
7
8
angular.module('myApp', []). config(['$routeProvider', function($routeProvider) { $routeProvider.when('/', { controller: 'HomeController', template: '<h2>We are home</h2>' }) .otherwise({redirectTo: '/'}); }]);

The $routeProvider can also handle passing parameters in the URL (for instance, like /people/42, where 42 is the :id of the person we’re looking up). Simply by prepending a string with a colon :, the $routeProvider will attempt to match the URL and place the string name as a key in the $routeParams service.

1
2
3
4
$routeProvider.when('/person/:id', { controller: 'PeopleController', template: '<div>Person show page: {{ name }}</div>' })

Inside the PeopleController, we can retrieve the :id of the person indicated in the route:

1
2
3
4
5
app.controller('PeopleController', function($scope, $routeParams) { // We now have access to the $routeParams // At the route /person/42, our $routeParams will look like: // { id: 42 } });

See it

 

Filters

In AngularJS, a filter provides a way to format data to display to the user. Angular gives us several built-in filters as well as an easy way to create our own.

Filters are invoked in the HTML with the | (pipe) character in the template binding characters {{ }}. For instance, let’s say we want to capitalize our string. We can either change all the characters in a string to be capitalized, or we can use a filter.

1
{{ name | uppercase }}

See it

{{ name | uppercase }}

We can also use filters from within JavaScript by using the $filter service. For instance, to use the uppercase JavaScript filter:

1
2
3
4
5
app.controller('DemoController', ['$scope', '$filter', function($scope, $filter) { $scope.name = $filter('lowercase')('Ari'); }]);

To pass an argument to a filter, we pass it with a colon after the filter name (for multiple arguments, we can simply append a colon after each argument). For example, the number filter will allow us to limit the number of decimal places a number can show. To pass the argument 2, we’ll append :2 to the number filter:

1
{{ 123.456789 | number:2 }}

See it

{{ 123.456789 | number:2 }}

We can use multiple filters at the same time by using two or more pipes. We’ll see such an example in a minute when we build a custom filter. Before we get to that, however, let’s look at the built-in filters that come out-of-the-box with AngularJS.

currency

The currency filter formats a number as currency. In other words, 123 as currency will look like: {{ 123 | currency }}.

Currency has the option of a currency symbol or identifier to display the currency. The default currency option is that of the current locale; however, you can pass in a currency to display.

See it

{{ 123 | currency }}
{{ 456 | currency:'USD $' }}

date

The date filter allows us to format a date based upon a requested format style. The date formatter provides us several built-in options. If no date format is passed, then it defaults to showing mediumDate (as you can see below).

Here are the built-in localizable formats:

{{ today | date:'medium' }}
{{ today | date:'short' }}
{{ today | date:'fullDate' }}
{{ today | date:'longDate' }}
{{ today | date:'mediumDate' }}
{{ today | date:'shortDate' }}
{{ today | date:'mediumTime' }}
{{ today | date:'shortTime' }}

The date formatter also enables you to customize your date format to your own liking. The format options allow you to format the date with the different components of a date. These format options can be combined and chained together to create one single date format, as well:

4 digit year
{{ today | date:'yyyy' }}
2 digit padded year
{{ today | date:'yy' }}
1 digit year
{{ today | date:'y' }}
month in year
{{ today | date:'MMMM' }}
month in year
{{ today | date:'MMM' }}
padded month in year
{{ today | date:'MM' }}
month in year
{{ today | date:'M' }}
padded day in month
{{ today | date:'dd' }}
day in month
{{ today | date:'d' }}
day in week
{{ today | date:'EEEE' }}
day in week
{{ today | date:'EEE' }}
padded hour in day
{{ today | date:'HH' }}
hour in day
{{ today | date:'H' }}
padded hour in am/pm
{{ today | date:'hh' }}
hour in am/pm
{{ today | date:'h' }}
padded minute in hour
{{ today | date:'mm' }}
minute in hour
{{ today | date:'m' }}
padded second in minute
{{ today | date:'ss' }}
second in minute
{{ today | date:'s' }}
Padded millisecond in second
{{ today | date:'.sss' }}
am/pm character
{{ today | date:'a' }}
4 digit representation of timezone offset
{{ today | date:'Z' }}

And some examples of custom date formatting

{{ today | date:'MMM d, y' }}
{{ today | date:'EEEE, d, M' }}
{{ today | date:'hh:mm:ss.sss' }}

filter

The filter filter selects a subset of items from an array of items and returns a new array. This filter is generally used as a way to filter out items for display. For instance, when using client-side searching, we can filter out items from an array immediately.

The filter method takes a string, object, or function that it will run to select or reject array elements.

If the first parameter passed in is a:
StringIt will accept elements that match against the string. If you want all that do NOT match the string, simply prepend the string with a `!`.
ObjectIt will compare objects that have a property name and that match like the simple substring match if only a string is passed in. If you want to match against all properties, you can use a `$` as the key.
FunctionIt will run the function over each element of the array, and the resulting elements will be in the new array.

You can also pass a second parameter into the filter method that will be used to determine if the expected value and the actual value should be considered a match.

If the second parameter passed in is:
trueIt will run a strict comparison match against the two (is the same as `angular.equals(expected, actual)`.
falseIt will look for a case-insensitive substring match.
FunctionIt will run the function and accept an element if the result of the function is truthy.

See it

All words that have an ‘e’ in them
{{ ['Ari', 'Lerner', 'Likes', 'To', 'Eat', 'Pizza'] | filter:'e' }}
All people that like pizza
{{ [{'name': 'Ari', 'City': 'San Francisco', 'favorite food': 'Pizza'}, {'name': 'Nate', 'City': 'San Francisco', 'favorite food': 'indian food'}] | filter:{'favorite food': 'Pizza'} }}
Filter with a function that returns true if the first letter is capitalized
{{ ['Ari', 'likes', 'to', 'travel'] | filter:isCapitalized }}

The isCapitalized function looks like:

1
2
$scope.isCapitalized = function(str) { return str[0] == str[0].toUpperCase(); }

json

The json filter will take a JSON, or JavaScript object, and turn it into a string. This transformation is very useful for debugging purposes, mainly:

See it

Json representation of the javascript object
{{ {'name': 'Ari', 'City': 'San Francisco'} | json }}

limitTo

The limitTo filter creates a new array or string that contains only the specified number of elements, either taken from the beginning or end, depending on whether the value is positive or negative.

If the limit exceeds the value of the string, then the entire array or string will be returned.

See it

Take the first 4 letters of the string
{{ "San Francisco is often cloudy" | limitTo:4 }}
Take the last 6 characters of the string
{{ "San Francisco is often cloudy" | limitTo:-6 }}
Only return the first result of the array
{{ ['a', 'b', 'c', 'd', 'e', 'f'] | limitTo:1 }}

lowercase

The lowercase filter simply lowercases the entire string.

See it

Lowercase string
{{ "San Francisco is often cloudy" | lowercase }}

number

The number filter formats a number as text. It can take a second parameter (optional) that will format the number to the specified number of decimal places (rounded).

If a non-numeric character is given, it will return an empty string.

See it

Simple number formatting
{{ 1234567890 | number }}
Format a number with only 1 decimal place
{{ 1.234567 | number:1 }}

orderBy

The orderBy filter orders the specific array using an expression.

The orderBy function can take two parameters: The first one is required, while the second is optional.

The first parameter is the predicate used to determine the order of the sorted array.

If the first parameter passed in is a(n):
functionIt will use this function as the `getter` function of the object.
stringIt will parse the string and use it as the key by which to order the elements of the array. You can pass in either `+` or `-` to force sort in ascending or descending order.
arrayIt will use these elements as predicates in the sort expression. It will use the first predicate for every element that is not strictly equal to the result of the expression.

The second parameter controls the sort order of the array (either reversed or not).

See it

Sorted people by name
{{ [{'name': 'Ari', 'status': 'awake'}, {'name': 'Nate', 'status': 'awake'}, {'name': 'Q', 'status': 'sleeping'}] | orderBy: '+name' }}
Reverse sorted people by status
{{ [{'name': 'Ari', 'status': 'awake'}, {'name': 'Nate', 'status': 'awake'}, {'name': 'Q', 'status': 'sleeping'}] | orderBy:'status':true }}

uppercase

The uppercase filter simply uppercases the entire string:

See it

Uppercase string
{{ "San Francisco is often cloudy" | uppercase }}

Making our own filter

As we saw above, it’s really easy to create our own custom filter. To create a filter, we put it under its own module. Let’s create one together: a filter that capitalizes the first character of a string.

First, we need to create it in a module that we’ll require in our app (this step is good practice):

1
2
3
4
angular.module('myApp.filters', []) .filter('capitalize', function() { return function(input) {} });

Filters are just functions to which we pass input. In the function above, we simply take the input as the string on which we are calling the filter. We can do some error-checking inside the function:

1
2
3
4
5
6
7
8
angular.module('myApp.filters', []) .filter('capitalize', function() { return function(input) { // input will be ginger in the usage below if (input) return input[0].toUpperCase() + input.slice(1); } });

See it

{{ 'ginger loves dog bones' | lowercase | capitalize }}

Topics we didn’t get a chance to cover

In this 7-part series, we’ve included a lot of material to get you to the point of using AngularJS comfortably. Unfortunately, we didn’t get to cover a lot of what AngularJS has to offer. We present this list of other topics as places to investigate further:

Want more? Look out for our book

Lastly, if you’ve enjoyed our series and articles, we are industriously working on a book series that covers these topics and much, much more. For more information on the first book in the series, head over to ng-book.com.

Other resources

For more information on AngularJS and other great resources for learning AngularJS more in depth:
Here are a few interesting Angular projects to get you moving:
Get the weekly email all focused on AngularJS. Sign up below to receive the weekly email and exclusive content.
We will never send you spam and it's a cinch to unsubscribe.

Download a free sample of the ng-book: The Complete Book on AngularJS

ng-book: The Complete Book on AngularJS is the canonical AngularJS book available today.

It's free, so just enter your email address and the PDF will be sent directly to your inbox. Mailchimp can take up to an hour to deliver the free sample chapter, but if you don't receive it within the hour, send us an email and we'll manually send them to you!

We'll send you updates about the book, when it updates and other free content.

We will never send you spam and it's a cinch to unsubscribe.

Comments

comments powered by Disqus