Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get data from Route or ActivatedRoute when subscribing to router.events.subscribe in Angular2?

I'm trying to get the data from a Router whenever the Route changes but I'm not having success. Here I set the asdf property

@NgModule({
  bootstrap: [AppComponent],
  declarations: [
    AppComponent,
    LoginComponent,
    DashboardComponent,
    OverviewComponent,
  ],
  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot([
      { path: '', pathMatch: 'full', redirectTo: '' },
      { component: LoginComponent, path: 'login' },
      {
        children: [
          { path: '', pathMatch: 'full', redirectTo: 'overview', data: { asdf: 'hello' } },
          { component: OverviewComponent, path: 'overview', data: { asdf: 'hello' } },
        ], component: DashboardComponent,
        path: '',
      },
    ]),
  ],
})
export class AppModule { }

And here I can get the URL from the router when the route changes but asdf is undefined :(

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ActivatedRoute, NavigationEnd } from '@angular/router';
@Component({
  selector: 'cs-map',
  styleUrls: ['map.component.css'],
  templateUrl: 'map.component.html',
})
export class MapComponent implements OnInit {
  private routerSub;

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

  public ngOnInit() {
    this.router.events.subscribe((val) => {
      if (val instanceof NavigationEnd) {
        let url = val.url;
        console.log(this.activatedRoute.snapshot.data['asdf']); // data is defined but asdf is not :(
      }
    });
  }
}

How can I get asdf's value?

Edit: I'm navigating to /overview

like image 800
ilovelamp Avatar asked Nov 17 '16 18:11

ilovelamp


People also ask

How do I get the URL for ActivatedRoute?

You can use the activated route to get active URL. (<RouterStateSnapshot>activatedRoute['_routerState']). url , if you like to type it.

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.


3 Answers

Something like this:

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

 this.router.events
        .filter(event => event instanceof NavigationEnd)
        .map(() => this.activatedRoute)
        .map(route => route.firstChild)
        .switchMap(route => route.data)
        .map(data => data['asdf'])
}
  1. For every events from router, I filter only NavigationEnd event
  2. Then I map this event to the activatedRoute (because I want to read the value of activatedRoute on NavigationEnd).
  3. I map the activatedRoute to the firstChild (first children declared with RouterModule.forRoot()).
  4. Then a switchMap is made to get the data of this route, a switchMap because data is an observable.
  5. Finally I map the data object to the key I want, in this case asdf
like image 56
Tuizi Avatar answered Sep 25 '22 18:09

Tuizi


The following is updated to work with rxjs v6

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

   this.router.events
        .pipe(
         filter(event => event instanceof NavigationEnd),
         map(() => this.activatedRoute),
         map(route => route.firstChild),
         switchMap(route => route.data),
         map(data => data['asdf']))
}
like image 31
Neelavar Avatar answered Sep 25 '22 18:09

Neelavar


Here is a modified example using @Neelavar link example.

It might look long but it's simple, be patient, if you stick through it, it should work. Just following the steps outlined should take a few minutes.

Full Explanation for my setup, for beginners

You should already know how to create components and setup routing, I won't explain that in depth.

1. Setup Data Property in Routes Array

In your routes array (you will find this either in app.module.ts or app-routes.module.ts or in some cases a routes.ts file).

If you can't find this, check basic routing setup in:

  • angular docs
  • a video as well

a basic setup is all you need!

Add the data property like so (you will need to generate the home component):

export const routes: Routes = [
  { path: 'home', component: HomeComponent, data: {title: 'home'} },
  { path: '', pathMatch: 'full', redirectTo: '/home' },
];

2. Create a class

RouteData.ts, I have it in the same folder space as app.module.ts

export class RouteData {
  title: string;
}

This is just to add types for using the observable in typescript, the IDE can then autocomplete "title" or "id" or "animation" for me if I add that to the data class later.

3. Import the following into app.component.ts

import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { filter, map, switchMap } from 'rxjs/operators';
import { RouteData } from './route-data';

Then above the constructor code, declare the variable

routeData$: Observable<RouteData>;

Then inside the constructor

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

4. Then inside constructor again

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

    this.routeData$ = router.events.pipe(
      filter(routeEvent => routeEvent instanceof NavigationEnd),
      map(() => activatedRoute),
      map(activatedRoute => activatedRoute.firstChild),
      switchMap(firstChild => firstChild.data),
      map((data: RouteData) => data)
      );

  }

Explanation of code adapted from @Tuizi link Example Comment:

  • For every event from router, filter only for the NavigationEnd event (when the router has completed navigation).

  • Then map (return) the currently activatedRoute.

  • Map the activatedRoute to the firstChild (The first child of this route in the router state tree).

  • Then a switchMap emits the data of this route, use a switchMap because each "data" that is emitted is an observable, the switch map will cancel the old observable when a new one is emitted, saving memory.

  • Map (return) the data object, assign it a type of RouteData.

  • Finally you can use this Observable in a template using async pipe

    <span *ngIf="routeData$ | async as routeData">
        {{ routeData.title | titlecase }}
      </span>

*A note: "async as" breaks type checking in the template at the moment :(

github issue

like image 28
Tayamba Mwanza Avatar answered Sep 21 '22 18:09

Tayamba Mwanza