How to get data from Route or ActivatedRoute when

2020-08-16 03:41发布

问题:

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

回答1:

Without while loop

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'])
}


回答2:

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']))
}


回答3:

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 ngOnInit

ngOnInit() {

    this.routeData$ = this.router.events.pipe(
      filter(routeEvent => routeEvent instanceof NavigationEnd),
      map(() => this.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



回答4:

If you are hitting the route the first time you load the route in your browser, ActivatedRoute will have the current data. But if you then click on something which causes a second router navigation, the ActivatedRoute instance for that second route will have the original route's information. This could be due to to the fact that it was the ActivatedRoute at the time it was injected into the constructor.

However there is an updated instance of ActivatedRouteSnapshot associated with the ActivationEnd event, so you can access the data like this:

this.router.events.pipe(
    filter(event => event instanceof ActivationEnd),
    map(event => (<ActivationEnd>event).snapshot),
    map(snapshot => (<ActivatedRouteSnapshot>snapshot).data)).subscribe(data => {
        console.log('data from the NEW route: ' + data);
    });


回答5:

data in the routes must be an array of objects rather than just object so define them like this

{ component: LoginComponent, path: 'login', data:[{myKey1: myValue1}] },

Now to retrieve it use this code in your components constructor

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

  router.events
    .filter(event => event instanceof NavigationEnd)
    .map(_ => this.router.routerState.root)
    .map(route => {
      while (route.firstChild) route = route.firstChild;
      return route;
    })
  .flatMap(route => route.data)
  .subscribe(data => {
    console.log(data[0].myKey1);

    });

}