You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

24 KiB

AngularJS - Routing

Lesson Objectives

  1. Describe Push State
  2. Set up your HTML with propper tags and directives
  3. Write initial JS
  4. Write a basic route
  5. Use url params
  6. Create route specific variables
  7. Create a default route
  8. 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 as a 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

  • $locationProvider handles 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
  • $routeProvider creates 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>