81

How do I get the RouteParams from a parent component?

App.ts:

@Component({
  ...
})

@RouteConfig([
  {path: '/', component: HomeComponent, as: 'Home'},
  {path: '/:username/...', component: ParentComponent, as: 'Parent'}
])

export class HomeComponent {
  ...
}

And then, in the ParentComponent, I can easily get my username param and set the child routes.

Parent.ts:

@Component({
  ...
})

@RouteConfig([
  { path: '/child-1', component: ChildOneComponent, as: 'ChildOne' },
  { path: '/child-2', component: ChildTwoComponent, as: 'ChildTwo' }
])

export class ParentComponent {

  public username: string;

  constructor(
    public params: RouteParams
  ) {
    this.username = params.get('username');
  }

  ...
}

But then, how can I get this same 'username' parameter in those child components? Doing the same trick as above, doesn't do it. Because those params are defined at the ProfileComponent or something??

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

  constructor(
    public params: RouteParams
  ) {
    this.username = params.get('username');
    // returns null
  }

  ...
}
Aico Klein Ovink
  • 1,647
  • 2
  • 14
  • 21
  • How about an input property on the children? E.g., in the parent template: ` ...` – Mark Rajcok Dec 28 '15 at 20:36
  • Would that also work on `...`? And is that the way to go then @MarkRajcok? – Aico Klein Ovink Dec 28 '15 at 21:06
  • I think you're asking if something like ` – Mark Rajcok Dec 28 '15 at 21:15
  • I'm sorry @MarkRajcok , I typed it wrong.. I meant ``, should I put an input on that. Because the child routes wil render there.. – Aico Klein Ovink Dec 28 '15 at 21:17
  • I forgot that ParentComponent is essentially a routing component, so there's no parent template that uses ``, so there's no place to put the `[username]` binding. Sorry, this was a bad/wrong suggestion on my part. – Mark Rajcok Dec 28 '15 at 21:23
  • 2
    There might be useful information https://github.com/angular/angular/issues/6204#issuecomment-173273143 – Günter Zöchbauer Jan 20 '16 at 17:51
  • As pointed out by @Lordking ng2 does not handle deeply nested routes. Now, going with the solution wherein one requests the parent (precisely the 2nd ancestor) injector, and getting the RouteParams from there seems to be an ugly but nevertheless a temporary workaround for the problem in hand. Shall update this post once a better solution is constructed for deeply nested parent child relationships. – darkdefender27 Jan 22 '16 at 17:52
  • For people interested in more than just parent's, but want all ancestors' params, see https://stackoverflow.com/a/42301718/4185989 – jmq Jan 26 '18 at 21:48
  • Starting from Angular 5 you can access parent params via paramsInheritanceStrategy No need to subscribe to parent route. See https://angular.io/api/router/ExtraOptions – Vugar Abdullayev Aug 06 '21 at 11:29

13 Answers13

74

UPDATE:

Now that Angular2 final was officially released, the correct way to do this is the following:

export class ChildComponent {

    private sub: any;

    private parentRouteId: number;

    constructor(private route: ActivatedRoute) { }

    ngOnInit() {
        this.sub = this.route.parent.params.subscribe(params => {
            this.parentRouteId = +params["id"];
        });
    }

    ngOnDestroy() {
        this.sub.unsubscribe();
    }
}

ORIGINAL:

Here is how i did it using the "@angular/router": "3.0.0-alpha.6" package:

export class ChildComponent {

    private sub: any;

    private parentRouteId: number;

    constructor(
        private router: Router,
        private route: ActivatedRoute) {
    }

    ngOnInit() {
        this.sub = this.router.routerState.parent(this.route).params.subscribe(params => {
            this.parentRouteId = +params["id"];
        });
    }

    ngOnDestroy() {
        this.sub.unsubscribe();
    }
}

In this example the route has the following format: /parent/:id/child/:childid

export const routes: RouterConfig = [
    {
        path: '/parent/:id',
        component: ParentComponent,
        children: [
            { path: '/child/:childid', component: ChildComponent }]
    }
];
Fábio Junqueira
  • 2,701
  • 21
  • 20
  • 2
    You need to call this in **ngOnInit** (as shown) not in the constructor as I foolishly tried to do at first. – Cameron Aug 05 '16 at 09:47
  • 2
    There's an alternative way since Angular 5.2 that doesn't require you to traverse `parent` 1+ times. See https://stackoverflow.com/a/48511516/4185989 Still worth considering the `subscribe` / `unsubscribe` pattern from this answer, though. – jmq Feb 13 '18 at 18:01
  • In Angular 6 `this.activatedRoute.parent.snapshot.params.someParam` – Tasnim Reza Jul 10 '18 at 14:56
  • Solution pointed to by @jmq is the best for angular 6 as well, we do not need to subscribe separately for parent id. – learning... Aug 01 '18 at 15:47
  • Perfect solution! Buy why I need to subscribe to get the parent params? The param is there already! ::thinking:: – moreirapontocom Apr 09 '19 at 21:41
  • what is the difference b/w this.route.parent.paramMap and this.route.parent.params? It happens that if changing params with paramMap the solution stops to work, any help? – Ievgen Oct 31 '19 at 02:02
10

You shouldn't try to use RouteParams in your ChildOneComponent.

Use RouteRegistry, instead!

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

  constructor(registry: RouteRegistry, location: Location) {
    route_registry.recognize(location.path(), []).then((instruction) => {
      console.log(instruction.component.params['username']);
    })
  }


  ...
}

UPDATE: As from this pull request (angular beta.9): https://github.com/angular/angular/pull/7163

You can now access to the current instruction without recognize(location.path(), []).

Example:

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

  constructor(_router: Router) {
    let instruction = _router.currentInstruction();
    this.username = instruction.component.params['username'];
  }

  ...
}

I haven't tried it, yet

Further details here:

https://github.com/angular/angular/blob/master/CHANGELOG.md#200-beta9-2016-03-09 https://angular.io/docs/ts/latest/api/router/Router-class.html

UPDATE 2: A small change as from angular 2.0.0.beta15:

Now currentInstruction is not a function anymore. Moreover, you have to load the root router. (thanks to @Lxrd-AJ for reporting)

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

  constructor(_router: Router) {
    let instruction = _router.root.currentInstruction;
    this.username = instruction.component.params['username'];
  }

  ...
}
ProGM
  • 6,949
  • 4
  • 33
  • 52
  • Is this the way it is supposed to be done for child routes? I'm also running into this issue where child routes cannot see routeparams of parent components. For example the route /users/:user_id/posts/:post_id, I cannot get the user_id from the posts component.... It seems hacky to me to have to use RouteRegistry. – mharris7190 Apr 08 '16 at 17:08
  • @mharris7190 I updated my answer. Starting from angular beta.9 you can get the current instruction directly from the Router component. – ProGM Apr 08 '16 at 18:46
  • Thanks for the update. I'm about to upgrade from beta.6 to beta.13 so I'll try it out after I do. – mharris7190 Apr 08 '16 at 22:36
  • 3
    A slight edit to this answer, Use `_router.root.currentInstruction.component.params['id']` . Emphasis on **root** as you now get the currentInstruction from the root router and not `_router`. PS: I am using `angular2.0.0-beta.15` – Lxrd-AJ Apr 26 '16 at 15:13
  • _router.root doesn't exist anymore. (I use Angular 2.4.7) – Eivind Gussiås Løkseth Apr 19 '17 at 20:14
7

As mentioned by Günter Zöchbauer, I used the comment at https://github.com/angular/angular/issues/6204#issuecomment-173273143 to address my problem. I used the Injector class from angular2/core to fetch the routeparams of the parent. Turns out angular 2 does not handle deeply nested routes. Maybe they'll add that in the future.

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);
}
Lordking
  • 1,413
  • 1
  • 13
  • 31
6

I found an ugly but working solution, by requesting the parent (precisely the 2nd ancestor) injector, and by getting the RouteParams from here.

Something like

@Component({
  ...
})
export class ChildOneComponent {
  public username: string;

  constructor(injector: Injector) {
    let params = injector.parent.parent.get(RouteParams);

    this.username = params.get('username');
  }
}
Yohan G.
  • 1,176
  • 1
  • 7
  • 7
4

RC5 + @angular/router": "3.0.0-rc.1 SOLUTION: It seems that this.router.routerState.queryParams has been deprecated. You can get the parent route params this way:

constructor(private activatedRoute: ActivatedRoute) {
}    

this.activatedRoute.parent.params.subscribe(
  (param: any) => {
    let userId = param['userId'];
    console.log(userId);
  });
Stephen Paul
  • 37,253
  • 15
  • 92
  • 74
2

You can take component of parent route inside of child component from injector and then get any from child component. In you case like this

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

  constructor(
    public params: RouteParams
    private _injector: Injector

  ) {
    var parentComponent = this._injector.get(ParentComponent)

    this.username = parentComponent.username;
    //or
    this.username = parentComponent.params.get('username');
  }

  ...
}
Danxil
  • 21
  • 1
2

Passing Injector instance to constructor in child component may not be good if you want to write unit tests for your code.

The easiest way to work around this is to have a service class in the parent component, in which you save your required params.

@Component({
    template: `<div><router-outlet></router-outlet></div>`,
    directives: [RouterOutlet],
    providers: [SomeServiceClass]
})
@RouteConfig([
    {path: "/", name: "IssueList", component: IssueListComponent, useAsDefault: true}
])
class IssueMountComponent {
    constructor(routeParams: RouteParams, someService: SomeServiceClass) {
        someService.id = routeParams.get('id');
    }
}

Then you just inject the same service to child components and access the params.

@Component({
    template: `some template here`
})
class IssueListComponent implements OnInit {
    issues: Issue[];
    constructor(private someService: SomeServiceClass) {}

    getIssues() {
        let id = this.someService.id;
        // do your magic here
    }

    ngOnInit() {
        this.getIssues();
    }
}

Note that you should scope such service to your parent component and its child components using "providers" in parent component decorator.

I recommend this article about DI and scopes in Angular 2: http://blog.thoughtram.io/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html

qdb
  • 41
  • 2
2

In RC6, router 3.0.0-rc.2 (probably works in RC5 as well), you can take route params from the URL as a snapshot in case that params won't change, without observables with this one liner:

this.route.snapshot.parent.params['username'];

Don't forget to inject ActivatedRoute as follows:

constructor(private route: ActivatedRoute) {};

mrgoos
  • 1,294
  • 14
  • 26
2

With RxJS's Observable.combineLatest, we can get something close to the idiomatic params handling:

import 'rxjs/add/operator/combineLatest';

import {Component} from '@angular/core';
import {ActivatedRoute, Params} from '@angular/router';
import {Observable} from 'rxjs/Observable';

@Component({ /* ... */ })
export class SomeChildComponent {
  email: string;
  id: string;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    Observable.combineLatest(this.route.params, this.route.parent.params)
        .forEach((params: Params[]) => {
          this.id = params[0]['id'];
          this.email = params[1]['email'];
        });
  }
}
In-Ho Yi
  • 978
  • 1
  • 8
  • 12
1

I ended up writing this kind of hack for Angular 2 rc.1

import { Router } from '@angular/router-deprecated';
import * as _ from 'lodash';

interface ParameterObject {
  [key: string]: any[];
};

/**
 * Traverse route.parent links until root router and check each level
 * currentInstruction and group parameters to single object.
 *
 * e.g.
 * {
 *   id: [314, 593],
 *   otherParam: [9]
 * }
 */
export default function mergeRouteParams(router: Router): ParameterObject {
  let mergedParameters: ParameterObject = {};
  while (router) {
    let currentInstruction = router.currentInstruction;
    if (currentInstruction) {
      let currentParams = currentInstruction.component.params;
      _.each(currentParams, (value, key) => {
        let valuesForKey = mergedParameters[key] || [];
        valuesForKey.unshift(value);
        mergedParameters[key] = valuesForKey;
      });
    }
    router = router.parent;
  }
  return mergedParameters;
}

Now in view I collect parameters in view instead of reading RouteParams I just get them through router:

@Component({
  ...
})

export class ChildishComponent {

  constructor(router: Router) {
    let allParams = mergeRouteParams(router);
    let parentRouteId = allParams['id'][0];
    let childRouteId = allParams['id'][1];
    let otherRandomParam = allParams.otherRandomParam[0];
  }

  ...
}  
Mikael Lepistö
  • 18,909
  • 3
  • 68
  • 70
  • Works great! I ended up making this method private inside of a `MergedRouteParams` class that implements the `get` method of the standard `RouteParams` class (second parameter is index, defaults to zero). – Jim Buck Aug 04 '16 at 14:18
0

In FINAL with little help of RXJS you can combine both maps (from child and parent):

(route) => Observable
    .zip(route.params, route.parent.params)
    .map(data => Object.assign({}, data[0], data[1]))

Other questions one might have:

  • Is it really a good idea to use above - because of coupling (couple child component with parent's param's - not on api level - hidden coupling),
  • Is it proper approach in term of RXJS (it would require hardcore RXJS user feedback ;)
wendro
  • 254
  • 3
  • 6
0

You can do it on the snapshot with the following, but if it changes, your id property will not be updated.

This example also shows how you can subscribe to all ancestor parameter changes and look for the one you are interested in by merging all of the parameter observables. However, be careful with this method because there could be multiple ancestors that have the same parameter key/name.

import { Component } from '@angular/core';
import { ActivatedRoute, Params, ActivatedRouteSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/observable/merge';

// This traverses the route, following ancestors, looking for the parameter.
function getParam(route: ActivatedRouteSnapshot, key: string): any {
  if (route != null) {
    let param = route.params[key];
    if (param === undefined) {
      return getParam(route.parent, key);
    } else {
      return param;
    }
  } else {
    return undefined;
  }
}

@Component({ /* ... */ })
export class SomeChildComponent {

  id: string;

  private _parameterSubscription: Subscription;

  constructor(private route: ActivatedRoute) {
  }

  ngOnInit() {
    // There is no need to do this if you subscribe to parameter changes like below.
    this.id = getParam(this.route.snapshot, 'id');

    let paramObservables: Observable<Params>[] =
      this.route.pathFromRoot.map(route => route.params);

    this._parametersSubscription =
      Observable.merge(...paramObservables).subscribe((params: Params) => {
        if ('id' in params) {
          // If there are ancestor routes that have used
          // the same parameter name, they will conflict!
          this.id = params['id'];
        }
      });
  }

  ngOnDestroy() {
    this._parameterSubscription.unsubscribe();
  }
}
shattar
  • 66
  • 6
0

Getting RouteParams from parent component in Angular 8 -

I have a route http://localhost:4200/partner/student-profile/1234/info

Parent route - student-profile

Param - 1234 (student_id)

Child route - info


Accessing param in child route (info) -

Import

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

Constructor

constructor(private activatedRoute: ActivatedRoute, private router: Router) { }

Accessing parent route params

this.activatedRoute.parent.paramMap.subscribe((params: ParamMap) => this.studentId = (params.get('student_id')));


Now, our variable studentId has the param value.

Vaibhav Vijay
  • 59
  • 1
  • 6