Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 Activatedroute params not working in Service or outside <router-outlet>

I have a very strange problem: index.html

<navigation-menu *ngIf="isLoggedIn"></navigation-menu>
<div class="content-wrapper" [ngClass]="{'fullContent' : !isLoggedIn}">
    <section class="content">
        <router-outlet></router-outlet>            
    </section>
</div>

The navigation-menu is a component for the nav menu. In router-outlet content there is a component called "assets".

What I did in asset component:

import { ActivatedRoute}from "@angular/router";
constructor(private route: ActivatedRoute){}
public ngOnInit(): void {
    this.route.params.subscribe(params => {
        const id = params["id"];
}

This works and I get the parameters of my route (which is kind of "asset/:id).

Now I tried the same in my Navigation-Menu Component (which is "outside" the router outlet) and in a global service called contextservice. Same code as above, but it is not even triggered on route change. If I try to get a current router snapshot

  const strId = this.route.snapshot.params["id"];

after NavigationEnd event was triggered it is the same result: strId is undefined because params are an empty object.

It only works in my asset component. Is this working as intended or how should this be handled?

My intension was to trigger an event from a global service (or a "global" component like the nav menu) which is listening to all route(-params) changes.

My only solution would be to parse the complete url after every NavigationEnd event which in my opinion is no proper way...Or to handle the params change in each child component (in router-outlet) itself.

Maybe I have just a basic error in understanding...

Thanks

Solution from accepted answer:

this.router.events.subscribe(val => {
        if (val instanceof RoutesRecognized) {
            var strId = val.state.root.firstChild.params["id"];
        }});

Don't forget to import RoutesRecognized from angular router!!

like image 730
Cabe Skuriles Avatar asked Nov 02 '16 08:11

Cabe Skuriles


People also ask

What is the difference between routes router and ActivatedRoute?

Difference between activatedroute and routerstate RouterState is an interface which represents the state of the router as a tree of activated routes. Every node of this tree knows about the "consumed" URL segments, the extracted parameters, and the resolved data.

What is the difference between ActivatedRoute and ActivatedRouteSnapshot?

Since ActivatedRoute can be reused, ActivatedRouteSnapshot is an immutable object representing a particular version of ActivatedRoute . It exposes all the same properties as ActivatedRoute as plain values, while ActivatedRoute exposes them as observables.

What does the ActivatedRoute interface allow you to access?

ActivatedRoutelink. Provides access to information about a route associated with a component that is loaded in an outlet. Use to traverse the RouterState tree and extract information from nodes.

Where do you set the route parameter value in Angular?

First, add links to the two components. Assign the anchor tag that you want to add the route to the routerLink attribute. Set the value of the attribute to the component to show when a user clicks on each link. Next, update your component template to include <router-outlet> .


4 Answers

The component added by the router gets the router segment (ActivatedRoute) passed, but in a service there is no activated route. You can subscribe to router.events and traverse the routes tree (router.firstChild...`) to get params out of a specific route sequement you need.

See also https://github.com/angular/angular/issues/11023

like image 135
Günter Zöchbauer Avatar answered Oct 21 '22 15:10

Günter Zöchbauer


Here is an angular service which does that :

import {Injectable}                                                        from '@angular/core';
import {ActivatedRoute, NavigationEnd, NavigationExtras, ParamMap, Router} from "@angular/router";
import {RouterExtensions}                                                  from "nativescript-angular/router";
import {NavigationOptions}                                                 from "nativescript-angular/router/ns-location-strategy";
import {Observable}                                                        from "rxjs/Observable";
import {first}                                                             from "rxjs/operators/first";
import {filter}                                                            from "rxjs/operators/filter";
import {map}                                                               from "rxjs/operators/map";
import {switchMap}                                                         from "rxjs/operators/switchMap";
import {unRegisterAndroidOnBack}                                           from "../../utils/view.utils";

@Injectable()
export class RoutingService
    {
        constructor(private routerExtensions: RouterExtensions, private route: ActivatedRoute, private router: Router)
        {
        }
        public getActivatedRouteParameter(paramName: string): Observable<ParamMap>
        {
            return this.router.events.pipe(filter(e => e instanceof NavigationEnd),
                                           map((): ActivatedRoute =>
                                               {
                                                   let route = this.route;
                                                   while (route.firstChild)
                                                       {
                                                           route = route.firstChild;
                                                       }
                                                   return route;
                                               }),
                                           filter((route: ActivatedRoute) => route.outlet === 'primary'),
                                           switchMap((route: ActivatedRoute) => route.paramMap) , first());

        }

.

like image 21
Royi Namir Avatar answered Oct 21 '22 15:10

Royi Namir


I have been browsing for a simple solution around the web and finally found something that works in angular 8.

https://medium.com/@eng.ohadb/how-to-get-route-path-parameters-in-an-angular-service-1965afe1470e

This works as expected. There are many different flavors available over the web. However only this one worked for me. I am new to rxjs and observer piping so it gets quickly confusing when the chain gets long for me.

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!!!!
      });
  }
}

Obviously afterwards you can design your service however you want. To interface your params.

like image 37
Mark Odey Avatar answered Oct 21 '22 14:10

Mark Odey


Actually, your activatedRoute is correct and updated, but you have all the tree in that. so if you go all the way inside of route.firstChild, you will finally find the last route, which I called deeperActivatedRoute (inside of route.firstChild...route.firstChild)

So what I did is create a service to track the deeperRoute, and have it always accessible

import { Injectable } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';

@Injectable()
export class ActivatedRouteService {
  private _deeperActivatedRoute: ActivatedRoute;
  get deeperActivatedRoute(): ActivatedRoute {
    return this._deeperActivatedRoute;
  }

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

  init(): void {
    this.router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {
        // Traverse the active route tree
        let activatedChild = this.route.firstChild;
        if (activatedChild != null) {
          let nextActivatedChild;
          while (nextActivatedChild != null) {
            nextActivatedChild = activatedChild.firstChild;
            if (nextActivatedChild != null) {
              activatedChild = activatedChild.firstChild;
            }
          }
        }

        this._deeperActivatedRoute = activatedChild || this.route;
      }
    });
  }
}

Then in app.component.ts I start the service (just to ensure it's always tracking)

export class AppComponent {
  constructor(private activatedRouteService: ActivatedRouteService) {
    this.activatedRouteService.init();
  }
}

And finally, take your route wherever service you are:

export class ForbiddenInterceptor implements HttpInterceptor {
  constructor(private activatedRouteService: ActivatedRouteService) { }

  doYourStuff(): void {
       //you'll have the correct activatedRoute here
       this.activatedRouteService.deeperActivatedRoute;
  }
}

Answering the question, you can just take the deeperActivatedRoute and check normally the snapshop.url, just as you would do in a component

like image 1
LuizAsFight Avatar answered Oct 21 '22 14:10

LuizAsFight