在 Angular 2 的路由器中获取对当前路由组件的引用?

Get a reference to a component of current route in Angular 2's router?

有没有一种干净的方法来获取对当前路由组件的引用?

这似乎可行,但似乎很老套:

 this.router.currentInstruction.
  component.componentType.prototype.somePropertyOrFunction();

事实上,您无权使用表达式访问与当前路由关联的组件实例。您只能获得组件类型。这就是为什么您需要使用原型但您获得了对函数的引用而不是对组件实例的方法的引用。

主要后果是 this 关键字(如果在方法中使用)将未定义。

要访问与当前路由关联的组件实例,您可以利用 OnActivate 钩子接口在路由激活时将此实例设置为共享服务:

@Component({
  selector: 'some-cmp',
  template: `
    <div>routerOnActivate: {{log}}</div>
  `})
  export class SomeCmp implements OnActivate {
    constructor(private service:RouteStateService) {
    }

    routerOnActivate(next: ComponentInstruction, prev: ComponentInstruction) {
      this.service.setCurrentRouteComponent(this);
    }
 }

那么您就可以通过这种方式访问​​组件实例:

var component = this.service.getCurrentRouteComponent();

可以使用 RouterOutletMap 访问组件实例,这是 Angular 用来存储 router-outlet 指令呈现的实例的一种有状态服务。您可能需要这些类型:

import { Router, RouterOutletMap, RouterOutlet } from '@angular/router';

Top-level outletMaprouter 的 属性,但它在 TS 中不可见,因此您可能需要强制转换为 any:

constructor(private router:Router) {
    const rootOutletMap:RouterOutletMap = (<any>this.router).outletMap;
}

RouterOutletMap 是一个 javascript object,它有一个 属性 _outlets。此 _outlets 是一个 hash-map-object,包含 RouterOutlet 个出口名称实例。如果插座没有名称,它将是 'primary'。得到RouterOutlet之后就可以抓取组件实例了:

const rootOutletMap:RouterOutletMap = (<any>this.router).outletMap,
      outletsHash = (<any>rootOutletMap)._outlets,
      primaryOutlet:RouteOutlet = outletsHash['primary'], // 'primary' is the default name

      component = primaryOutlet.component; // <-- this is the instance

RouterOutletMap是一棵树,所以根图有对children的引用等等。如果需要,您可以组织一个查找过程来查找特定路径的组件。这是递归组件提取的示例:

// returns an array of all the router-outlets component currently rendered
private getComponentsChain(routerOutletMap: RouterOutletMap) {
    const outlets: any = (<any>routerOutletMap)._outlets,
          outletsList = [],
          components = [];

    _.forOwn(outlets, (value: RouterOutlet, key: string) => {
        outletsList.push(value);
        components.push(value.component);
    });

    const recursiveComponents = _.flatMap(
        outletsList,
        (o: RouterOutlet) => this.getComponentsChain(o.outletMap));

    return [...components, ...recursiveComponents];
}

/* ...then somewhere in component: */
console.log(this.getComponentsChain((<any>this.router).outletMap));

走这条路需要注意的几点:

  • 它使用 non-public angular 功能,因此将来可能会更改;
  • 路由器插座有它自己的生命周期,如果你在错误的时刻选择它,你可能会得到 'Outlet is not activated' 错误。

您可以从 CanActivate 守卫中获取组件的类型。例如:

    @Injectable()
    export class MyGuard: CanActivate
    {
      canActivate(route: ActivatedRouteSnapshot) : boolean{
         let componentType = route.routeConfig.component;
      }
    }

我找到的唯一简短(但很老套)的方法是下一个(Angular 4、6、7、8):

const currentComponent = (router as any).rootContexts.contexts 
  && (router as any).rootContexts.contexts.get('primary')
  && (router as any).rootContexts.contexts.get('primary').outlet.component;

注意:这是一种 HACK,如果您有多个路由器插座等,则可能无法正常工作。