Angular 7: Routing

In web development, routing means splitting the application into different areas usually based on rules that are derived from the current URL in the browser.

For instance, if we visit the / path of a website, we may be visiting the home route of that website. Or if we visit /about we want to render the “about page”, and so on.

Why Do We Need Routing?

Defining routes in our application is useful because we can:

For example, imagine we are writing an inventory application similar to the one we described in previous chapters.

When we first visit the application, we might see a search form where we can enter a search term and get a list of products that match that term.

After that, we might click a given product to visit that product’s details page.

Because our app is client-side, it’s not technically required that we change the URL when we change “pages”. But it’s worth thinking about for a minute: what would be the consequences of using the same URL for all pages?

Or put in a positive light, routing lets us define a URL string that specifies where within our app a user should be.

In our inventory example we could determine a series of different routes for each activity, for instance:

The initial root URL could be represented by http://our-app/. When we visit this page, we could be redirected to our “home” route at http://our-app/home.

When accessing the ‘About Us’ area, the URL could become http://our-app/about. This way if we sent the URL http://our-app/about to another user they would see same page.

How client-side routing works

Perhaps you’ve written server-side routing code before (though, it isn’t necessary to complete this chapter). Generally with server-side routing, the HTTP request comes in and the server will render a different controller depending on the incoming URL.

For instance, with Express.js you might write something like this:

{lang=javascript,line-numbers=off}
var express = require(‘express’); var router = express.Router();

  // define the about route
router.get('/about', function(req, res) {
  res.send('About us');
});

Or with Ruby on Rails you might have:

{lang=ruby,line-numbers=off}
# routes.rb get ‘/about’, to: ‘pages#about’

  # PagesController.rb
class PagesController < ActionController::Base
  def about
    render
  end
end

The pattern varies per framework, but in both of these cases you have a server that accepts a request and routes to a controller and the controller runs a specific action, depending on the path and parameters.

Client-side routing is very similar in concept but different in implementation. With client-side routing we’re not necessarily making a request to the server on every URL change. With our Angular apps, we refer to them as “Single Page Apps” (SPA) because our server only gives us a single page and it’s our JavaScript that renders the different pages.

So how can we have different routes in our JavaScript code?

The beginning: using anchor tags

Client-side routing started out with a clever hack: Instead of using a normal server-side URL for a page in our SPA, we use the anchor tag as the client-side URL.

As you may already know, anchor tags were traditionally used to link directly to a place within the webpage and make the browser scroll all the way to where that anchor was defined. For instance, if we define an anchor tag in an HTML page:

{lang=html,line-numbers=off}
<h1>About</h1>

And we visited the URL http://something/#about, the browser would jump straight to that H1 tag that identified by the about anchor.

The clever move for client-side frameworks used for SPAs was to take the anchor tags and use them represent the routes within the app by formatting them as paths.

For example, the about route for an SPA would be something like http://something/#/about. This is what is known as hash-based routing.

What’s neat about this trick is that it looks like a “normal” URL because we’re starting our anchor with a slash (/about).

The evolution: HTML5 client-side routing

With the introduction of HTML5, browsers acquired the ability to programmatically create new browser history entries that change the displayed URL without the need for a new request.

This is achieved using the history.pushState method that exposes the browser’s navigational history to JavaScript.

So now, instead of relying on the anchor hack to navigate routes, modern frameworks can rely on pushState to perform history manipulation without reloads.

Angular 1 Note: This way of routing already works in Angular 1, but it needs to be explicitly enabled using $locationProvider.html5Mode(true).

In Angular, however, the HTML5 is the default mode. Later in this chapter we show how to change from HTML5 mode to the old anchor tag mode.

There’s two things you need to be aware of when using HTML5 mode routing, though

  1. Not all browsers support HTML5 mode routing, so if you need to support older browsers you might be stuck with hash-based routing for a while.
  2. The server has to support HTML5 based routing.

It may not be immediately clear why the server has to support HTML5 based-routing, we’ll talk more about why later in this chapter.

Writing our first routes

The Angular docs recommends using HTML5 mode routing. But due to the challenges mentioned in the previous section we will for simplicity be using hash based routing in our examples.

In Angular we configure routes by mapping paths to the component that will handle them.

Let’s create a small app that has multiple routes. On this sample application we will have 3 routes:

And when the user visits the root path (/#/), it will redirect to the home path.

Components of Angular routing

There are three main components that we use to configure routing in Angular:

Let’s look at each one more closely.

Imports

In order to use the router in Angular, we import constants from the @angular/router package:

    import {
      RouterModule,
      Routes
    } from '@angular/router';

Now we can define our router configuration.

Routes

To define routes for our application, create a Routes configuration and then use RouterModule.forRoot(routes) to provide our application with the dependencies necessary to use the router. First, let’s look at the routes definitions:

    const routes: Routes = [
      // basic routes
      { path: '', redirectTo: 'home', pathMatch: 'full' },
      { path: 'home', component: HomeComponent },
      { path: 'about', component: AboutComponent },
      { path: 'contact', component: ContactComponent },
      { path: 'contactus', redirectTo: 'contact' },
    
      // authentication demo
      { path: 'login', component: LoginComponent },
      {
        path: 'protected',
        component: ProtectedComponent,
        canActivate: [ LoggedInGuard ]
      },
    
      // nested
      {
        path: 'products',
        component: ProductsComponent,
        children: childRoutes
      }
    ];

Notice a few things about the routes:

We’ll dive into the details of each route in this chapter, but at a high-level, the goal of routes is to specify which component will handle a given path.

Redirections

When we use redirectTo on a route definition, it will tell the router that when we visit the path of the route, we want the browser to be redirected to another route.

In our sample code above, if we visit the root path at http://localhost:4200/#/, we’ll be redirected to the route home.

Another example is the contactus route:

      { path: 'contactus', redirectTo: 'contact' },

In this case, if we visit the URL http://localhost:4200/#/contactus, we’ll see that the browser redirects to /contact.

Sample Code The complete code for the examples in this section can be found in the routes/routing folder of the sample code. That folder contains a README.md, which gives instructions for building and running the project.

There are many different imports required for routing and we don’t list every single one in every code example below. However we do list the filename and line number from which almost every example is taken from. If you’re having trouble figuring out how to import a particular class, open up the code using your editor to see the entire code listing.

Try running the code while reading this section and feel free play around to get a deeper insight about how it all works.

Installing our Routes

Now that we have our Routes routes, we need to install it. To use the routes in our app we do two things to our NgModule:

  1. Import the RouterModule
  2. Install the routes using RouterModule.forRoot(routes) in the imports of our NgModule
 
This page is a preview of ng-book 2.
Get the rest of this chapter plus hundreds of pages Angular 7 instruction, 5 sample projects, a screencast, and more.

 

Ready to master Angular 7?

  • What if you could master the entire framework – with solid foundations – in less time without beating your head against a wall? Imagine how quickly you could work if you knew the best practices and the best tools?
  • Stop wasting your time searching and have everything you need to be productive in one, well-organized place, with complete examples to get your project up without needing to resort to endless hours of research.
  • You will learn what you need to know to work professionally with ng-book: The Complete Book on Angular 7 or get your money back.
Download the First Chapter (for free)