Professional Documents
Culture Documents
© 2013, Cognizant Technology Solutions
© 2013, Cognizant Technology Solutions
AngularJS's routing API enables us to map URLs within our application with specific
functionalities.
This is typically done by associating a template and controller with a particular URL.
So, we can say that each AngularJS route is composed of a URL, a template, and a
controller.
When someone first lands on our web page, it's a full-page load.
Subsequently, when the user clicks a link and navigates to a different URL, the route
changes and content of the route are dynamically loaded into the page via an AJAX call.
The benefit of this approach is that it results in much less network traffic.
In a typical web app, we'd load each page fully with each request, serving loads of
repetitive content such as headers and footers multiple times in the process.
But when you use multiple views and routing there's no repetitive content since the
headers, footers, sidebars etc. are only served as a part of the first request.
All subsequent requests simply update specific elements of your page
As you might expect, this leads to faster rendering of the views.
controller: The controller which will be associated with the view. After all, we need a
scope against which the template will be compiled.
templateUrl: The HTML template to be used.
Here, you should note that the controller parameter is actually optional. You can either
define the controller at route level as we are already doing or just use it in the
template through ng-controller.
Also you should note that $routeProvider.when() adds a new mapping and returns the
same $routeProvider instance. So, you can chain these function calls to add multiple
mappings just as we did above.
The controllers (app/js/controllers.js):
angular.module('myApp.controllers', []).controller('Controller1',
function($scope){
$scope.message="Hello, world";}).controller('Controller2',
function($scope){
$scope.now=new Date();
});
Main view (app/index.html):
<!doctype html>
<html lang="en" ng-app="myApp">
<head> <meta charset="utf-8"> <title>AngularJS Routing</title>
<link rel="stylesheet" href="css/app.css"/>
</head>
<body>
<ul class="menu">
<li><a href="#/view1">view1</a></li>
<li><a href="#/view2">view2</a></li>
</ul>
<ng-view></ng-view>
<script src="lib/angular/angular.js"></script>
<script src="lib/angular/angular-route.js"></script>
<script src="js/app.js"></script> <script src="js/controllers.js"></script>
</body>
</html>
The most important point to note here is that our main view, index.html, has an
element <ng-view>.
This acts as a container for the different views (in our case view1.html and view2.html).
When the user clicks on a link (for example#/view1), the route changes
to index.html#/view1 and AngularJS searches for the template mapped with the current
route
Once the template is found the content is fetched and included in <ng-view> and compiled
against the $scope so that the expressions ({{}}) are evaluated.
The same $scope is passed as an argument to the controller associated with the particular
route.
So, you can set required models on the $scope and access those in the template.
Note that there should be only one <ng-view> in your page, which will be used to show content
for different routes.
You can also use ng-view as an attribute or class. So, the above code can use ng-view as:
<div class="ng-view"></div>
<div ng-view></div>
Do not use the element version if you really want to be compatible with Internet
Explorer. Instead, use the attribute version.
Also note that ng-view creates a new scope that prototypically inherits from the parent
scope. This $scope is passed as an argument to the controller's constructor function
associated with a particular route.
Template1 (app/partials/view1.html):
<p>From View 1: {{message}}</p>
Template2 (app/partials/view2.html):
<p>From View 2: {{now | date:'medium'}}</p>
13 © 2013, Cognizant Technology Solutions
What will be covered?
Day 5 (Multiple Views and Routing)
You might have already noticed the route name is prefixed with # in the URL.
For example, when you click on hyperlinkview1, the URL in the address bar changes
to index.html#/view1.
This is called hashbang mode, which is the default in AngularJS.
The other option is to ask AngularJS to use HTML5 history API.
This mode is called html5Mode.
In this case, the URLs in address bar for the two routes will be changed to:
/view1 from index.html#/view1
/view2 from index.html#/view2
The html5Mode, when activated, removes # from your URLs and takes advantage of
HTML5's pushState API.
angular.module('myApp').config(function($routeProvider,$locationProvider){
$routeProvider.when('/view1',{
controller:'Controller1',
templateUrl:'partials/view1.html'
}).when('/view2',{
controller: 'Controller2',
templateUrl: 'partials/view2.html' });
$locationProvider.html5Mode(true); //activate HTML5 Mode
});
You also need to change the hyperlinks in the main page (index.html) to remove
the # prefixes.
<ul class="menu">
<li><a href="/view1">view1</a></li>
<li><a href="/view2">view2</a></li>
</ul>
If you set html5Mode to true, AngularJS first checks for history API support in the
browser. If it's available then the html5Mode is used, otherwise AngularJS falls back to
the hashbang mode.
But there is a gotcha! When navigating inside of the app, everything works as expected.
If you try to open a URL like http://localhost:8080/view1 directly in the browser it will
result in a 404.
The reason for this is that there's no AngularJS when you directly ask the server
for http://localhost:8080/view1.
In the case of the hashbang version, the browser always loads the index.html first, and
AngularJS then steps in to resolve the hashbang URL.
Hence this is the limitation of html5Mode.
If you want to stick to html5Mode, you'll need some kind of server-side configuration to
resolve this issue.
So, if somebody tries to mess around and tries loading a route that doesn't exist
(like http://localhost:8000/index.html#/view3) your app won't break.
You can safely redirect the user to a default route with the help of otherwise().
Sometimes it's necessary to pass some parameters to the controller associated with
your view.
For example, in a blogging app, you may want to pass a post ID in the URL, which the
controller can grab and then fetch the related content via an Ajax call.
These parameters passed in the route are called route parameters and are exposed by a
integrated AngularJS service called $routeParams.
To demonstrate this concept let's adjust our previous demo app to use $routeParams.
app.js:
angular.module('myApp', [ 'myApp.controllers', 'ngRoute']);
angular.module('myApp').config(function($routeProvider,$locationProvider)
{ $routeProvider.when('/view1',{
controller:'Controller1',
templateUrl:'/partials/view1.html' }).when('/view2/:firstname/:lastname',{
controller: 'Controller2',
templateUrl: '/partials/view2.html' }).otherwise({redirectTo:'/view1'});
$locationProvider.html5Mode(true);});
partials/view2.html:
<p> From View2. <ul>
<li>First name: {{firstname}}</li>
<li>Last name: {{lastname}}</li>
</ul>
22
</p>
© 2013, Cognizant Technology Solutions
What will be covered?
Day 5 (Multiple Views and Routing)
Using $routeParams in the Controller
controllers.js:
angular.module('myApp.controllers', []).controller('Controller1',function($scope,
$location){
$scope.loadView2=function(){ $location.path('/view2/'+$scope.firstname+'/'+
$scope.lastname);
}
}).controller('Controller2',function($scope,$routeParams)
{ $scope.firstname=$routeParams.firstname;
$scope.lastname=$routeParams.lastname;
});
controllers.js:
Controller1 declares a dependency on something new: the $location service.
This is a built-in service that parses the URL in the browser address bar and exposes it to
our app via several functions.
We'll discuss $location in detail later, for now, just note that $location.path() is used to
load a new route.
Controller2 declares a dependency on $routeParams, which is an AngularJS service that
exposes the route parameters to our controller.
Apart from named parameters, $routeParams also parses the query string, if present. So,
if your URL contains a query string like ?key1=value1, the key/value pair is added
to $routeParams.
The second argument can be an object that is used to pass $stateParams to the new state (As
opposed to $routeParams).
Our last change will be in Controller2 to use $stateParams instead of $routeParams to obtain
the named parameters passed to the state.
angular.module('myApp.controllers').controller('Controller2',
function($scope,$stateParams,names){
$scope.firstname=$stateParams.firstname;
$scope.lastname=$stateParams.lastname;
$scope.names=names;
});
A promise object represents a value that may not be available yet, but will be at some point
in future.
It enables you to write asynchronous code in a more synchronous way.
This means your asynchronous function will return immediately and you can treat this return
value as a proxy to the actual value that'll be obtained in future.
A typical example of the use of a promise object is in an AJAX request to a remote server.
This won't hold any value yet; it just acts as a proxy to the actual value that will be received
from the server later.
When the AJAX request completes and an actual response arrives from the server,
the Promise is resolved with that data.
If the response can't be obtained due to some error, the promise is rejected with an error
message.
The promise object also exposes several methods that enable you to write callbacks, which
will be called once it's resolved or rejected.
Let's take a look at some pseudo code that logs in a user and (once the login is successful)
performs some other task.
In the first scenario we pass a callback to login() that's called after login is successful:
user.login(username,password,function(){ //This callback is passed to login() so that it's
called only after login is successful.
});
Now, here's the login() function returning a promise, and we use it to perform some task once
login is successful:
user.login(username,password).then(function(){ //do something here
});
AngularJS enables you to create and use promises through a built-in service called $q.
The following pseudo code shows how to use the API exposed by $q:
function showResult(){
var promise = getPromise(); // get the Promise instance
promise.then(function(message) {
console.log('Promise Resolved with Message: '+message);
}, function(message) {
console.log('Promise Rejected with Message: '+message);
})
;}
promise.then(successCallback,failureCallback,notifyCallback);
The success callback is called when the promise is resolved (as a result of
calling deferred.resolve()).
The callback accepts one argument, which represents the resolution value (the value passed
to deferred.resolve()).
The second callback passed to then() is the error callback and is called when the Promise is
rejected as a result of callingdeferred.reject().
Again it accepts a single argument, which represents the rejection reason (the value passed
todeferred.reject()).
The third argument is the notify callback which may be called multiple times to indicate the
operation progress (we'll see it shortly).