角度2 - 检查当前活动路线"名称"

时间:2017-01-05 15:21:00

标签: angular angular2-routing

我有一个带有几个嵌套子视图的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 /:点名
  • /队列/:日期/:偏置/:类型/:bundleId /:点名/:的TaskID

例如

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

想象一下,你有一个包含一些元素的页面:

  1. 一个UI部分显示了一个电影列表
  2. 其他部分在点击影片列表中的项目时会显示影片详细信息,但会显示在同一页面上。
  3. 在电影明细处点击角色名称时显示角色详情的另一部分,也显示在同一页面上。
  4. 等...
  5. 基本上,在查看角色细节时,我仍然可以点击进入电影列表。

    搜索定义每条路线的名称,但似乎所有答案报告此功能已从Angular 2最终删除。在使用UI路由器的Angular 1中,我可以定义每个路由的名称,并且可以使用内置函数state.is(ROUTE_NAME)轻松获取路由。

    所以我现在正在做的是基于window.location来获取URL并将此字符串拆分为/以获取参数的数量。但它更难编码。

    有关做同样事情的经验吗?我如何区分当前活跃的路线?

6 个答案:

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

另见Changing the page title using the Angular 2 new router

答案 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