5

I have the following routes configured in app-routing.module.ts

const routes: Routes = [
  {
    path: 'abc/:id', component: AbcComponent, data: { category: 'Public' }
  },
  {
    path: 'xyz/:id/tester/:mapId', component: XyzComponent, data: { category: 'Private' }
  },
  { path: '**', redirectTo: '/page-not-found', pathMatch: 'full'}
]

In app.component.ts I would like to get the category of each Route based on the URL passed:

Ex: going to http://myapp.com/abc/123 should return category as Public going to http://myapp.com/xyz/123/tester/456 should return category as Private

Here is what I have so far:

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

checkRouteAndGetCategory()
{
  this.router.events.pipe(
        filter(event => event instanceof NavigationEnd),
        map(() => this.activatedRoute),
        map(route => {
          while (route.firstChild) route = route.firstChild
          return route
        }),
        filter(route => route.outlet === 'primary'),
        mergeMap(route => route.data)
      ).subscribe(data =>
        console.log('data', data)
      )
}

The above code does not seem to get the right route. Ex: If I am on http://myapp.com/abc/123 page and navigated to http://myapp.com/xyz/123/tester/456, it seems to get the data for http://myapp.com/abc/123 page.

NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
Jake
  • 25,479
  • 31
  • 107
  • 168

3 Answers3

10

This is what I have in my app component

constructor(private route: ActivatedRoute) {
}

ngOnInit(): void {
  this.router.events.pipe(
    filter(event => event instanceof NavigationEnd),
    map(() => this.rootRoute(this.route)),
    filter((route: ActivatedRoute) => route.outlet === 'primary'),
    mergeMap((route: ActivatedRoute) => route.data)
  ).subscribe((event: {[name: string]: any}) => {
    this.titleService.setRouteTitle(event['title']);
  });
}

private rootRoute(route: ActivatedRoute): ActivatedRoute {
  while (route.firstChild) {
    route = route.firstChild;
  }
  return route;
}

Where my app routes look like:

{ path: 'login', component: LoginComponent, data: { title: 'Login' } }

And my title service is responsible for setting the title.

The only difference I see between mine and yours is that you bind to the router in your constructor and I do it in ngOnInit. Can you try calling what you have in ngOnInit? I'm not sure if this will make a difference, but it's worth a shot.

Kurt Hamilton
  • 12,490
  • 1
  • 24
  • 40
  • We did have different names for our injected `ActivatedRoute`. I also think that both of our methods do the same thing - they were just being called in different places. If yours still doesn't work from ngOnInit, then I have no idea what's going on. Next step would be to recreate in a stackblitz. – Kurt Hamilton Feb 17 '20 at 20:53
  • I posted the bounty, I think this answer is great, looking if there is more simple code, lot of people have different answers, https://stackoverflow.com/questions/43512695/how-to-get-route-data-into-app-component-in-angular-2 this.title = route.root.firstChild.snapshot.data['PageName']; etc –  Sep 14 '20 at 16:30
1

I would consider moving this to a service, say CategoryService

CategoryService

import { Injectable } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { filter, map, mergeMap } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class CategoryService {
  constructor(private router: Router ) { }
  private routerEvents =  this.router.events;
  private navigationEndEvent = this.routerEvents
    .pipe(filter(event => event instanceof NavigationEnd))
  category$ = this.navigationEndEvent
    .pipe(
      mergeMap(() => this.rootRoute(this.router.routerState.root).data),
      map(({category}: any) => category)
    )
  private rootRoute = (route: ActivatedRoute): ActivatedRoute => {
    return route.firstChild ? this.rootRoute(route.firstChild) : route;
  }
}

Component

constructor(private categoryService: CategoryService) {
  }
  category$ = this.categoryService.category$;
  ...
}

HTML

<span>{{ category$ | async }}</span>

Sample on Stackblitz

Owen Kelvin
  • 14,054
  • 10
  • 41
  • 74
  • `mergeMap(() => this.rootRoute(this.router.routerState.root).data)` is what made it for me, instread of `mergeMap(() => this.rootRoute(this.route).data)`. – Hasan Can Saral Mar 28 '22 at 13:33
-1

It's a short way to get route data in the component.

constructor(router:Router, route:ActivatedRoute) {
    router.events
      .filter(e => e instanceof NavigationEnd)
      .forEach(e => {
        this.title = route.root.firstChild.snapshot.data.category;
    });
} 
Prakash Harvani
  • 1,007
  • 7
  • 18