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.

29 KiB

AngularJS - Routing

Lesson Outline

We will be creating a basic page about a Vet. This will have links which load various template files via the Angular Router. Final code is available in the instructor_examples/routing directory, but I'll be building it temporarily in instructor_examples/live_coding.

  1. Running Angular Server with http-server
  2. Routing Overview
  3. Mini exercise
  4. What we are building
  5. Push State Overview
  6. Write initial HTML
  7. Write initial JavaScript and app config
  8. Enable HTML5 Push state
  9. Create Contact Template
  10. Create Contact Controller
  11. Create Contact Route
  12. Create About Template and Route
  13. Pets page; Using parameter from URL, Route / Controller / Template
  14. Pricing page; Pass data from Route to Template
  15. Joke page; using template script tag
  16. All page; nesting partials/templates
  17. Default / Fallback Page
  18. Google Link / External page
  19. Final Code

Running Angular Server with http-server

Note: Sometimes the page gets cached and updates are not seen. Try a hard refresh by holding down the shift key when you refresh

Sometimes hard refresh isn't enough, try clear browsing history

We're going to create a basic AngularJS application, without an Express server for this exercise. We want to be able to browser to http://localhost:8080. We are not using nodemon for this exercise, nor do we need mongod.

  • Install npm-server by running npm install -g http-server from the terminal.
  • Create a directory called routing. mkdir routing and cd routing
  • Create empty app.js and index.html files inside of it. touch app.js index.html
  • Run the server with http-server -o
  • View the (currently blank) files by browsing to http://localhost:8080

Routing Overview

The Angular Router enables navigation from one view and controllers to the next as users perform application tasks such as clicking on links.

This is similar the routing in Express that we saw prior, and the routing that we will see in Rails in Unit 4.

Another way to think about it is when you have a specific URL, you want to have a specific section of code run.

For example, you might have /products to show an index of all your products, /products/:id to show a single product, /login that displays the login screen, /register that displays the registration screen.

Routing will also pass the application parameters from the URL, such as a product id, or a search query string like /search?query=apple. The application can then use these parameters as a form of user input.

Mini Exercise

Write a short list of three possible routes that an application might have, and one parameter that you might use for input. Respond in a thread here on Slack.

What We Are Building

We're going to build the beginnings an AngularJS application, focusing almost exclusively on the routes. The view templates, controllers, HTML and CSS will be bare-bones to let us focus on this.

My cat Jade is feeling a little sick, so we're going to think about building a basic page for a Veterinarian.

Example Site Image

Pages we need:

  • Home (/)
  • Contact (/contact)
  • About (/about)
  • Pricing (/pricing)
  • Cats (/pets/cats) - Both of these will use just one route/controller/template
  • Dogs (/pets/dogs) - Both of these will use just one route/controller/template
  • Joke of the Day (/joke) - This will use an inline template
  • All (/all) - This will combine multiple templates
  • Bad URL (/badurl) - This will show a default / fallback page
  • Link to Google - external link

These are a bit arbitrary and artificial of goals, but will help give us some more context to talk about specific links.

Push State Overview

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 requests
    • 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

Write Initial HTML

We're going to setup most of the HTML we need upfront. This is a bit much to type, but this is the biggest single section of code we'll see at once here.

  • 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...)
  • Add a link the /contact and other pages outlined above. You do this as you'd create any other relative link. In a moment, we'll see how to handle this with our JavaScript.
  • Add a reference to our JavaScript file in the html, and add <body ng-app="VetApp">
  • Next, tell angular where to render the templates with the ng-view directive
  • Route relative to a base url (/ in this case) with <base href="/">

file: index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title></title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js" charset="utf-8"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-route.min.js" charset="utf-8"></script>
  <script src="app.js" charset="utf-8"></script>
  <base href="/" target="_blank">
</head>
<body ng-app="MyApp">
  <h1>Vets and Pets!</h1>
  <nav>
    <ul>
      <li><a href="/">Home (/)</a></li>
      <li><a href="/contact">Contact</a></li>
      <li><a href="/about">About</a></li>
      <li><a href="/pricing">Pricing</a></li>
      <li><a href="/joke">Joke of the Day</a></li>
    </ul>

    <h3>Pets</h3>
    <ul>
      <li><a href="/pets/cats">Cats</a></li>
      <li><a href="/pets/dogs">Dogs</a></li>
    </ul>
  </nav>

  <!-- This is the main view that will be filled in with view templates -->
  <main ng-view></main>
</body>
</html>

Write initial JS

We need one dependency for our module

file: app.js

const app = angular.module('MyApp', ['ngRoute']);

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 app = angular.module('MyApp', ['ngRoute']);

//.config just runs once on load.  Looks like a controller, but without a name
app.config(function() { });

Let's add two important services. This is via Dependency Injection, and will perhaps seem a little odd at first.

  • $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: app.js

//include $routeProvider and $locationProvider
const app = angular.module('MyApp', ['ngRoute']);

app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
  // Enables Push State
  $locationProvider.html5Mode({ enabled: true });
}]);

Create Contact Templates

Create a partial file that has some text inside of it, and uses the ctrl.phone, which we will populate and link together with the Controller and Route in the next steps

file: contact.html

<h2>Contact Us:</h2>
{{ctrl.phone}}

Create Contact Controller

file: app.js

const app = angular.module('MyApp', ['ngRoute']);

app.controller('ContactController', function () {
  this.phone = '555-1212';
});

app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
  // Enables Push State
  $locationProvider.html5Mode({ enabled: true });
}]);

Create Contact Route

Now let's add a basic route within the config function. We'll use the ContactController we added above to tie to all together. After this, the link should show a new view when clicked on!

$routeProvider.when('/contact', {
  templateUrl: 'contact.html', // render http://localhost:3000/contact.html
  controller: 'ContactController', // attach controller ContactController
  controllerAs: 'ctrl' // alias for ContactController (like ng-controller="ContactController as ctrl")
});

Question for Slack: Why would a controller alias be useful? Respond in a threaded message.

Then we create a basic empty controller by adding app.controller('ContactController', function() { });. Then assign the value of this.phone so we can use it in the template.

file: app.js

const app = angular.module('MyApp', ['ngRoute']);

app.controller('ContactController', function () {
  this.phone = '555-1212';
});

app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
  // Enables Push State
  $locationProvider.html5Mode({ enabled: true });

  $routeProvider.when('/contact', {
    templateUrl: 'contact.html',
    controller: 'ContactController',
    controllerAs: 'ctrl'
  });
}]);

You Turn:

Make a template, route and controller for a /about page. Don't worry about passing data from the controller to the template.

Using URL Params for Individual Pets Pages

We can access url params with a pattern that is similar to express:

$routeProvider.when('/pets/:id', {  // when http://localhost:3000/pets/:id - :id is a param just like in express
  templateUrl: 'pets.html',
  controller: 'PetController',
  controllerAs: 'ctrl'
});

Inside the controller, we can access those url params using $routeParams that we pass to the function:

app.controller('PetController', ['$routeParams', function ($routeParams) {
  this.id = $routeParams.id; //access :id from url
}]);

These are used to pass cats and dogs from the links we created earlier in the ul elements, but they could be used to pass numbers or other data from the URL into the routes and controller:

<h3>Pets</h3>
<ul>
  <li><a href="/pets/cats">Cats</a></li>
  <li><a href="/pets/dogs">Dogs</a></li>
</ul>

file: app.js

const app = angular.module('MyApp', ['ngRoute']);

app.controller('ContactController', function () {
  this.phone = '555-1212';
});

app.controller('PetController', ['$routeParams', function ($routeParams) {
  // access :id from url
  this.id = $routeParams.id;
}]);

app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
  // Enables Push State
  $locationProvider.html5Mode({ enabled: true });

  $routeProvider.when('/contact', {
    templateUrl: 'contact.html',
    controller: 'ContactController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/about', {
    templateUrl: 'about.html'
    // We don't need to specify a controller, because there's no data
  });

  // when http://localhost:3000/url1/:id - :id is a param just like in express
  $routeProvider.when('/pets/:id', {
    templateUrl: 'pets.html',
    controller: 'PetController',
    controllerAs: 'ctrl'
  });
}]);

Create Pricing Page: Pass data from Route to Controller to Template

You can pass data from the route into the controller, and then use it in a template.

Here we pass the price of $1 million dollars from the route to the controller. Note the function definition uses $route instead of $routeParams!

Create a PricingController, route and template:

file: app.js

const app = angular.module('MyApp', ['ngRoute']);

app.controller('ContactController', function () {
  this.phone = '555-1212';
});

app.controller('PetController', ['$routeParams', function ($routeParams) {
  // access :id from url
  this.id = $routeParams.id;
}]);

app.controller('PricingController', ['$route', function ($route) {
  this.price = $route.current.price;
}]);

app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
  // Enables Push State
  $locationProvider.html5Mode({ enabled: true });

  $routeProvider.when('/contact', {
    templateUrl: 'contact.html',
    controller: 'ContactController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/about', {
    templateUrl: 'about.html'
    // We don't need to specify a controller, because there's no data
  });

  // when http://localhost:3000/url1/:id - :id is a param just like in express
  $routeProvider.when('/pets/:id', {
    templateUrl: 'pets.html',
    controller: 'PetController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/pricing', {
    templateUrl: 'pricing.html',
    controller: 'PricingController',
    controllerAs: 'ctrl',
    price: '$1 trillion dollars'
  });
}]);

file: pricing.html

<h2>Pricing</h2>
{{ctrl.price}}

file: app.js

const app = angular.module('MyApp', ['ngRoute']);

app.controller('ContactController', function () {
  this.phone = '555-1212';
});

app.controller('PetController', ['$routeParams', function ($routeParams) {
  // access :id from url
  this.id = $routeParams.id;
}]);

app.controller('PricingController', ['$route', function ($route) {
  this.price = $route.current.price;
}]);

app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
  // Enables Push State
  $locationProvider.html5Mode({ enabled: true });

  $routeProvider.when('/contact', {
    templateUrl: 'contact.html',
    controller: 'ContactController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/about', {
    templateUrl: 'about.html'
    // We don't need to specify a controller, because there's no data
  });

  // when http://localhost:3000/url1/:id - :id is a param just like in express
  $routeProvider.when('/pets/:id', {
    templateUrl: 'pets.html',
    controller: 'PetController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/pricing', {
    templateUrl: 'pricing.html',
    controller: 'PricingController',
    controllerAs: 'ctrl',
    price: '$1 trillion dollars'
  });
}]);

Joke page; using template script tag

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

Here we'll create a joke.html template, but instead of making a new file, we'll put the contents inside a script tag in index.html. Also, create a link to /joke in the navigation list.

<script type="text/ng-template" id='joke.html'>
  <h2>Pet Joke of the Day<h2/>
  <p>{{ctrl.joke}}</p>
</script>

file: index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title></title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js" charset="utf-8"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-route.min.js" charset="utf-8"></script>
  <script src="app.js" charset="utf-8"></script>
  <base href="/" target="_blank">
</head>
<body ng-app="MyApp">
  <h1>Vets and Pets!</h1>
  <nav>
    <ul>
      <li><a href="/">Home (/)</a></li>
      <li><a href="/contact">Contact</a></li>
      <li><a href="/about">About</a></li>
      <li><a href="/pricing">Pricing</a></li>
      <li><a href="/joke">Joke of the Day</a></li>
    </ul>

    <h3>Pets</h3>
    <ul>
      <li><a href="/pets/cats">Cats</a></li>
      <li><a href="/pets/dogs">Dogs</a></li>
    </ul>
  </nav>

  <!-- This is the main view that will be filled in with view templates -->
  <main ng-view></main>

  <script type="text/ng-template" id='joke.html'>
    <h2>Pet Joke of the Day</h2>
    <p>{{ctrl.joke}}</p>
  </script>
</body>
</html>

Then, create a JokeController and /joke route. Also, create an array of jokes in the JokeController, and pass the first one to this.joke so that the joke is displayed in the template.

If you have a moment of extra time, try to figure out how to make a random joke appear each time on the page.

file app.js

const app = angular.module('MyApp', ['ngRoute']);

app.controller('ContactController', function () {
  this.phone = '555-1212';
});

app.controller('PetController', ['$routeParams', function ($routeParams) {
  // access :id from url
  this.id = $routeParams.id;
}]);

app.controller('JokeController', ['$route', function () {
  this.joke = "Why don't cats like online shopping? They prefere a cat-alogue"
}]);

app.controller('PricingController', ['$route', function ($route) {
  this.price = $route.current.price;
}]);

app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
  // Enables Push State
  $locationProvider.html5Mode({ enabled: true });

  $routeProvider.when('/contact', {
    templateUrl: 'contact.html',
    controller: 'ContactController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/about', {
    templateUrl: 'about.html'
    // We don't need to specify a controller, because there's no data
  });

  // when http://localhost:3000/url1/:id - :id is a param just like in express
  $routeProvider.when('/pets/:id', {
    templateUrl: 'pets.html',
    controller: 'PetController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/pricing', {
    templateUrl: 'pricing.html',
    controller: 'PricingController',
    controllerAs: 'ctrl',
    price: '$1 trillion dollars'
  });

  $routeProvider.when('/joke', {
    templateUrl: 'joke.html',
    controller: 'JokeController',
    controllerAs: 'ctrl'
  });
}]);

All Page - Nesting Partials/Templates

Combining multiple partials is possible by having multiple ng-include elements in an html file. This is a great way to reuse code. Our example here is a bit artificial, but will show you how to reuse partials we've already created above all together.

Create an all.html file, and make ng-include elements for contact.html, pricing.html and joke.html.

file: all.html

<ng-include src="contact.html"></ng-include>
<ng-include src="pricing.html"></ng-include>
<ng-include src="joke.html"></ng-include>

Then create a link to /all in the index.html

file: index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title></title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js" charset="utf-8"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-route.min.js" charset="utf-8"></script>
  <script src="app.js" charset="utf-8"></script>
  <base href="/" target="_blank">
</head>
<body ng-app="MyApp">
  <h1>Vets and Pets!</h1>
  <nav>
    <ul>
      <li><a href="/">Home (/)</a></li>
      <li><a href="/contact">Contact</a></li>
      <li><a href="/about">About</a></li>
      <li><a href="/pricing">Pricing</a></li>
      <li><a href="/joke">Joke of the Day</a></li>
      <li><a href="/all">Contact, Pricing and Joke!</a></li>
    </ul>

    <h3>Pets</h3>
    <ul>
      <li><a href="/pets/cats">Cats</a></li>
      <li><a href="/pets/dogs">Dogs</a></li>
    </ul>
  </nav>

  <!-- This is the main view that will be filled in with view templates -->
  <main ng-view></main>

  <script type="text/ng-template" id='joke.html'>
    <h2>Pet Joke of the Day</h2>
    <p>{{ctrl.joke}}</p>
  </script>

</body>
</html>

Create an AllController that will give us the data needed for those partials. There are cleaner ways of not-repeating ourselves for this data, but for now let's just repeat the code to keep things simple.

Then, create a route to /all that will use the AllController and the all.html partial.

const app = angular.module('MyApp', ['ngRoute']);

app.controller('ContactController', function () {
  this.phone = '555-1212';
});

app.controller('PetController', ['$routeParams', function ($routeParams) {
  // access :id from url
  this.id = $routeParams.id;
}]);

app.controller('JokeController', ['$route', function () {
  const jokes = ["Why don't cats like dogs?",
    "What is the difference between a duck?"];

  //you can access variables created in the when action like this
  this.joke = jokes[0];
}]);

app.controller('PricingController', ['$route', function ($route) {
  this.price = $route.current.price;
}]);

app.controller('AllController', ['$route', function () {
  const jokes = ["Why don't cats like dogs?",
    "What is the difference between a duck?"];

  this.joke = jokes[1];
  this.phone = '555-1212';
  this.price = '$1 billion dollars';
}]);

app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
  // Enables Push State
  $locationProvider.html5Mode({ enabled: true });

  $routeProvider.when('/contact', {
    templateUrl: 'contact.html',
    controller: 'ContactController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/about', {
    templateUrl: 'about.html'
    // We don't need to specify a controller, because there's no data
  });

  // when http://localhost:3000/url1/:id - :id is a param just like in express
  $routeProvider.when('/pets/:id', {
    templateUrl: 'pets.html',
    controller: 'PetController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/pricing', {
    templateUrl: 'pricing.html',
    controller: 'PricingController',
    controllerAs: 'ctrl',
    price: '$1 trillion dollars'
  });

  $routeProvider.when('/joke', {
    templateUrl: 'joke.html',
    controller: 'JokeController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/all', {
    templateUrl: 'all.html',
    controller: 'AllController',
    controllerAs: 'ctrl'
  });
}]);

Default / Fallback routes

What if we have a link that doesn't match the routes defined? We can set a catch-all default route in that case using otherwise. You can either have a specific controller and template you'd like to use, or you can redirect it with redirectTo in the app.js file.

$routeProvider.otherwise({
  redirectTo: '/'
});

In index.html then you can put a link do a page that otherwise doesn't exist:

<a href="/badurl">/badurl</a>

Afterward, app.js and index.html should look like these:

file: app.js

const app = angular.module('MyApp', ['ngRoute']);

app.controller('ContactController', function () {
  this.phone = '555-1212';
});

app.controller('PetController', ['$routeParams', function ($routeParams) {
  // access :id from url
  this.id = $routeParams.id;
}]);

app.controller('JokeController', ['$route', function () {
  const jokes = ["Why don't cats like dogs?",
    "What is the difference between a duck?"];

  //you can access variables created in the when action like this
  this.joke = jokes[0];
}]);

app.controller('PricingController', ['$route', function ($route) {
  this.price = $route.current.price;
}]);

app.controller('AllController', ['$route', function () {
  const jokes = ["Why don't cats like dogs?",
    "What is the difference between a duck?"];

  this.joke = jokes[1];
  this.phone = '555-1212';
  this.price = '$1 billion dollars';
}]);

app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
  // Enables Push State
  $locationProvider.html5Mode({ enabled: true });

  $routeProvider.when('/contact', {
    templateUrl: 'contact.html',
    controller: 'ContactController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/about', {
    templateUrl: 'about.html'
    // We don't need to specify a controller, because there's no data
  });

  // when http://localhost:3000/url1/:id - :id is a param just like in express
  $routeProvider.when('/pets/:id', {
    templateUrl: 'pets.html',
    controller: 'PetController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/pricing', {
    templateUrl: 'pricing.html',
    controller: 'PricingController',
    controllerAs: 'ctrl',
    price: '$1 trillion dollars'
  });

  $routeProvider.when('/joke', {
    templateUrl: 'joke.html',
    controller: 'JokeController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/all', {
    templateUrl: 'all.html',
    controller: 'AllController',
    controllerAs: 'ctrl'
  });

  $routeProvider.otherwise({
    redirectTo: '/'
  });
}]);

file: index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title></title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js" charset="utf-8"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-route.min.js" charset="utf-8"></script>
  <script src="app.js" charset="utf-8"></script>
  <base href="/" target="_blank">
</head>
<body ng-app="MyApp">
  <h1>Vets and Pets!</h1>
  <nav>
    <ul>
      <li><a href="/">Home (/)</a></li>
      <li><a href="/contact">Contact</a></li>
      <li><a href="/about">About</a></li>
      <li><a href="/pricing">Pricing</a></li>
      <li><a href="/joke">Joke of the Day</a></li>
      <li><a href="/all">Contact, Pricing and Joke!</a></li>
      <li><a href="/badurl">/badurl</a>, redirects to default home page (/)</li>
    </ul>

    <h3>Pets</h3>
    <ul>
      <li><a href="/pets/cats">Cats</a></li>
      <li><a href="/pets/dogs">Dogs</a></li>
    </ul>
  </nav>

  <!-- This is the main view that will be filled in with view templates -->
  <main ng-view></main>

  <script type="text/ng-template" id='joke.html'>
    <h2>Pet Joke of the Day</h2>
    <p>{{ctrl.joke}}</p>
  </script>

</body>
</html>

If we include a link to another site using http, instead of https it skips the AngularJS Routes and simply opens a new tab/window.

We do this by just adding a standard link in the index.html

<li><a href="http://www.google.com">google</a></li>

Final code

Here are our main two files, index.html and app.js. The partial files can be found in instructor_examples/routing

file: index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title></title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js" charset="utf-8"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-route.min.js" charset="utf-8"></script>
  <script src="app.js" charset="utf-8"></script>
  <base href="/" target="_blank">
</head>
<body ng-app="MyApp">
  <h1>Vets and Pets!</h1>
  <nav>
    <ul>
      <li><a href="/">Home (/)</a></li>
      <li><a href="/contact">Contact</a></li>
      <li><a href="/about">About</a></li>
      <li><a href="/pricing">Pricing</a></li>
      <li><a href="/joke">Joke of the Day</a></li>
      <li><a href="/all">Contact, Pricing and Joke!</a></li>
      <li><a href="/badurl">/badurl</a>, redirects to default home page (/)</li>

      <!-- only this link skips the router because it starts with http -->
      <li><a href="http://www.google.com">google</a> loads external site</li>
    </ul>

    <h3>Pets</h3>
    <ul>
      <li><a href="/pets/cats">Cats</a></li>
      <li><a href="/pets/dogs">Dogs</a></li>
    </ul>
  </nav>

  <!-- This is the main view that will be filled in with view templates -->
  <main ng-view></main>

  <script type="text/ng-template" id='joke.html'>
    <h2>Pet Joke of the Day</h2>
    <p>{{ctrl.joke}}</p>
  </script>

</body>
</html>

file: app.js

const app = angular.module('MyApp', ['ngRoute']);

app.controller('ContactController', function () {
  this.phone = '555-1212';
});

app.controller('PetController', ['$routeParams', function ($routeParams) {
  // access :id from url
  this.id = $routeParams.id;
}]);

app.controller('JokeController', ['$route', function () {
  const jokes = ["Why don't cats like dogs?",
    "What is the difference between a duck?"];

  //you can access variables created in the when action like this
  this.joke = jokes[0];
}]);

app.controller('PricingController', ['$route', function ($route) {
  this.price = $route.current.price;
}]);

app.controller('AllController', ['$route', function () {
  const jokes = ["Why don't cats like dogs?",
    "What is the difference between a duck?"];

  this.joke = jokes[1];
  this.phone = '555-1212';
  this.price = '$1 billion dollars';
}]);

app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
  // Enables Push State
  $locationProvider.html5Mode({ enabled: true });

  $routeProvider.when('/contact', {
    templateUrl: 'contact.html',
    controller: 'ContactController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/about', {
    templateUrl: 'about.html'
    // We don't need to specify a controller, because there's no data
  });

  // when http://localhost:3000/url1/:id - :id is a param just like in express
  $routeProvider.when('/pets/:id', {
    templateUrl: 'pets.html',
    controller: 'PetController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/pricing', {
    templateUrl: 'pricing.html',
    controller: 'PricingController',
    controllerAs: 'ctrl',
    price: '$1 trillion dollars'
  });

  $routeProvider.when('/joke', {
    templateUrl: 'joke.html',
    controller: 'JokeController',
    controllerAs: 'ctrl'
  });

  $routeProvider.when('/all', {
    templateUrl: 'all.html',
    controller: 'AllController',
    controllerAs: 'ctrl'
  });

  $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
  });
}]);