17
votes

In the latest version of @angular/router 3.0.0-rc.1 The way you get the parameters from a URL/route changed.

Based on this documentation you should be able to get the params by subscribing to the params but in my case it seems that is not working.

What I want to achieve is to get params into my parent component FROM child routes.

For example let's say this is my routes:

const routes: Routes = [
  {
    path: 'parent',
    component: ParentComponent,
    pathMatch: 'prefix',
    children: [
      {
        path: ':id',
        component: ChildComponent
      }
    ]
  }
];

I want to get the id parameter and use it in my ParentComponent. So I'm trying like this:

export class ParentComponent implements OnInit {

  sub: any;
  constructor(
    private route: ActivatedRoute) {
    this.route = route;
   }

  ngOnInit() {

    this.sub = this.route.params.subscribe(params => {
     let id = params['id'];
     console.log(id);
   });

  }

}

Like this I'm getting:

Undefined

What am I missing here?

6

6 Answers

37
votes

The ActivatedRoute has getters to access its parent/child route information.

In order to access the first child route from the parent, you would use:

this.route.firstChild.params

If you wanted all the child routes you would use the children property. This returns an array of ActivatedRoute

this.route.children

If you were in a child route and needed parameters from the parent:

this.route.parent.params

10
votes

It's super easy to get the child parameters via ActivatedRoute, however you can quickly run into issues if you change the current child.

First a couple notes:

  • activatedRoute.firstChild property is an ActivatedRoute instance and NOT an observable.
  • Also note that activatedRoute.paramMap is an observable.

Here's how you can run into problems:

  • Consider a component with two child routes - and corresponding components - where only one is shown at a time (for example a tabbed interface).
  • If you access firstChild.paramMap inside your ngOnInit handler you'll be able to subscribe to it and monitor the parameters as they change. This will look and work just fine.
  • If you switch to the second tab and then back to the first everything will break. This is because you originally subscribed paramMap on an ActivatedRoute instance that doesn't exist anymore.
  • Important: This is only a problem if you're trying to access the child's paramMap. If you're trying to access your 'own' paramMap, or the child component also injects paramMap then everything will be fine.

The cleanest solution to this I've found is as follows:

Note: First inject router: Router in addition to route: ActivatedRoute.

const firstChildParams$ = this.router.events.pipe(filter(e => e instanceof NavigationEnd),
                                                  startWith(undefined),
                                                  switchMap(e => this.route.firstChild!.paramMap));

What this does is listens for only NavigationEnd events, after which a new firstChild will be available. By using switchMap you don't need to worry about unsubscribing to old redundant maps. Also not that the actual value of the navigation event is never used, and startWith is needed to be able to immediately handle parameters the first time.

I've been collecting helpful things like this into a RouterHelperService that I can inject anywhere, and not have to remember all this craziness.


Some day I may suggest that there should be an observable equivalent of firstChild, then this wouldn't be a problem. However it would perhaps introduce even more confusion in other scenarios!

5
votes

The child parameters are associated/stored with the child ActivatedRoute. They are not available on the parent's ActivatedRoute. So you first need to get the child's ActivatedRoute using getter firstChild or children.

Then, the parent can either subscribe to child parameter changes:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute }               from '@angular/router';
import { Subscription }                 from 'rxjs/Subscription';

export class ParentComponent implements OnInit, OnDestroy {
   private sub: Subscription;
   constructor(private route: ActivatedRoute) {}
   ngOnInit() {
      this.sub = this.route.firstChild.params.subscribe(
        params => console.log(params.id));
   }
   ngOnDestroy() {
      this.sub.unsubscribe();
   }
}

or it can get a snapshot of the child parameters:

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

export class ParentComponent {
   constructor(private route: ActivatedRoute) {}
   someMethod() {
      console.log(this.route.firstChild.snapshot.params.id);
   }
}

If you want to get all of the children (e.g., if you have multiple outlets), use ActivatedRoute.children or ActivatedRouteSnapshot.children to get an array of child ActivatedRoutes or child ActivatedRouteShapshots.

1
votes

By using this.activatedRoute.snapshot.firstChild.params

1
votes
  this.router.events.subscribe(event => {
            if (event instanceof RoutesRecognized) {
                // let strId = event.state.root.firstChild.children[0].data;
                let a = event.state.root.firstChild.children.map((route) => {
                    while (route.firstChild) {
                        route = route.firstChild;
                    }

                    return route;
                });
                console.log('Title', a[0].data.title);
                this.meta.updateTitle(a[0].data.title);
            }
            if (event instanceof NavigationEnd) {
                (<any>window).gtag('config', this.googleAnalyticsCode, {
                    'page_title' : this.titleService.getTitle(),
                    'page_path': event.urlAfterRedirects
                });
            }
        });
1
votes
this.route.snapshot.paramMap.get('id');