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