41

I have a main component that has a router-outlet in it. In the component that is loaded in the router-outlet I grab the url parameter like this:

ngOnInit(): void {
    // _route is injected ActivatedRoute
    this._route.params.subscribe((params: Params) => {
        if(params['url']){
          this.testUrl = params['url'].replace(new RegExp('\%2[fF]', 'g'), '/');

        }

    });
}

This works fine, but when I try it on my top level component the params object is always empty. I don't understand why as the nested components param object has the data in it and I am trying to access it exactly the same way. There are no errors to go on, the param object is just empty.

Why doesn't my parent component get the right Params object from ActivatedRoute?

edit:

as requested full parent component

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

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
})
export class AppComponent {
  public testUrl: string;
  constructor(private router: Router, private _route: ActivatedRoute) {

  }
  ngOnInit(): void{
    this._route.queryParams.subscribe((params: Params) => {
      console.log(params);

      if (params['url']) {
        this.testUrl = params['url'].replace(new RegExp('\%2[fF]', 'g'), '/');
        alert(params['url']);
      }
    });
  }

}

router code for app.module.ts:

RouterModule.forRoot([
  { path: '', component: CheckMobileComponent },
  { path: '**', component: CheckMobileComponent }
]),

router code for nested module:

RouterModule.forChild([
 { path: 'report', component: MobileReportComponent }

There is no directly specified route for my app.component as it is loaded by a selector in index.html.

Guerrilla
  • 13,375
  • 31
  • 109
  • 210

6 Answers6

74

ActivatedRoute: Contains the information about a route associated with a component loaded in an router outlet. If you would like to access route details outside of it, use the code below.

import { Component } from '@angular/core';
import { Router, ActivatedRoute, Params, RoutesRecognized } from '@angular/router';

export class AppComponent {

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

    }

    ngOnInit(): void {
        this.router.events.subscribe(val => {

            if (val instanceof RoutesRecognized) {

                console.log(val.state.root.firstChild.params);

            }
        });

    }
}

There are other ways to share data in between components for example by using a service.

For more details about how you can tackle this as concept, read comments here.

Community
  • 1
  • 1
KKS
  • 3,600
  • 1
  • 27
  • 54
  • 1
    This is a great information, It's the I've seen any information on this behavior thanks for sharing :) – Jessy Aug 02 '18 at 11:59
  • this.route.events is undefined. Maybe something changed since 2017? – RobKohr Nov 05 '19 at 16:09
  • @RobKohr yes, maybe something changed since then as back in 2017 I tested this code before the answer was posted. Unfortunately, I don't remember the version that I was using at that time. – KKS Nov 05 '19 at 17:00
  • Sample works with Angular 11, just tested it. – Silvos Mar 23 '21 at 13:37
27

The Very simple answer

import { Component } from '@angular/core';
import { Router, ActivatedRoute, Params, RoutesRecognized } from '@angular/router';

export class AppComponent {

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

    ngOnInit(): void {
        this.actRoute.firstChild.params.subscribe(
            (params: any) => {
                if (params.hasOwnProperty('<whatever the param name>') != '') {
                    //do whatever you want
                    console.log(params.<whatever the param name>);
                } 
            });
    }
}
VithuBati
  • 1,918
  • 1
  • 18
  • 26
  • 1
    Woah that's so great! Best solution yet. can't believe that I had to work through overcomplicated stuff like this: https://medium.com/@tomastrajan/how-to-get-route-path-parameters-in-non-routed-angular-components-32fc90d9cb52 – hugo der hungrige Dec 03 '19 at 09:58
  • 2
    @hugoderhungrige Because this won't work if you are not in a component children of the root router. I am currently dealing with a headache of a project which didn't include header menu in the root routing and its a real pain to retireve any params. urgh. – Mark Odey Jan 27 '20 at 17:32
  • Could you explain why this solution is the solution? As a beginner for Angular wouldn't quite understand the differences between this and just using 'actRoute.params.subscribe'. Thanks – Brenton Scott Oct 21 '21 at 08:19
8

If you'd like to access the router parameters via a service, this approach will not work! consider using a service to prevent the replication of the "Route params value extraction logic" (from this Medium article):

@Injectable({
  providedIn: 'root'
})
export class MyParamsAwareService {
  constructor(private router: Router) { 
    this.router.events
      .pipe(
        filter(e => (e instanceof ActivationEnd) && (Object.keys(e.snapshot.params).length > 0)),
        map(e => e instanceof ActivationEnd ? e.snapshot.params : {})
      )
      .subscribe(params => {
      console.log(params);
      // Do whatever you want here!!!!
      });
  }
}
1O1
  • 304
  • 1
  • 3
  • 12
5

Even better solution from the angular doc.

You can compute all params (or data) in the router state or to get params outside * of an activated component by traversing the RouterState tree as in the followin

collectRouteParams(): Record<string, string> {
    let params: Record<string, string> = {};

    const stack: ActivatedRouteSnapshot[] = [
      this.router.routerState.snapshot.root,
    ];

    while (stack.length > 0) {
      const route = stack.pop();
      if (route === undefined) {
        continue;
      }

      params = { ...params, ...route.params };

      stack.push(...route.children);
    }

    return params;
  }
Luca Marangon
  • 762
  • 1
  • 8
  • 13
4

ActivatedRoute works for components loaded via router-outlet. From docs - it:

Contains the information about a route associated with a component loaded in an outlet.

I don't think you can use it in components that are not loaded in an outlet. It also doesn't work in base classes your component extends.

class A { constructor(public route: ActivatedRoute) } 
class B extends A { ngOnInit() { this.route; } }        // not working
class C { constructor(public route: ActivatedRoute) }   // working if loaded in outlet
Sasxa
  • 40,334
  • 16
  • 88
  • 102
  • Thanks for reply. I guess I have to make an intermediary service then? Or is there simpler way? – Guerrilla Mar 22 '17 at 10:34
  • Can't think of any atm.. I usually leave it in the component, but depending on how much/often you use it service might be a good idea... – Sasxa Mar 22 '17 at 10:36
2

I was having a very similar problem with this mainly due to my misunderstanding of how routes actually worked. I thought I could just go up the chain of path parameters and each one would be a parent/child relationship. However, if a route has more than one element to the path, (e.g. /dashboard and profile/:user_id) it will depend on how you set up the routes in your routing module(s).

To explain a different way that made it click for me:

// If I am trying to access ActivatedRoute from the CheckMobileComponent (say 
// through a `router-outlet`), I could get it through something like 
// `this._route.firstChild`

{ path: '', component: CheckMobileComponent, children: [

    // If I want to get things from the '' context in MobileReportComponent, it 
    // would be through something like `this._route.parent`

    { path: 'report', component: MobileReportComponent }

  ]   

}
cchapman
  • 3,269
  • 10
  • 50
  • 68