Angular 2: Passing Data to Routes

This blog was updated on July 26, 2016. The RC.5 version of the code is available at https://github.com/Farata/angular2typescript/tree/master/chapter3/router_samples

In the previous blog I did a high-level overview of Angular 2 router and wrote a basic routing application that demonstrated displaying different components in a predefined area of the window. We often need not only display a component, but also pass some data to it. For example, if we navigate from the Home to the Product Detail route we need to pass the product ID to the destination route. The destination route can access passed parameters using the class ActivatedRoute, which knows its URL segment, the outlet, and allows to access parameters passed to this route. I’ll illustrate the use of both classes by modifying the app from the previous blog.

Extracting parameters from ActivatedRoute

When the user navigates to the ProductDetail route, we need to pass the product ID to this route to display details of the particular product. Let’s modify the code of the application from the previous blog so the RootComponent can pass the product ID to ProductDetailComponent. The new version of this component will be called `ProductDetailComponentParam`, and Angular will inject the object of type ActivatedRoute into this component. The ActivatedRoute object will contain the information about the component loaded into outlet.

import {Component} from '@angular/core';
import {ActivatedRoute} from '@angular/router';

@Component({
  selector: 'product',
  template: `<h1 class="product">Product Detail for Product: {{productID}}</h1>`, // 1
  styles: ['.product {background: cyan}']
})
export class ProductDetailComponentParam {
  productID: string;

  constructor(route: ActivatedRoute) { // 2
    this.productID = route.snapshot.params['id']; // 3
  }
}
\

1. Display the received product ID using binding.

2. The constructor of this component requests Angular to inject the object ActivatedRoute.

3. Get the value of the parameter named id and assign it to the class variable productID, which is used in template via binding.

NOTE: In the above code sample we’ve used the property snapshot of ActivatedRoute to get the values of the parameter passed to the route. The snapshot` property represents a component loaded into the outlet at a particular moment of time. The class ActivatedRoute has another property called params of type Observable, which allows a component that’s loaded by the router to subscribe to the incoming parameters that may be changing over time. See this blog for details.

The ActivatedRoute object will contain all parameters that are being passed to this component. If you write your Angular application in TypeScript, you just need to declare the constructor’s argument specifying its type, and Angular will know how to instantiate and inject this object.

In the next code sample we’ll change the configuration of the product route and routerLink to ensure that the value of the product ID will be passed to the component ProdutDetailComponentParam if the user choses to go this route.

import {bootstrap} from '@angular/platform-browser-dynamic';
import {Component} from '@angular/core';
import {LocationStrategy, HashLocationStrategy} from '@angular/common';
import {provideRouter, ROUTER_DIRECTIVES} from '@angular/router';

import {HomeComponent} from './components/home';
import {ProductDetailComponentParam} from './components/product-param';

@Component({
    selector: 'basic-routing',
    directives: [ROUTER_DIRECTIVES],
    template: `
        <a [routerLink]="['./']">Home</a>
        <a [routerLink]="['./product', 1234]">Product Details</a> // 1
        <router-outlet></router-outlet>
    `
})
class RootComponent {}

bootstrap(RootComponent, [
    provideRouter([
        {path: '',            component: HomeComponent},
        {path: 'product/:id', component: ProductDetailComponentParam} // 2
    ]),
    {provide: LocationStrategy, useClass: HashLocationStrategy}
]);

1. This time there are two elements in the array given to routerLink: the path that the route starts with and the number that represents product ID.

2. The path property has an additional URL segment /:id.

The routerLink property for the “Product Details” link is initialized with a two-element array. The elements of the array build up the path specified in the routes configuration given to provideRouter(). The first element of the array represents the static part of the route’s path: /product. The second element represents the variable part of the path, i.e. /:id.

For simplicity we’ve hard-coded the ID to be 1234, but if the class RootComponent had a variable productID pointing at the corresponding object, we could have written { productID} instead of 1234. For the product detail route Angular will construct the URL segment /product/1234.

The following screenshot shows how the product detail view will be rendered in the browser:

ch4_basic_routing_product_detail_param

Note the URL. Angular router replaced the path /product/:id with /product/1234.

Let’s review the steps that Angular performed under the hood for rendering the main page of the application:

1. Check the the content of each routerLink to find the corresponding routes configurations.

2. Parse the URLs and replace the parameter names with actual values where specified.

3. Build the actual <a href=””> tags that the browser understands.

The next screenshot shows a snapshot of the home page of our application with the Chrome Dev Tools panel open.

ch4_basic_routing_product_detail_href

Since the path property of the configured Home route had an empty string, Angular didn’t add anything to the base URL of the page. But the anchor under Product Details link is already converted into a regular HTML tag. When the user clicks on the Product Details link, the router will attach a hash sign and add /product/1234 to the base URL so the absolute URL of the Product Detail view will become http://localhost:8080/#/product/1234.

Passing static data to a route

While most of the times parent components will be passing data to their children, Angular also offers a mechanism to pass additional data to components at the time of the route configuration. For example, besides the dynamic data like product ID we may need to pass a flag indicating if the application runs in production environment or not. This can be done by using the data property of your route configuration. For example, our route for the product details can be configured as follows:

{path: 'product/:id', component: ProductDetailComponentParam ,
                      data: [{isProd: true}]}

The data property can contain an array of arbitrary string key-values pairs. When the router will open ProductDetailComponentParam the data value will be located in the data property of the ActivatedRoute.snapshot:

export class ProductDetailComponentParam {
  productID: string;
  isProdEnvironment: string;

  constructor(route: ActivatedRoute) {
    this.productID = route.snapshot.params['id'];

    this.isProdEnvironment = route.snapshot.data[0]['isProd'];
  }
}

Passing data to a route via the data property is not an alternative to configuring parameters in the path property as in path: ‘product/:id’ but can come handy when you need to pass some data to a route during the configuration phase, e.g. is it a production or QA environment.

If you’re interested in learning Angular 2 in depth, enroll into one of our workshops. We’ll run the next public training   online starting from September 11, 2016. I can also deliver this workshop privately for your organization (send an email to training@faratasystems.com).

37 thoughts on “Angular 2: Passing Data to Routes

  1. Updated the code and the plunk to run in Alpha 52 (routerLink instead of router-link and imports for Component and bootstrap).

  2. Nice read, but how would it be possible to have RouteData dynamic?
    i.e. to add some dynamic data acquired in the HomeComponent to RouteData so that the ProductDetailComponentParam can access it.

    1. RouteData is not intended to be used for passing dinamic data to the route. Use RouteParams for this. In my example I’ve been passing 1234 to the ProductDetailComponent, but this could as well be a bound variable productId that was somehow acquired in the HomeComponent.

      1. Hello. I am trying to get params since a page that is the parent route, where page are loaded. But when a page route another pages, it can’t get the params passed to it. Only with RouteData this king of page can get the params passed. How could i do the params to the page where another pages are loaded with and it could get these params ?

    1. If these components are located within the same route, you can bind the data from the parent to child using @Input() parameters.
      To arrang binding between siblings within the same route you need to implement the mediator design pattern using @Input() and @Output() parameters, which is described in chapter 6 of our angular book.

      Cross-route binding via @Input() params is not supported at this point.

  3. if ,RouteData is not intended to be used for passing dynamic data to the route then whats the alternate for passing data via routing without showing in the URL(path) ? because i found only RouteData to send data without showing in the URL.

    1. This is a client side navigation, and the whole idea is to assign unique URLs to different views of a single-page app. Why would you want to hide the URL?

  4. say you have product/1234/details, product/1234/settings and product/1234/stocks etc… do you think there is a way for angular to handle this kind of situation? I mean how would you pass the id down to the child pages?

    1. Yes, Angular supports child routes. You can configure the routes details and stocks by using @RouteConfig in the component that renders product/1234/.

      1. To elaborate on dbaroy comment, Say I have a top level route /product/:product_id, component: ProductComponent
        And then in ProductComponent I have another RouteConfig setting up {/details, component: DetailsComponent}

        ProductComponent can fetch the product_id out of the RouteParams, but can not pass this to DetailsComponent since it is rendered through the ProductComponent’s router-outlet. How then can I read the :product_id in the DetailsComponent?

        I could just make all the subroutes into top-level routes, i.e. in my root component have /product/:product_id/details, but I would like to avoid making all top level routes in order to avoid having to have all these components require a common layout.

        1. You could get parent params, and parent of parent params too, by using Injector
          like this:
          constructor(private _issueService: IssueService, private _injector: Injector) {}

          getIssues() {
          let id = this._injector.parent.parent.get(RouteParams).get(‘id’);
          this._issueService.getIssues(id).then(issues => this.issues = issues);
          }

  5. @Claudio Tormen

    It’s not a good idea to do parent.parent… This makes tightly coupled components.

    You can pass any parameters to any components by having an injectable service that holds the sate you’re interested in. Then inject it into any components regardless of how they are rendered – with or without the router outlet.

    Technically, it’s an implementation of the Mediator design pattern via dependency injection.

    1. Hey Yakov,
      Thanks for the article.

      I’m also wondering like @Max how do you pass params through the URL chain.
      I saw that you can set a property when you’re doing: `this.router.navigate([PARAM], { preserveQueryParams: true });` But if you’re building your navigation tree in the route file there’s no option to declare it there.
      Yeah, I guess you can use a service for that but isn’t there a better way today? (RC6).
      Cheers.

      1. I’m not sure why you need to declare it in the route config file. You can do this:

        router.navigate('/fragment1/123/fragment2/456',
        {preserveQueryParams: true, preserveFragment: true})

        or this:

        <a [routerLink]="/fragment1/123/fragment2/456" preserveQueryParams preserveFragment>
        1. Because that’s not my case.
          Let’s say that my URL will look like: http://www.myapp.com/123/dashboard/items.
          123 is the :id that I want to pass. The dashboard is another module that receive this ID. I want to pass this ID to the items module as well.
          If I try to use:

          this.route.snapshot.params['id']

          in my items module it won’t work, only in the dashboard module since params won’t pass through.
          Currently I’m using a service for url params but I wish that there was a better way.
          Is that make sense now?

        2. Well, I might be confusing things here.
          I’m talking about route parameters and not query params.

        3. In this case just use child routes:

          const routes: Routes = [
              {path: '',            component: HomeComponent},
              {path: 'product/:id', component: ProductDetailComponent,
                  children: [
                    {path: '',           component: ProductDescriptionComponent},
                    {path: 'seller/:id', component: SellerInfoComponent}
                  ]}
          ];
          

          When you’ll navigate to the SellerInfoComponent, the URL will include both product and seller’s IDS.
          The working code sample is here:
          https://github.com/Farata/angular2typescript/blob/master/chapter3/router_samples/app/main-child.ts

    1. Sure you have 🙂 Your article is great!
      What was missing, is the access to the route params of all the parents.
      It appears to be just a small addition, to what you have already wrote:

      route.snapshot.**parent**.params['id'];

      I think that it’s worth mentioning in this post for other people who look for this resolution.

      1. Agree, this is useful, and I should include in in the blog. I’ll also include a code snippet showing how to subscribe to the changing params.

  6. So, this is my dilemma which I can not find an answer. I want to use the same component which is rendered via a d routerLink in my html from my menu but I want to pass into the component a different parameter value. see snippet:

    People

    People

    I don’t even know if this is valid code however as you can see I would like to select ether one of these but pass in that parameter value into the .ts file and be able to display something like “These are all Male People” or “These are all Female People” in my html.

  7. THANK YOU, this explanation is so detailed. I’ve read official documentation, a lot of blogs until found yours and it’s so clarified to me a router mechanism and passing data between components. THANK YOU, maybe I will buy your book.

  8. hey, i am facing a problem. The issue is that the data section of my route has a ‘title’ field, which is used to show the page title in a breadcrumb. Now suppose i have a list of products and the breadcrumb title is /products. when i select a product, currently the breadcrumb displays ‘/products/product detail’ because the title is ‘product detail’. however i want the actual product name to be displayed in breadcrumb, that is, dynamically set the route title. how can i do that?

  9. I have different states in my routing module.In angular 1.5 I was using a custom variable “accessValues” to check whether the user has access to a particular state.I wan to do something similar to this in angular 2 .How is it possible?How to pass “accessValues” to my states and check the value while state change.

    Angular 1.5 code :
    .state(‘home’, {
    url: ‘/home’,
    templateUrl: ‘modules/home/views/home.html’,
    controller: ‘HomeController as vm’,
    accessValues: [‘View Home Page’]
    })

  10. Can we post data to an angular component like how we post data to a service/servlet/jsp/.aspx page? If so how to read that data?

Leave a comment