Angular 2 - Check current active route “name”

后端 未结 6 1586
说谎
说谎 2020-12-24 09:36

I am having an Angular 2 application with several nested children view. But it will be displayed on the same page though several router-outlet.



        
相关标签:
6条回答
  • 2020-12-24 09:59

    my answer is similar, but did in a different way, so I think it's good to post

    What is different: I don't need to change anything on my routes, I did a service to track the deeper ActivatedRoute (inside or firstChild...firstChild)

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

    then in app.component.ts I start the service (just to ensure it's always tracking)

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

    and finally, take your route wherever service you are:

    export class ForbiddenInterceptor implements HttpInterceptor {
      constructor(private activatedRouteService: ActivatedRouteService) { }
    
      doYourStuff(): void {
           //you'll have the correct activatedRoute here
           this.activatedRouteService.deeperActivatedRoute;
      }
    }
    

    answering the question, you can just take the deeperActivatedRoute and check normally the snapshop.url, just as you would do in a component

    0 讨论(0)
  • 2020-12-24 10:00

    Create a service called ActiveState which will subscribe to changes to the router, using 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;
      }
    }
    

    Then on your route we add a simple value on the data param of the route called stateName for each state we want to name:

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

    Then when you inject state : ActiveState you can simple test the value state.is("Point")

    0 讨论(0)
  • 2020-12-24 10:01

    name was removed quite some time ago from routes, but routes allow to add arbitrary data

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

    This code constructs a title from the names of all route segments. This could probably be simplified for your use case.

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

    See also Changing the page title using the Angular 2 new router

    0 讨论(0)
  • 2020-12-24 10:08

    Try this:

    this.router.url.split("/")[3] //change number to get needed :route
    
    0 讨论(0)
  • 2020-12-24 10:12
    • I would create a simple service that tracks the active state.
    • This service can be injected where needed to get or set the active state.
    • You are already using Resolvers, so you could set the state identifier in there.

    Create a Service called 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 your resolvers such as 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({});
      }
    }
    

    So in the other resolvers update the this.state.setActive("") value as required.


    Then to determine which state you are in, inject ActiveState where you want to use the current state, such as in a @Component, i.e.

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

    Notes:

    • Don't forget to register your ActiveState service as a Provider in:

      @NgModule({
        ...
        providers:[ActiveState]
        ...
      })
      export class AppModule { }
      
    • Simpler - Non-Observable Version I've used an Observable<string> so changes to the active state can be subscribed to, but this could be simplified to just be a string if you don't want that functionality:

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

      Then when you inject state : ActiveState you can simple test the value state.is("Point")

    I hope that's useful.

    0 讨论(0)
  • 2020-12-24 10:14

    I believe there is an issue with Scott's answer where he uses the ActivatedRoute inside the constructor of the service. This route won't get updated.

    I thought of another solution which might peek your interest. It again comes down on using the data property on the routes, but now using another resolve service:

    You are going to need a RouterConfig like this, where for each route you add the state: StateResolve and a data object containing the state name:

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

    don't forget to add the StateResolve service to the providers array

    Your StateResolve service will look something like this:

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

    Obviously you will need a StateService which has the setState method, but I guess from here it's pretty self-explanatory.

    Perhaps using a resolve guard is a bit eccentric, but if you think about it, you are trying to resolve data before you show the route. In this case, the state inside the data variable, so it does make sense to use the Resolve to access the data property

    0 讨论(0)
提交回复
热议问题