50

I'm trying to get the :id param defined in

RouterModule.forRoot([
  {
    path: 'orders',
    component: ListComponent,
    children: [
      {
        path: 'view/:id',
        component: ViewComponent
      },
    ]
  }
])

from ListComponent.

I already tried:

route: ActivatedRoute

route.params.subscribe(params => {
  let id = +params['id'];
})

from ListComponent, but the params array is empty, I suppose it's because the :id param belongs to ViewComponent not ListComponent.

How can I get the id param in ListComponent?

Paolo
  • 20,112
  • 21
  • 72
  • 113

5 Answers5

78

You can use the firstChild property or the ActivatedRoute to get the child route:

route.firstChild.snapshot.params['id']
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • looks like the property routerState doesnt exist on ActivatedRoute, also i wanna get the id inside parent component not child component so i used route.firstChild.params – Fernando Popoca Ortiz Jan 09 '17 at 17:42
  • I misread the question a bit and assumed @run yards answers what you actually want. I updated my answer. – Günter Zöchbauer Jan 09 '17 at 17:45
  • 4
    I used route.snapshot.firstChild, not route.firstChild.snapshot Also check firstChild is defined first – Rusty Rob Jul 31 '17 at 02:28
  • I am getting error params of null in parent comenent if i use the above answer , what is the proper lifecycl hook to get that – Runali Oct 25 '17 at 10:10
  • The lifecycle hook doesn't matter. You subscribe to params as shown in the question and then your callback gets called when params are updated. – Günter Zöchbauer Oct 25 '17 at 10:12
  • yes , But I am loading my child route in one click function, so i am unable to catch child params before child loads , so i wrote above subscription i am getting error in console – Runali Oct 25 '17 at 10:52
  • 1
    There won't be child params before the child is loaded. You can for example inject ActivatedRoute to the child component and make the child component pass the params to a shared service. – Günter Zöchbauer Oct 25 '17 at 10:53
  • 1
    it's working but i am getting error in console ilke "ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'true'. Current value: 'false'" – Runali Oct 25 '17 at 11:41
  • `TypeError: Cannot read property 'snapshot' of null` also in method `ngOnInit` – Vlad Dec 19 '17 at 04:05
  • I guess it's better to create a new question with your code – Günter Zöchbauer Dec 19 '17 at 07:12
  • @GünterZöchbauer I think all you need to do is update your answer to be `route.snapshot`. Something must have changed or you got it wrong in your original answer. For example I just ran this code with Angular 5.2 and it worked `_this.route.snapshot.firstChild.params['id']` – Simon_Weaver Feb 08 '18 at 08:07
  • FYI this worked for getting grandchildren ... this.route.firstChild.firstChild.snapshot.params['id'] – danday74 Dec 10 '20 at 03:01
25

To get the child's params inside the parent component, i used the firstChild property of ActivatedRoute, because i only have on child route

route: ActivatedRoute

route.firstChild.params.subscribe(params => {
  let id = +params['id'];
});
  • 4
    The only correct answer, because things never happen in sync in Angular, and this answer covers the subscription (async) part as well. – Chris Vilches Sep 10 '17 at 03:43
4

Recursively finding child params wont always get you the params you are looking for. Specially when moving routes around.

Instead, an RxJS ReplaySubject can be used to publish the current child id.

Create a new service:

@Injectable()
export class ChildIdService  {
  current$ = new ReplaySubject<number>();
}

Import it on the child component and subscribe to the replay subject:

export class ChildComponent implements OnInit {
  constructor(private ar: ActivatedRoute, public ci: ChildIdService){}

  ngOnInit(){
    this.ar.params.map(params => +params.id).subscribe(this.ci.current$);
  }
}

Import the service on the parent component and use the async pipe to subscribe to the child value:

<span>Mother component child ID: {{ci.current$ | async}}</span>

Here is a working snippet: https://stackblitz.com/edit/angular-b8xome (add /child/42 to the browser app preview to see it working)


Alternatively define the components as siblings and add the parent as a componentless route, ex.: https://vsavkin.com/the-powerful-url-matching-engine-of-angular-router-775dad593b03 under "Sibling Components Using Same Data"

golfadas
  • 5,351
  • 3
  • 32
  • 39
  • Any particular reason for using ReplaySubject instead of other kinds of Subjects? I'm still trying to grasp the usecases for them, so understanding it in this example would help. – Seedmanc Feb 25 '18 at 12:14
  • ReplaySubject will memorize the previous result, making the observable hot for any future subscriptions. This is helpful if you subscribe to this observable in more than one place, and want to access previously pushed results. – golfadas Feb 26 '18 at 10:21
0

You can keep digging through the route.firstChild.params['id'] if there is just the one child route or use something like route.children[0].children[1].params['id'] to dig through multiple levels using bracket notation to access sibling routes. Its a bit of trial and error when it comes to finding the correct level.

rtn
  • 2,012
  • 4
  • 21
  • 39
0

another way of doing this is adding the following in the parent component:

<ng-container *ngIf="routerOutlet.isActivated && routerOutlet.activatedRoute?.paramMap | async as paramMap">
child id is: {{paramMap.get('id')}}!
</ng-container>

<router-outlet #routerOutlet="outlet"></router-outlet>

of course, it would be nicer to use have resolver at the child instead of params

<ng-container *ngIf="routerOutlet.activatedRouteData['child'] as child">
child id is : {{child.id}}
</ng-container>

<router-outlet #routerOutlet="outlet"></router-outlet>

assuming child route has something like:

// ...
resolve: {
 child: 'childResolver',
},

and the child resolver can be in some module:

// ...
providers: [{
    provide: 'childResolver',
    useValue: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => ({
      id: route.paramMap.get('id')
    })
  }],
Amit Portnoy
  • 5,957
  • 2
  • 29
  • 30