我有一个带有几个嵌套子视图的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
}
}
所以基本上我可以浏览路线列表
例如
#/queue/2017-01-05/480/20/57f4c26507b36e3684007b52/1/57fb0abb07b36e39d8e88df8/1
想象一下,你有一个包含一些元素的页面:
基本上,在查看角色细节时,我仍然可以点击进入电影列表。
搜索定义每条路线的名称,但似乎所有答案报告此功能已从Angular 2最终删除。在使用UI路由器的Angular 1中,我可以定义每个路由的名称,并且可以使用内置函数state.is(ROUTE_NAME)
轻松获取路由。
所以我现在正在做的是基于window.location来获取URL并将此字符串拆分为/
以获取参数的数量。但它更难编码。
有关做同样事情的经验吗?我如何区分当前活跃的路线?
答案 0 :(得分:14)
使用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")
答案 1 :(得分:5)
我相信Scott的答案存在问题,他在服务的构造函数中使用了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
后卫有点古怪,但是如果你考虑一下,你就是在展示路线之前尝试解析数据。在这种情况下,数据变量中的状态,因此使用Resolve
访问data
属性
答案 2 :(得分:2)
创建名为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
中,即
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
,但这可以简化为如果你不想要这个功能,那就是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")
我希望这很有用。
答案 3 :(得分:2)
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;
}
}
答案 4 :(得分:0)
我的回答是相似的,但是用不同的方式,所以我认为发布是很好的
有什么不同:我不需要更改路线,我提供了一项服务来跟踪更深的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;
}
}
回答问题,就像在组件中一样,您只需采取deeperActivatedRoute并正常检查snapshop.url即可。
答案 5 :(得分:0)
尝试一下:
this.router.url.split("/")[3] //change number to get needed :route