# AngularJS - Routing
## Lesson Objectives
1. Describe Push State
1. Set up your HTML with propper tags and directives
1. Write initial JS
1. Write a basic route
1. Use url params
1. Create route specific variables
1. Create a default route
1. 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
```html
```
Next, tell angular where to render the templates with the `ng-view` directive
```html
```
We need to make sure all links are relative to a specific base url (/ in this case). Add the following:
```html
```
file: index.html
```html
```
Links that start with `http` will not use push state. Everything else will.
file: index.html
```html
```
## Write initial JS
We need one dependency for our module
file: js/app.js
```javascript
const myApp = angular.module('MyApp', ['ngRoute']);
```
Add `ng-app="MyApp"` to the `` tag in your html
index.html
```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
```javascript
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
```javascript
//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 });`.
```javascript
$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
```javascript
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
```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
```javascript
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:
```javascript
$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
```javascript
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
```html
```
Inside the controller, we can access those url params:
```javascript
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
this.id = $routeParams.id; //access :id from url
}]);
```
file: js/app.js
```javascript
//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
```html
{{ctrl.id}}
```
## Create route specific variables
We can create a variable for a specific route:
```javascript
$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
```javascript
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:
```javascript
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
this.id = $routeParams.id; //access :id from url
}]);
```
file: js/app.js
```javascript
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:
```html
```
create partials/partial3.html
```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:
```javascript
$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
```javascript
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:
```html
````
file: index.html
```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
```html
```
file: index.html
```html
```
file: js/app.js
```javascript
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
```html
```
### Sub Partials
You can have includes inside templates.
file: partials/partial5.html
```html
template 5
```
create a route handler in js/app.js:
```javascript
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:
```html
```