Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Is there a clean way to get a reference to the component of current route?

This seems to work, but seems very hacky:

 this.router.currentInstruction.
  component.componentType.prototype.somePropertyOrFunction();
like image 959
wannabeartist Avatar asked Mar 24 '16 12:03

wannabeartist


3 Answers

In fact, you don't have access to the component instance associated to the current route with your expression. You only get the component type. That's why you need to use the prototype but you get a reference to function and don't reference to methods of the component instance.

The main consequence will be that the this keyword (if used within the methods) will be undefined.

To access the component instance associated to the current route, you could leverage the OnActivate hook interface to set this instance into a shared service when the route is activated:

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

Then you would be able to access the component instance this way:

var component = this.service.getCurrentRouteComponent();
like image 112
Thierry Templier Avatar answered Oct 19 '22 02:10

Thierry Templier


It is possible to access the component instance using RouterOutletMap that is a kind of stateful service used by Angular to store instances rendered by router-outlet directive. You might need these types:

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

Top-level outletMap is a property of the router, but it's not visible in TS so you might need cast to any:

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

The RouterOutletMap is a javascript object that has a property _outlets. This _outlets is a hash-map-object containing RouterOutlet instances by the outlet name. If an outlet has no name, it will be 'primary'. After you got RouterOutlet you can grab the component instance:

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 is a tree, so the root map has a reference to children and so on. You can organize a lookup procedure to find a component for a particular route if you need to. Here is an example of recursive components extraction:

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

Few points to note if you go this way:

  • it uses non-public angular features so that might change in the future;
  • router outlets have it's own lyfecycle, you might get 'Outlet is not activated' error if you pick it at the wrong moment.
like image 4
admax Avatar answered Oct 19 '22 00:10

admax


Only short (but hacky) way I found is next (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;

BEWARE: that's a HACK, may not work if you have more than one router outlet etc.

like image 1
Vladimir Tolstikov Avatar answered Oct 19 '22 01:10

Vladimir Tolstikov