Angular 2 - Проверить текущий активный маршрут "имя"

У меня есть приложение Angular 2 с несколькими представлениями вложенных детей. Но он будет отображаться на одной странице, хотя несколько 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
    }
}

Так что в основном я могу путешествовать по списку маршрутов

  • /очередь
  • /очередь/:/Дата: офсет/Тип
  • /очередь/:/Дата: офсет/Тип/: BundleID
  • /очередь/:/Дата: офсет/Тип/: BundleID/: pointId
  • /очередь/:/Дата: офсет/Тип/: BundleID/: pointId/: TaskId

Например

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

Представьте, что у вас есть страница с некоторым элементом:

  • Одна часть пользовательского интерфейса показала список фильмов
  • Другая часть показывает детали фильма при нажатии на элемент в списке фильмов, но отображается на той же странице.
  • Еще одна часть, чтобы показать детали персонажа, когда вы нажимаете на имя символа в деталях фильма, а также показываете на той же странице.
  • и т.д...

В принципе, я все еще могу щелкнуть в списке фильмов, пока я просматриваю детали персонажа.

Поиск определения имени для каждого маршрута, но, похоже, все ответы на ответ, эта функция уже удалена из Angular 2 final. В Angular 1 с использованием UI-маршрутизатора я могу определить имя для каждого маршрута и легко получить маршрут с помощью встроенной функции state.is(ROUTE_NAME).

Итак, что я делаю сейчас, это база на window.location, чтобы получить URL-адрес и разбить эту строку на /, чтобы получить количество параметров. Но это более жестко закодированное.

Есть ли опыт в том, чтобы делать то же самое? Как я могу различить, какой маршрут в данный момент активен?

Ответ 1

Я считаю, что есть проблема с ответом Скотта, где он использует ActivatedRoute внутри конструктора службы. Этот маршрут не будет обновлен.

Я подумал о другом решении, которое могло бы заглянуть в ваш интерес. Он снова опускается с использованием свойства data на маршрутах, но теперь использует другую службу разрешения:

Вам понадобится RouterConfig, как это, где для каждого маршрута вы добавляете state: StateResolve и объект данных, содержащий имя состояния:

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

не забудьте добавить службу StateResolve в массив поставщиков

Ваша служба StateResolve будет выглядеть примерно так:

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

Очевидно, вам понадобится StateService, у которого есть метод setState, но я думаю, что здесь он довольно понятен.

Возможно, использование resolve guard немного эксцентрично, но если вы думаете об этом, вы пытаетесь разрешить данные, прежде чем показывать маршрут. В этом случае состояние внутри переменной данных, поэтому имеет смысл использовать resolve для доступа к свойству data

Ответ 2

Создайте службу с именем ActiveState, которая будет subscribe изменена на маршрутизатор, используя 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;
  }
}

Затем на вашем маршруте мы добавим простое значение в параметре data маршрута с именем stateName для каждого состояния, которое мы хотим назвать:

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

Затем, когда вы вводите state : ActiveState, вы можете просто проверить значение state.is("Point")

Ответ 3

  • Я бы создал простой сервис, который отслеживает активное состояние.
  • Эта служба может быть добавлена, если требуется, чтобы получить или установить активное состояние.
  • Вы уже используете Resolvers, чтобы установить идентификатор состояния там.

Создайте службу под названием 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);
  }
}

В ваших распознавателях, таких как PointListResolve... TaskListResolve и т.д.

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

Поэтому в других преобразователях обновляется значение this.state.setActive("") по мере необходимости.


Затем, чтобы определить, в каком состоянии вы находитесь, введите ActiveState, где вы хотите использовать текущее состояние, например, в @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();
  }
}

Примечания:

  • Не забудьте зарегистрировать свою службу ActiveState как Provider в:

    @NgModule({
      ...
      providers:[ActiveState]
      ...
    })
    export class AppModule { }
    
  • Упрощенная версия, не наблюдаемая. Я использовал Observable<string>, поэтому изменения в активном состоянии могут быть subscribed to, но это может быть упрощено, чтобы просто быть string если вы не хотите использовать эту функцию:

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

    Затем, когда вы вводите state : ActiveState, вы можете просто проверить значение state.is("Point")

Я надеюсь, что это полезно.

Ответ 4

name был удален довольно давно из маршрутов, но маршруты позволяют добавлять произвольные данные

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

Этот код создает заголовок из имен всех сегментов маршрута. Вероятно, это может быть упрощено для вашего использования.

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

См. также Изменение названия страницы с помощью Angular 2 нового маршрутизатора

Ответ 5

мой ответ похож, но сделал по-другому, поэтому я думаю, что это хорошо, чтобы опубликовать

Что отличается: мне не нужно ничего менять на моих маршрутах, я сделал сервис для отслеживания более глубокого ActivatedRoute (inside или firstChild... firstChild)

создать сервис

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

затем в app.component.ts я запускаю сервис (просто чтобы он всегда отслеживался)

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

и, наконец, проложите свой маршрут, где бы вы ни находились:

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

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

Отвечая на вопрос, вы можете просто взять deeperActivationRoute и проверить обычно snapshop.url, так же, как вы сделали бы в компоненте

Ответ 6

Попробуй это:

this.router.url.split("/")[3] //change number to get needed :route