web-dev-qa-db-de.com

Winkel 2 - Aktuellen Namen der aktiven Route prüfen

Ich habe eine Angular 2-Anwendung mit mehreren verschachtelten Kinderansichten. Es wird jedoch auf derselben Seite angezeigt, obwohl mehrere 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
    }
}

Im Grunde kann ich die Routenliste durchfahren

  • /Warteschlange
  • / Warteschlange /: Datum /: Offset /: Typ
  • / queue /: date /: offset /: type /: bundleId
  • / queue /: date /: offset /: type /: bundleId /: pointId
  • / queue /: date /: offset /: type /: bundleId /: pointId /: taskId

Zum Beispiel

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

Stellen Sie sich vor, Sie haben eine Seite mit einem Element:

  1. Ein Teil der Benutzeroberfläche zeigte eine Filmliste
  2. Der andere Teil zeigt ein Filmdetail, wenn Sie auf ein Element in einer Filmliste klicken, jedoch auf derselben Seite anzeigen.
  3. Ein weiterer Bereich, in dem ein Zeichendetail angezeigt wird, wenn in den Filmdetail in den Charakternamen geklickt wird, und auch auf derselben Seite angezeigt.
  4. usw...

Grundsätzlich kann ich immer noch in die Filmliste klicken, während ich ein Zeichendetail sehe.

Suche nach dem Definieren des Namens für jede Route, scheint jedoch den gesamten Antwortbericht zu enthalten, für den diese Funktion bereits aus Angular 2 final entfernt wurde. In Angular 1 mit dem UI-Router kann ich den Namen für jede Route definieren und die Route mit der integrierten Funktion state.is(ROUTE_NAME) sehr einfach abrufen. 

Was ich jetzt mache, ist auf der window.location Basis, um die URL zu erhalten, und diese Zeichenfolge in / aufzuteilen, um die Anzahl der Parameter zu erhalten. Aber es ist mehr hartcodiert.

Irgendwelche Erfahrung, das gleiche zu tun? Wie kann ich feststellen, welche Route gerade aktiv ist?

12
trungk18

Ich glaube, es gibt ein Problem mit Scotts Antwort, bei dem er die ActivatedRoute im Konstruktor des Dienstes verwendet. Diese Route wird nicht aktualisiert.

Ich habe an eine andere Lösung gedacht, die Ihr Interesse wecken könnte. Es kommt wieder auf die Verwendung der data-Eigenschaft auf den Routen an, aber jetzt wird ein anderer Auflösungsdienst verwendet:

Sie benötigen eine RouterConfig wie folgt, wobei Sie für jede Route den state: StateResolve und ein Datenobjekt hinzufügen, das den Statusnamen enthält:

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

Vergessen Sie nicht, den StateResolve-Service zum Provider-Array hinzuzufügen

Ihr StateResolve-Service sieht ungefähr so ​​aus:

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

Natürlich benötigen Sie eine StateService, die die setState-Methode hat, aber ich denke, von hier aus ist es ziemlich selbsterklärend.

Vielleicht ist die Verwendung eines resolve-Guard etwas exzentrisch, aber wenn Sie darüber nachdenken, versuchen Sie, Daten aufzulösen, bevor Sie die Route anzeigen. In diesem Fall befindet sich der Status in der Datenvariablen. Daher ist es sinnvoll, die Variable Resolve für den Zugriff auf die Eigenschaft data zu verwenden

5
PierreDuc

Erstellen Sie einen Dienst mit dem Namen ActiveState, der subscribe Änderungen am Router mithilfe von router.events.subscribe vornehmen wird:

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

Dann fügen wir auf Ihrer Route einen einfachen Wert für den data-Parameter der Route mit dem Namen stateName für jeden Status hinzu, den wir benennen möchten:

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'}
        }
    ...

Wenn Sie dann state : ActiveState injizieren, können Sie den Wert state.is("Point") einfach testen.

10
Scott
  • Ich würde einen einfachen Dienst erstellen, der den aktiven Status verfolgt. 
  • Dieser Dienst kann bei Bedarf eingefügt werden, damit get oder set den aktiven Status erhält. 
  • Sie verwenden bereits Resolver, so dass Sie den Zustandsbezeichner set dort angeben können.

Erstellen Sie einen Dienst mit dem Namen 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);
  }
}

In Ihren Resolvern wie PointListResolve ... TaskListResolve usw.

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

Aktualisieren Sie in den anderen Resolvern den this.state.setActive("")-Wert nach Bedarf.


Um festzustellen, in welchem ​​Status Sie sich befinden, geben Sie ActiveState an, wo Sie den aktuellen Status verwenden möchten, z. B. in einem @Component.

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

Anmerkungen:

  • Vergessen Sie nicht, Ihren ActiveState-Service als Provider zu registrieren: 

    @NgModule({
      ...
      providers:[ActiveState]
      ...
    })
    export class AppModule { }
    
  • Simpler - Nicht beobachtbare Version Ich habe einen Observable<string> verwendet. Änderungen am aktiven Status können subscribed sein. Dies kann jedoch vereinfacht werden, um nur eine string zu sein, wenn Sie diese Funktion nicht möchten:

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

    Wenn Sie dann state : ActiveState injizieren, können Sie den Wert state.is("Point") einfach testen.

Ich hoffe das ist nützlich.

2
Scott

name wurde vor einiger Zeit von den Routen entfernt. Mit den Routen können jedoch beliebige Daten hinzugefügt werden

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

Dieser Code erstellt einen Titel aus den Namen aller Routensegmente. Dies könnte möglicherweise für Ihren Anwendungsfall vereinfacht werden.

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

Siehe auch Ändern des Seitentitels mit dem neuen Angular 2-Router

2

meine Antwort ist ähnlich, aber auf eine andere Art und Weise, also finde ich es gut, zu posten

Was ist anders: Ich muss an meinen Routen nichts ändern, ich habe einen Dienst durchgeführt, um die tiefere ActivatedRoute zu verfolgen (in oder firstChild ... firstChild)

erstellen Sie den Dienst

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

dann in app.component.ts starte ich den Dienst (um sicherzustellen, dass er immer verfolgt wird)

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

und schließlich nehmen Sie Ihre Route, wo immer Sie sind:

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

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

wenn Sie die Frage beantworten, können Sie einfach die deeperActivatedRoute nehmen und normalerweise die Datei snapshop.url überprüfen, genau wie in einer Komponente

0
LuizAsFight