Angular Router

What is the Router?

  • Enables routing from one view to another

  • Assigns a top level/parent component to a url

  • Can be navigated through links or programmatically

  • Logs activity with browser history journal as to support forward and back buttons

  • Small applications might not need any routing

  • Angular-CLI does not generate routes by default

  • Useng new my-app --routing true or ng g m my-module --routing true to generate routes

If you're not sure you will absolutely need routing, start developing your application without it. You can always add it later.

Router Components

Importing the Router

Routing can be added directly to the app.module if really small applications, or you can move it to a separate app-routing module to help keep things organized. You will need to import Routes and RouterModule into your module. Any routes that are defined with will need to be registered with the RouterModule.

app-routing.module.ts
import { NgModule } from "@angular/core";
import { Routes, RouterModule } from "@angular/router";

const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

Base href

The Angular-CLI will define the base href as '/', but this can be changed in the index.html. If your application needs to pre-pend the url with a base name, you can set it in the index.html.

<base href="/my-app">

// url now becomes
localhost:4200/my-app/

Router Outlet

The app.component is the starting component for your application and will define the layout for your application. In the example below, every page will have a navigation header and a footer. As we change routes/views in our application, they will be rendered in the router-outlet tags. Router outlets render the currently routed component.

Defining Routes

app.component.html
<nav>
  // my navigations links
</nav>

<router-outlet></router-outlet>

<footer>
  // my footer stuff
</footer>

Routes can be defined in your app-routing module or feature modules. The basic syntax for a route is `{path: 'some-path', component: MyComponent}`. In the example below, we have created three routes for two different views. The 3rd route defines a default router that if the route is empty it will redirect to the login route. The last route is a wildcard route that says if there is no match, then render a page not found (404) component.

pathMatch - can be set to full or prefix. Prefix tells the router to match the redirect route when the remaining URL begins with the redirect route's prefix path. Since '' will match every url, we need to use pathMatch set to full for it to work correctly. This technique creates a default route.

You can also pass along static data to a route using the data attribute.

Notice the second route has a :id in it's path? This tells the router that we will be expecting a dynamic parameter , like an id, in our route. We can read those parameters later in our component.

The easiest way to allow users to navigate to different routes is to provide them with links they can click to navigate through your application. Although it might be tempting to manually build the links yourself, Angular provides a routerLink directive that will do the heavy lifting for you.

<a [routerLink]="['/login']">Login</a>&nbsp;&nbsp;
<a [routerLink]="['/profile-editor', id]">Profile Editor</a>

<br>
<br>
<router-outlet></router-outlet>

When the user clicks on the Profile Editor link, the router will look up the route 'profile-editor' and render the associated ProfileEditorComponent into the router-outlet html tags.

Programatic Navigation

Sometimes we need the application to redirect a user to a different view, such as when they successfully save some data, we may want to send them to a different page immediately after. Angular provides the Router service to make navigation simple.

Passing Data in Routes

Data can be passed from one view to another using route parameters. This is commonly done when a user clicks a record from a list of records so they can inspect the data more closely or to edit it. The detail view component will need the id of the selected record, so we can pass it as a route parameter.

{
  path: 'profile-editor/:id',
  component: ProfileEditorComponent
}

// we will need to pass an id to profile-editor

Route Parameters

We can pass the route parameters using a routerLink or programmatically using the router object.

<a [routerLink]="['/profile-editor', profileId]">Profile Editor</a>
// the id assigned to the profileId varible will get added to the route
// example route:   /profile-editor/123

// programmatically
let profileId = 123;

this.router.navigate(['/profile-editor', profileId]);

// creates the same route:   /profile-editor/123

Reading Route Params

We have just learned how to pass parameters from one component to another, but now we need to learn how to read the passed parameters. We can read them synchronously or asynchronously, depending on how we want to approach it using the ActivatedRoute service.

constructor(private route: ActivatedRoute) {}

ngOnInit() {
    // syncronous
    let id = +this.route.snapshot.paramMap['id'];
    this.loadProfile(id);

    // or 

    // asyncronous
    this.route.paramMap.subscribe(params => {
      let id = +params.get('id');
      this.loadProfile(id);
    });
  }

Why choose synchronous vs asynchronous? In our synchronous example, we will read the id from the paramMap and load the profile. If the id changes, we won't get an update. The second approach using the asynchronous method will load the profile again if a new id is emitted.

Subscribing to Observables: The subscriptions don't automatically get cleaned up and destroyed by themselves. It's your job to assign a variable to the subscription and destroy it when your component is destroyed.

Query Parameters

Query parameters are a great way to pass optional data. Unlike route parameters, they won't be part of the route matching process, so if they aren't passed over, they won't change which route is selected. These types of parameters are great for passing details about the state of your application, like data paging. This data might include page size, current page, filters, and sort columns. Passing and reading query parameters is very similar

<a [routerLink]="['/users']" [queryParams]="{page: 1, size:25}">Profile Editor</a>

// the query string parameters are added to the route
// example route:   /users?page=1;size=25

// programmatically
let profileId = 123;

this.router.navigate(['/profile-editor', 1], {queryParams: {page: 1, size:25}});

// creates the route:   /profile-editor/1?page=1&size=25
constructor(private route: ActivatedRoute) {}

ngOnInit() {
    // syncronous
    let params = this.route.snapshot.queryParamMap;
    let page = +params['page'],
         size = +params['size'];

    // or 

    // asyncronous
    this.route.queryParamMap.subscribe(params => {
      let page = +params.get('page'),
          size = +params.get('size');
    });
  }

Prefer paramMap over params and queryParamMap over queryParams. params and queryParams are deprecated and will be removed in future versions.

Child Routes

Some routes should only be available after navigating to a starting point page and should be defined as child routes. In this example, the user could see a list of products, select a product, and then they would have the choice of seeing the summary or details of the selected product.

export const routes: Routes = [
  { path: 'products', component: ProductsComponent },
  { path: 'product/:id', component: ProductComponent,
    children: [
      { path: '', redirectTo: 'summary', pathMatch: 'full' },
      { path: 'summary', component: ProductSummaryComponent },
      { path: 'details', component: ProductDetailComponent }
    ]
  }
];

The parent route component will have it's own router outlet and can render it's child components within it's template.

<p>Product Details: {{id}}</p>
<!-- Product information -->
<nav>
  <a [routerLink]="['summary']">Summary</a>
  <a [routerLink]="['details']">Details</a>
</nav>
<router-outlet></router-outlet>
<!-- Overview & Specs components get added here by the router -->

Child Router Links - notice how there is no reference of the parent component in the defined routes or the router links? Since they are defined as children routes of the product component, Angular already knows how to handle them without references to the parent.

Reading Parent Params or QueryParams

The parent route and query parameters can be read by the child using the parent property of the ActivateRoute service.

this.router.parent.snapshot.paramMap['id'];

this.router.parent.snapshot.queryParamMap['id'];

Multiple Router Outlets

In some scenarios you may want more than one router outlet where you would have a primary outlet and an auxiliary outlet. A common scenario use case for this would be if you wanted a sidebar to be displayed that isn't effected by route changes in the primary outlet.

// default outlet
<router-outlet></router-outlet>

// sidebar outlet
<router-outlet name="sidebar"></router-outlet>

Now that we have two different router outlets, we need to define routes for the two different outlets.

The last two routes have the outlet set to sidebar to tell Angular to render it in a different router outlet. This technique allows you to create more advanced user interfaces.

<nav>
  <a [routerLink]="['/dashboard']">Dashboard</a>&nbsp;
  <a [routerLink]="['/products']">Products</a>&nbsp;
  <a [routerLink]="[{ outlets: { 'sidebar': ['sidebar'] } }]">Sidebar</a>&nbsp;
  <a [routerLink]="[{ outlets: { 'sidebar': ['sidebar2'] } }]">
    Show Sidebar2</a>
</nav>

// sidebar.component.ts
<p>
  sidebar works!
  <a [routerLink]="['', { outlets: { 'sidebar': ['sidebar2']}}]">Show Sidebar2</a>
</p>

Summary

  • What is the router

  • How to import the router and set base Urls

  • Router Outlet

  • How to define routes

  • Creating router links and programmatically navigating

  • How to pass and read data using route and query parameters

  • Child routes

  • Multiple router outlets

Last updated