24 KiB
AngularJS - Routing
Lesson Objectives
- Describe Push State
- Set up your HTML with propper tags and directives
- Write initial JS
- Write a basic route
- Use url params
- Create route specific variables
- Create a default route
- Use extended features of templates
Describe Push State
You can update the browser's url without refreshing the page using JS
- this is called Push State. It's part of HTML5
- up until recently, the only way to update the url is make a new request
- click a link
- type into browser's url bar and hit enter
- now you can do that with javascript
What can Angular do with this?
- angular will listen for changes in the url
- next, angular will render a template into a specific part of the page that we define
- it will then attach a controller to this template and alias it
asa variable which can be referenced in the HTML
Set up your HTML with proper tags and directives
- We need to include Angular and ngRoute, which is a separate file
- This approach is similar to express, where functionality that is not crucial is moved to separate packages (ejs, method-override, etc...)
file: index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
</head>
<body>
</body>
</html>
Next, tell angular where to render the templates with the ng-view directive
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
</head>
<body>
<main ng-view></main><!--add ng-view here-->
</body>
</html>
We need to make sure all links are relative to a specific base url (/ in this case). Add the following:
<head>
<!-- other head tag stuff here -->
<base href="/">
</head>
file: index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
<base href="/"><!-- add base href here -->
</head>
<body>
<main ng-view></main>
</body>
</html>
Links that start with http will not use push state. Everything else will.
file: index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
<base href="/">
</head>
<body>
<nav>
<ul>
<li><a href="/">root</a></li>
<li><a href="/url1">url1 with no param</a></li>
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
</ul>
</nav>
<main ng-view></main>
</body>
</html>
Write initial JS
We need one dependency for our module
file: js/app.js
const myApp = angular.module('MyApp', ['ngRoute']);
Add ng-app="MyApp" to the <body> tag in your html
index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
<base href="/">
</head>
<body ng-app="MyApp">
<nav>
<ul>
<li><a href="/">root</a></li>
<li><a href="/url1">url1 with no param</a></li>
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
</ul>
</nav>
<main ng-view></main>
</body>
</html>
We need to create a config for our module to make this work. A module config runs only once at load time.
file: js/app.js
const myApp = angular.module('MyApp', ['ngRoute']);
myApp.config(function() { //.config just runs once on load. Looks like a controller, but without a name
});
Let's add two important services
$locationProviderhandles push state- push state
- allows you to update the URL in your browser without reloading the page
- also updates your history when URL changes via push state
- push state
$routeProvidercreates event listeners which render a template into ng-view when the browser URL matches its URL
file: js/app.js
//include $routeProvider and $locationProvider
const myApp = angular.module('MyApp', ['ngRoute']);
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
}]);
Write a basic route
Now let's add a basic route within the config function, after $locationProvider.html5Mode({ enabled: true });.
$routeProvider.when('/url1', { //when http://localhost:3000/url1
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
controller: function(){ // attach controller
//controller code goes here
},
controllerAs: 'ctrl' // alias for controller (like ng-controller="Ctrl1 as ctrl")
});
file: js/app.js
const myApp = angular.module('MyApp', ['ngRoute']);
myApp.controller('Ctrl1', function(){
});
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
$routeProvider.when('/url1', { //when http://localhost:3000/url1
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
controller: function(){ // attach controller
//controller code goes here
this.foo = 'bar'
},
controllerAs: 'ctrl' // alias for controller (like ng-controller="Ctrl1 as ctrl")
});
}]);
add the file: partials/partial1.html
Controller foo var: {{ctrl.foo}}
- Now when you click the "url1 with no param" text, you should see the partial appear in the appropriate place
- Note, if you refresh the page after clicking the link, you'll get a file not found
-
That's because there's no file called
url1 -
You'll have to tell express to serve up your index.html file for that route
app.get('/url1', (req, res)=>{ res.render('/public/index.html') //note the initial / so it doesn't go into views })
-
Use url params
We can access url params with a pattern that is similar to express:
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
templateUrl: 'partials/partial2.html',
controller: 'Ctrl2',
controllerAs: 'ctrl'
});
file: js/app.js
const myApp = angular.module('MyApp', ['ngRoute']);
myApp.controller('Ctrl1', function(){
});
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
$routeProvider.when('/url1', { //when http://localhost:3000/url1
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
controller: 'Ctrl1', // attach controller Ctrl1
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
});
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
templateUrl: 'partials/partial2.html',
controller: 'Ctrl2',
controllerAs: 'ctrl'
});
}]);
file: index.html
<!DOCTYPE html>
<html>
<head>
<base href="/">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body ng-app="MyApp">
<nav>
<ul>
<li><a href="/">root</a></li>
<li><a href="/url1">url1 with no param</a></li>
<li><a href="/url1/someid">url1 with an extra param</a></li>
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
</ul>
</nav>
<main ng-view></main>
</body>
</html>
Inside the controller, we can access those url params:
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
this.id = $routeParams.id; //access :id from url
}]);
file: js/app.js
//include $routeParams service
const myApp = angular.module('MyApp', ['ngRoute']);
myApp.controller('Ctrl1', function(){
});
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
this.id = $routeParams.id; //access :id from url
}]);
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
$routeProvider.when('/url1', { //when http://localhost:3000/url1
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
controller: 'Ctrl1', // attach controller Ctrl1
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
});
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
templateUrl: 'partials/partial2.html',
controller: 'Ctrl2',
controllerAs: 'ctrl'
});
}]);
file: partials/partial2.html
{{ctrl.id}}
Create route specific variables
We can create a variable for a specific route:
$routeProvider.when('/url3', {
//... some code
someVarSpecificToThisRoute: 'weeee' // you can pass variables into the controller here if you'd like
//...some code
});
file: js/app.js
const myApp = angular.module('MyApp', ['ngRoute']);
myApp.controller('Ctrl1', function(){
});
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
this.id = $routeParams.id; //access :id from url
}]);
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
$routeProvider.when('/url1', { //when http://localhost:3000/url1
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
controller: 'Ctrl1', // attach controller Ctrl1
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
});
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
templateUrl: 'partials/partial2.html',
controller: 'Ctrl2',
controllerAs: 'ctrl'
});
$routeProvider.when('/url3', {
templateUrl: 'partials/partial3.html',
controller: 'Ctrl3',
controllerAs: 'ctrl',
someVarSpecificToThisRoute: 'weeee' // you can pass variables into the controller here if you'd like
});
}]);
And access it in the controller:
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
this.id = $routeParams.id; //access :id from url
}]);
file: js/app.js
const myApp = angular.module('MyApp', ['ngRoute']);
myApp.controller('Ctrl1', function(){
});
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
this.id = $routeParams.id; //access :id from url
}]);
myApp.controller('Ctrl3', ['$route', function ($route) {
this.funVar = $route.current.someVarSpecificToThisRoute; //you can access variables created in the when action like this
}]);
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
$routeProvider.when('/url1', { //when http://localhost:3000/url1
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
controller: 'Ctrl1', // attach controller Ctrl1
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
});
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
templateUrl: 'partials/partial2.html',
controller: 'Ctrl2',
controllerAs: 'ctrl'
});
$routeProvider.when('/url3', {
templateUrl: 'partials/partial3.html',
controller: 'Ctrl3',
controllerAs: 'ctrl',
someVarSpecificToThisRoute: 'weeee' // you can pass variables into the controller here if you'd like
});
}]);
add link in index.html:
<!DOCTYPE html>
<html>
<head>
<base href="/">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body ng-app="MyApp">
<nav>
<ul>
<li><a href="/">root</a></li>
<li><a href="/url1">url1 with no param</a></li>
<li><a href="/url1/someid">url1 with an extra param</a></li>
<li><a href="/url3">url2</a></li>
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
</ul>
</nav>
<main ng-view></main>
</body>
</html>
create partials/partial3.html
{{ctrl.funVar}}
Create a default route
Lastly, you can create a default route. Note at any point, you don't have to have a controller/partial combo. You can also have just a redirect property:
$routeProvider.otherwise({ // if browser url doesn't match any of the above...
//here you can do something like above if you'd like with a template and a controller
redirectTo: '/' // or you can redirect to another url.
//redirection can happen in any 'when' action; I just happened to do it here. I could have put it in one of the above sections too
});
file: js/app.js
const myApp = angular.module('MyApp', ['ngRoute']);
myApp.controller('Ctrl1', function(){
});
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
this.id = $routeParams.id; //access :id from url
}]);
myApp.controller('Ctrl3', ['$route', function ($route) {
this.funVar = $route.current.someVarSpecificToThisRoute; //you can access variables created in the when action like this
}]);
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
$routeProvider.when('/url1', { //when http://localhost:3000/url1
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
controller: 'Ctrl1', // attach controller Ctrl1
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
});
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
templateUrl: 'partials/partial2.html',
controller: 'Ctrl2',
controllerAs: 'ctrl'
});
$routeProvider.when('/url2', {
templateUrl: 'partials/partial3.html',
controller: 'Ctrl3',
controllerAs: 'ctrl',
someVarSpecificToThisRoute: 'weeee' // you can pass variables into the controller here if you'd like
});
$routeProvider.otherwise({ // if browser url doesn't match any of the above...
//here you can do something like above if you'd like with a template and a controller
redirectTo: '/' // or you can redirect to another url.
//redirection can happen in any 'when' action; I just happened to do it here. I could have put it in one of the above sections too
});
}]);
create a route that doesn't match any predefined url:
<li><a href="/badurl">bad url</a></li>
file: index.html
<!DOCTYPE html>
<html>
<head>
<base href="/">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body ng-app="MyApp">
<nav>
<ul>
<li><a href="/">root</a></li>
<li><a href="/url1">url1 with no param</a></li>
<li><a href="/url1/someid">url1 with an extra param</a></li>
<li><a href="/url2">url2</a></li>
<li><a href="/badurl">bad url</a></li>
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
</ul>
</nav>
<main ng-view></main>
</body>
</html>
Use extended features of templates
Script tag templates
You can have a template inside a script tag instead of an external file.
- the advantage to this is that it doesn't need to make an extra http request to get the template
- the downside is that your html gets more bloated
<script type="text/ng-template" id='partials/partial4.html'>
template 4<br/>
</script>
file: index.html
<!DOCTYPE html>
<html>
<head>
<base href="/">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body ng-app="MyApp">
<nav>
<ul>
<li><a href="/">root</a></li>
<li><a href="/url1">url1 with no param</a></li>
<li><a href="/url1/someid">url1 with an extra param</a></li>
<li><a href="/url2">url2</a></li>
<li><a href="/badurl">bad url</a></li>
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
</ul>
</nav>
<main ng-view></main>
<script type="text/ng-template" id='partials/partial4.html'>
template 4<br/>
</script>
</body>
</html>
file: js/app.js
const myApp = angular.module('MyApp', ['ngRoute']);
myApp.controller('Ctrl1', function(){
});
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
this.id = $routeParams.id; //access :id from url
}]);
myApp.controller('Ctrl3', ['$route', function ($route) {
this.funVar = $route.current.someVarSpecificToThisRoute; //you can access variables created in the when action like this
}]);
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
$routeProvider.when('/url1', { //when http://localhost:3000/url1
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
controller: 'Ctrl1', // attach controller Ctrl1
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
});
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
templateUrl: 'partials/partial2.html',
controller: 'Ctrl2',
controllerAs: 'ctrl'
});
$routeProvider.when('/url2', {
templateUrl: 'partials/partial3.html',
controller: 'Ctrl3',
controllerAs: 'ctrl',
someVarSpecificToThisRoute: 'weeee' // you can pass variables into the controller here if you'd like
});
$routeProvider.when('/url4', {
templateUrl: 'partials/partial4.html'
})
$routeProvider.otherwise({ // if browser url doesn't match any of the above...
//here you can do something like above if you'd like with a template and a controller
redirectTo: '/' // or you can redirect to another url.
//redirection can happen in any 'when' action; I just happened to do it here. I could have put it in one of the above sections too
});
}]);
create link in index.html
<!DOCTYPE html>
<html>
<head>
<base href="/">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body ng-app="MyApp">
<nav>
<ul>
<li><a href="/">root</a></li>
<li><a href="/url1">url1 with no param</a></li>
<li><a href="/url1/someid">url1 with an extra param</a></li>
<li><a href="/url2">url2</a></li>
<li><a href="/badurl">bad url</a></li>
<li><a href="/url4">url4</a></li>
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
</ul>
</nav>
<main ng-view></main>
<script type="text/ng-template" id='partials/partial4.html'>
template 4<br/>
</script>
</body>
</html>
Sub Partials
You can have includes inside templates.
file: partials/partial5.html
template 5<br/>
<ng-include src="'partials/partial1.html'"></ng-include>
create a route handler in js/app.js:
const myApp = angular.module('MyApp', ['ngRoute']);
myApp.controller('Ctrl1', function(){
});
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
this.id = $routeParams.id; //access :id from url
}]);
myApp.controller('Ctrl3', ['$route', function ($route) {
this.funVar = $route.current.someVarSpecificToThisRoute; //you can access variables created in the when action like this
}]);
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
$routeProvider.when('/url1', { //when http://localhost:3000/url1
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
controller: 'Ctrl1', // attach controller Ctrl1
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
});
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
templateUrl: 'partials/partial2.html',
controller: 'Ctrl2',
controllerAs: 'ctrl'
});
$routeProvider.when('/url2', {
templateUrl: 'partials/partial3.html',
controller: 'Ctrl3',
controllerAs: 'ctrl',
someVarSpecificToThisRoute: 'weeee' // you can pass variables into the controller here if you'd like
});
$routeProvider.when('/url4', {
templateUrl: 'partials/partial4.html'
})
$routeProvider.when('/url5', {
templateUrl: 'partials/partial5.html'
})
$routeProvider.otherwise({ // if browser url doesn't match any of the above...
//here you can do something like above if you'd like with a template and a controller
redirectTo: '/' // or you can redirect to another url.
//redirection can happen in any 'when' action; I just happened to do it here. I could have put it in one of the above sections too
});
}]);
add a link in index.html:
<!DOCTYPE html>
<html>
<head>
<base href="/">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body ng-app="MyApp">
<nav>
<ul>
<li><a href="/">root</a></li>
<li><a href="/url1">url1 with no param</a></li>
<li><a href="/url1/someid">url1 with an extra param</a></li>
<li><a href="/url2">url2</a></li>
<li><a href="/badurl">bad url</a></li>
<li><a href="/url4">url4</a></li>
<li><a href="/url5">url5</a></li>
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
</ul>
</nav>
<main ng-view></main>
<script type="text/ng-template" id='partials/partial4.html'>
template 4<br/>
</script>
</body>
</html>