web-dev-qa-db-fra.com

Angular 2 - Vérifiez le "nom" de la route active actuelle

Je vais avoir une application angulaire 2 avec plusieurs enfants imbriqués voir. Mais il sera affiché sur la même page si plusieurs router-outlet

const routes: Routes = [
    {
        path: 'queue/:date/:offset/:type',
        component: BundleListComponent,
        resolve: {
            response: BundleListResolve
        },
        children: [
            {
                path: ':bundleId', component: PointListComponent,
                resolve: {
                    response: PointListResolve
                },
                children: [
                    {
                        path: ':pointId', component: TaskListComponent,
                        resolve: {
                            response: TaskListResolve
                        },
                        children: [
                            {
                                path: ':taskId', component: TaskDetailComponent,
                                resolve: {
                                    response: TaskDetailResolve
                                }
                            },
                            { path: '', component: EmptyComponent }
                        ]
                    },
                    { path: '', component: EmptyComponent }
                ]
            },
            { path: '', component: EmptyComponent }
        ]

    },
    {
        path: 'queue',
        component: QueueRedirectComponent
    }
}

Donc, fondamentalement, je peux parcourir la liste des itinéraires

  • /queue
  • / queue /: date /: offset /: type
  • / queue /: date /: offset /: type /: bundleId
  • / queue /: date /: offset /: type /: bundleId /: pointId
  • / queue /: date /: offset /: type /: bundleId /: pointId /: taskId

Par exemple

#/queue/2017-01-05/480/20/57f4c26507b36e3684007b52/1/57fb0abb07b36e39d8e88df8/1

Imaginez que vous avez une page avec un élément:

  1. Une partie de l'interface utilisateur a montré une liste de films
  2. L’autre partie affiche un détail du film lorsque vous cliquez sur un élément de la liste mais qu’il est affiché sur la même page.
  3. Une autre partie pour afficher les détails du personnage lorsque vous cliquez sur le nom du personnage dans le détail du film, et également sur la même page.
  4. etc...

En gros, je peux toujours cliquer sur la liste de films pendant que je regarde un détail de personnage.

Si vous cherchez à définir le nom de chaque itinéraire mais que tout semble indiquer le résultat, cette fonctionnalité a déjà été supprimée de Angular 2 final. Dans Angular 1, à l’aide du routeur UI, je peux définir le nom de chaque itinéraire et l’obtenir très facilement avec la fonction intégrée state.is(ROUTE_NAME)

Donc ce que je fais maintenant est basé sur le window.location pour obtenir l'URL et le fractionnement de cette chaîne par / pour obtenir le nombre de paramètres. Mais c'est plus codé en dur.

Une expérience sur faire la même chose? Comment puis-je distinguer quel itinéraire est actuellement actif?

12
trungk18

Je pense qu'il y a un problème avec la réponse de Scott qui utilise ActivatedRoute dans le constructeur du service. Cet itinéraire ne sera pas mis à jour.

J'ai pensé à une autre solution qui pourrait intéresser votre intérêt. Cela revient encore à utiliser la propriété data sur les routes, mais maintenant à l'aide d'un autre service de résolution:

Vous allez avoir besoin d'une RouterConfig comme celle-ci, où pour chaque route vous ajoutez le state: StateResolve et un objet de données contenant le nom de l'état:

const routes: RouterConfig = [{
    path: 'queue/:date/:offset/:type',
    component: BundleListComponent,
    resolve: {
       response: BundleListResolve,
       state: StateResolve
    },
    data: {
       state: 'Bundle'
    },
    ...
]

N'oubliez pas d'ajouter le service StateResolve au groupe de fournisseurs

Votre service StateResolve ressemblera à ceci:

@Injectable()
export class StateResolve implements Resolve<string> {

   constructor(private stateService: StateService) {}

   resolve(route: ActivatedRouteSnapshot): string {
       let state: string = route.data['state']
       this.stateService.setState(state);
       return state;
   }
}

De toute évidence, vous aurez besoin d'une StateService qui utilise la méthode setState, mais à partir de là, c'est assez explicite.

Utiliser une garde resolve est peut-être un peu excentrique, mais si vous y réfléchissez, vous essayez de résoudre les données avant de montrer la route. Dans ce cas, l'état à l'intérieur de la variable data, il est donc logique d'utiliser la variable Resolve pour accéder à la propriété data.

5
PierreDuc

Créez un service appelé ActiveState qui subscribe permettra de modifier le routeur, à l'aide de router.events.subscribe:

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

@Injectable()
export class ActiveState {

  public name : string;

  constructor(router : Router, route : ActivatedRoute)
  {
    router.events.subscribe(event => {
      if(event instanceof NavigationEnd){

        // Traverse the active route tree
        var snapshot = route.snapshot;
        var activated = route.firstChild;
        if(activated != null) {
          while (activated != null) {
            snapshot = activated.snapshot;
            activated = activated.firstChild;
          }
        }

        // Try finding the 'stateName' from the data
        this.name = snapshot.data['stateName'] || "unnamed";
      }
    });
  }

  is(name : string) : boolean
  {
    return this.name === name;
  }
}

Ensuite, sur votre route, nous ajoutons une valeur simple au paramètre data de la route appelée stateName pour chaque état que nous voulons nommer:

const routes: Routes = [
{
    path: 'queue/:date/:offset/:type',
    component: BundleListComponent,
    resolve: { response: BundleListResolve }
    data: { stateName: 'Bundle' },
    children: [
        {
            path: ':bundleId', component: PointListComponent,
            resolve: { response: PointListResolve },
            data: { stateName: 'Point'}
        }
    ...

Ensuite, lorsque vous injectez state : ActiveState, vous pouvez simplement tester la valeur state.is("Point")

10
Scott
  • Je créerais un service simple qui suit l'état actif. 
  • Ce service peut être injecté si nécessaire pour obtenir ou définir l'état actif. 
  • Vous utilisez déjà Resolvers, vous pouvez donc définir l'identificateur d'état qui s'y trouve.

Créez un service appelé ActiveState:

import {Injectable} from "@angular/core";
import {Observable} from "rxjs";

@Injectable()
export class ActiveState {

  public current : Observable<string>;
  private observer : any;

  constructor()
  {
    // Create an observable (it can be subscribed to)
    this.current = new Observable(observer => {
      this.observer = observer;
      observer.next('Unknown'); // The default unknown state
    });
  }

  setActive(name : string) : void
  {
    this.observer.next(name);
  }
}

Dans vos résolveurs tels que PointListResolve ... TaskListResolve etc.

import {Resolve, ActivatedRouteSnapshot} from "@angular/router";
import {Injectable} from "@angular/core";
import {Observable} from "rxjs";
import {ActiveState} from "services/ActiveState.service"; 

@Injectable()
export class PointListResolver implements Resolve<any> {

  // Inject the ActiveState in your constructor
  constructor(private state : ActiveState) {}

  resolve(route: ActivatedRouteSnapshot): Observable<any> {
    // Set the active state name
    this.state.setActive("Point"); // We are here: /queue/:date/:offset/:type/:bundleId/:pointId

    // Do your regular resolve functionality (if you don't need to resolve, this blank resolver of an empty object will work)
    // ...
    return Observable.of({});
  }
}

Ainsi, dans les autres résolveurs, mettez à jour la valeur this.state.setActive("") comme requis.


Ensuite, pour déterminer l’état dans lequel vous vous trouvez, injectez ActiveState à l’emplacement où vous souhaitez utiliser l’état actuel, par exemple dans un @Component, c.-à-d.

import {Component, OnDestroy} from '@angular/core';
import {ActiveState} from "services/ActiveState.service";

@Component({
  selector: 'my-current-state-component',
  template: `The current state is: {{stateName}}`,
})
export class MyCurrentStateComponent implements OnDestroy {

  public stateName : string;
  private sub : Subscription;

  // Inject in ActiveState
  constructor(private state : ActiveState)
  {
    // Subscribe to the current State
    this.sub = state.current.subscribe(name => {
      this.stateName = name;

      // Other logic to perform when the active route name changes
      ...
    });
  }

  ngOnDestroy()
  {
     this.sub.unsubscribe();
  }
}

Remarques:

  • N'oubliez pas d'enregistrer votre service ActiveState en tant que Provider dans: 

    @NgModule({
      ...
      providers:[ActiveState]
      ...
    })
    export class AppModule { }
    
  • Plus simple - Version non observable J'ai utilisé un Observable<string> afin que les modifications apportées à l'état actif puissent prendre la valeur subscribed, mais ceci pourrait être simplifié pour devenir simplement une string si vous ne voulez pas cette fonctionnalité:

    import {Injectable} from "@angular/core";
    
    @Injectable()
    export class ActiveState {
    
      public current : string;
    
      setActive(name : string) : void
      {
        this.current = name;
      }
    
      is(name : string) : boolean
      {
        return name == this.current;
      }
    }
    

    Ensuite, lorsque vous injectez state : ActiveState, vous pouvez simplement tester la valeur state.is("Point")

J'espère que c'est utile.

2
Scott

name a été retiré des routes il y a longtemps, mais les routes permettent d'ajouter des données arbitraires

const routes: RouterConfig = [
    {
        path: '',
        redirectTo: '/login',
        pathMatch: 'full',
    },
    {
        path: 'login',
        component: LoginComponent,
        data: {title: 'Login'}
    },
    {
        path: 'home',
        component: HomeComponent,
        data: {title: 'Home'}
    },
    {
        path: 'wepays',
        component: WePaysComponent,
        data: {title: 'WePays'}
    }
];

Ce code construit un titre à partir des noms de tous les segments de route. Cela pourrait probablement être simplifié pour votre cas d'utilisation.

export class AppComponent { 
  constructor(titleService:Title, router:Router, activatedRoute:ActivatedRoute) {
    router.events.subscribe(event => {
      if(event instanceof NavigationEnd) {
        var title = this.getTitle(router.routerState, router.routerState.root).join('-');
        console.log('title', title);
        titleService.setTitle(title);
      }
    });
  }

  // collect that title data properties from all child routes
  // there might be a better way but this worked for me
  getTitle(state, parent) {
    var data = [];
    if(parent && parent.snapshot.data && parent.snapshot.data.title) {
      data.Push(parent.snapshot.data.title);
    }

    if(state && parent) {
      data.Push(... this.getTitle(state, state.firstChild(parent)));
    }
    return data;
  }
}

Voir aussi Changement du titre de la page à l’aide du nouveau routeur Angular 2

2
Günter Zöchbauer

ma réponse est similaire, mais l'a fait d'une manière différente, donc je pense qu'il est bon de poster

Ce qui est différent: je n'ai pas besoin de changer quoi que ce soit sur mes routes, j'ai créé un service pour suivre ActivatedRoute plus en profondeur (inside ou firstChild ... firstChild).

créer le service

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

puis dans app.component.ts je lance le service (juste pour m'assurer qu'il est toujours suivi)

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

et enfin, prenez votre itinéraire où que vous soyez, comme service:

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

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

pour répondre à la question, vous pouvez simplement prendre deeperActivatedRoute et vérifier normalement le fichier snapshop.url, comme vous le feriez dans un composant.

0
LuizAsFight