fbpx Skip to content

Aquent | DEV6

Angular 2: Component Router – Part 2: Dynamic URLs

Written by: James McGeachie

In part 1 we looked into how to implement simple static routing with the Angular 2 Component router. Now we’re going to cover how to handle dynamic routing. Note that with the new Angular 2 Release Candidate 5 (RC5) there have been some changes vs. part 1 and we’ll highlight these as we go along. Our examples are once again in TypeScript.

Components

We previously described our small vehicles app as having 3 components: The app component, a home component and a vehicles component. To illustrate dynamic routing we’re going to introduce another component named Vehicle Information, which we’ll load for a specific vehicle. Before we look at that component, let’s prepare our vehicles component so we can click to navigate to individual vehicles.

Vehicles Component

Example filename: vehicles.component.ts

import { Router } from ‘@angular/router';


@Component({

    selector: ‘vehicles‘,

    template: `

    <ul>

        <li *ngFor="let vehicle of vehicles"

        (click)=“goToVehicle(vehicle.id)">

        {{vehicle.name}}

        </li>

    </ul>`

})

Note that in this example, within our template string we iterate through a list of vehicles. For each of them we bind a click event calling a function ‘goToVehicle(vehicle.id)’. This click is the action that will navigate the user to another state.

Within the same file assume we have a controller for the component. In that controller, we will inject the router that we imported at the top of the file.

constructor(private router: Router) {}

Now, let’s look at the function we called from the template.

goToVehicle(id: number) {

    this.router.navigate([‘/vehicle-information', id]);

}

This function takes one argument (vehicle id, passed from the template) and calls the router’s ‘navigate’ function, passing in an array containing 2 items:

  • The path
  • The id of the vehicle

So now the question is – how does the router’s navigate function know what to do with this array? Let’s look at App Routing to find out.

App Routing

Example filename: app.routing.ts

First, let’s go over some quick changes to the router configuration that were introduced in RC5.

Previously in RC4 we had the following import statement:

import { provideRouter, RouterConfig }  from ‘@angular/router’;

With RC5, we now import the following:

import { ModuleWithProviders } from '@angular/core';

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

Our configuration block is very similar, except now it’s of type ‘Routes’ rather than ‘RouterConfig’. Let’s look at what we need to add for dynamic routes:

const routes: Routes = [

    {

      path: 'vehicles',

      component: VehiclesComponent

    },

    {

      path: ‘home’,

      component: HomeComponent

    },

    {

      path: ‘vehicle-information/:id’,

      component: VehicleInformationComponent

    }

];

Our new object once again has a path property and a component property, but now it has something new – a colon followed by “id”. Think of “id” like a variable that can hold a value. The colon before ‘id’ in the path specifies that this is the dynamic section of the path, so the actual value of “id” is expected to change. The component line remains the same, we point to the specific component that we want to load with this path.

With RC5 a change has been made to how we bootstrap the app and provide these routes, but before we get into that, let’s complete our understanding of dynamic routing by looking at our new vehicle information component.

Vehicle Information Component

Example filename: vehicle-information.component.ts

import { Component, OnInit } from ‘@angular/core’;

import { Component, OnInit } from '@angular/core';

import { ActivatedRoute, Params } from '@angular/router';

 

@Component({

  selector: ‘vehicle-information',

  template: `

  <div *ngIf="vehicle">

    <h2>{{vehicle.model}}</h2>

    <h3>{{vehicle.manufacturer}}</h3>

    <h3>{{vehicle.year}}</h3>

 </div>

  `

})

 

export class VehicleInformationComponent implements OnInit {

 

  constructor(

    private route: ActivatedRoute

  ) {}

 

  ngOnInit(): void {

    this.route.params.forEach((params: Params) => {

      let id = +params['id'];

      this.vehicle = this.getVehicle(id);

    });

  }

 

  getVehicle(id): Vehicle {

    // implementation returns a vehicle object from a collection.

  }

 

}

In the above code, we will assume we already have an implementation of getVehicle that synchronously returns the vehicle object that matches the id passed, and that we have a vehicle class that defines 3 properties (model, manufacturer, year). What we’re interested in for this example is how we retrieve this object based on the id in our path, which is happening in our ngOnInit function. Let’s look at the function body.

this.route.params.forEach((params: Params) => {

  let id = +params['id'];

  this.vehicle = this.getVehicle(id);

});

‘this.route’ is the activatedRoute, which we injected in the constructor and have access to from the import statement at the top of the code. What we’re doing here is iterating through all parameters that have been given. In this case we only have one (id). Within the loop we convert the id from params to a number with the + operator. Then we assign ‘this.vehicle’ to be the result of our getVehicle(id) function, which will be the vehicle object for that vehicle.

Now that we’ve set the vehicle object, our template will be displaying the properties for that specific vehicle! We have achieved our desired effect.

To finish off our understanding of routing, let’s look at how we bootstrap our app with our routes available using the new RC5 NgModule approach to modularity.

Bootstrap with NgModule

In the Router Config file we built earlier on (app.routing.ts), we left out one new line that’s part of the new approach:

export const routing: ModuleWithProviders = RouterModule.forRoot(routes);

Here we export our own ‘routing’ module by calling the Angular RouterModule’s forRoot function and passing in our routes array from earlier.

Where do we use this ‘routing’ module? Within our NgModule decorator. The following will be in another file, app.module.ts

import { NgModule } from ‘@angular/core';

import { BrowserModule } from '@angular/platform-browser';

 

import { routing } from './app.routing';

 

import { AppComponent }  from './app.component';

import { HomeComponent } from './home.component';

import { VehiclesComponent } from './vehicles.component';

import { VehicleInformationComponent } from './vehicle-information.component';

 

@NgModule({

  imports: [

    BrowserModule,

    routing

  ],

  declarations: [

    AppComponent,

    HomeComponent,

    VehicleInformationComponent,

    VehiclesComponent

  ],

  bootstrap: [ AppComponent ]

})

export class AppModule { }

Note that in this file we’re importing all our components throughout this app and then declaring them in our new @NgModule decorator. This replaces the requirement to specify the directives array in each component decorator. We would also be importing all services if we had any too and they would be in a providers array. However, what we care about for our example is the imports array – here we include our ‘routing’ module, which we imported at the top of the file. Now our specified routes will be available when the app bootstraps. The bootstrap function itself will now be in your main.js file and will look like this:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app.module';


platformBrowserDynamic().bootstrapModule(AppModule);

We now know how to add dynamic routing to our app using the RC5 approach to routing and modularity! Check back for more in-depth Angular 2 content in the future.

Sign up for our Angular Course

Learn the most recent version and start building your own Angular apps

view course details