Kesarion Kesarion - 6 months ago 84
Javascript Question

How can I get a directive/component instance inside another component?

I have an AlertComponent that I would like to use as a directive in my AppComponent and expose it so that it's available (as a sort of singleton) to all the routes/children components from AppComponent. But I can't seem to find a way to get the instance of the AlertComponent object used as a directive in order to call it's methods and see the changes made on the directive (i.e. add/remove alerts to/from the page).

Here is AlertComponent:

import { Component } from 'angular2/core';

import { Alert } from './model';

@Component({
selector: 'alerts',
templateUrl: './alert/index.html'
})
export class AlertComponent {
alerts: Array<Alert>;

constructor() {}

add(alert: Alert) {
this.alerts.push(alert);
}

remove(index: number) {
this.alerts.splice(index, 1);
}

clear() {
this.alerts = [];
}
}

export { Alert };


And AppComponent:

import { Component, OnInit, provide } from 'angular2/core';
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router';
import { HTTP_PROVIDERS, RequestOptions } from 'angular2/http';
import { CookieService } from 'angular2-cookie/core';

import { UserComponent } from '../user/component';
import { AlertComponent, Alert } from '../alert/component';

import { ExtendedRequestOptions } from '../extended/RequestOptions';
import { UtilObservable } from '../util/observable';

@Component({
selector: 'app',
template: `
<alerts></alerts>
<router-outlet></router-outlet>
`,
//styleUrls: [ 'app/style.css' ],
directives: [
ROUTER_DIRECTIVES,
AlertComponent
],
providers: [
ROUTER_PROVIDERS,
HTTP_PROVIDERS,
provide(RequestOptions, { useClass: ExtendedRequestOptions }),
CookieService,
UtilObservable,
AlertComponent
]
})
@RouteConfig([{
path: '/user/:action',
name: 'User',
component: UserComponent,
useAsDefault: true
}
])
export class AppComponent implements OnInit {

constructor(public _alert: AlertComponent) {}

ngOnInit() {
this._alert.add(new Alert('success', 'Success!'));
}
}


I'd like to have the same instance of AlertComponent available to all descendant routes/children of AppComponent (e.g. UserComponent), so as to add alerts to the same directive.

Is this possible? Or is there another, more proper way to do this?

[Update]

The chosen solution answers the title question, but I also wanted to have a simple solution to share alerts among my components. Here's how to do that:

AlertComponent:

import { Component } from 'angular2/core';

import { Alert } from './model';

export class Alerts extends Array<Alert> {}

@Component({
selector: 'alerts',
templateUrl: './alert/index.html'
})
export class AlertComponent {
constructor(public alerts: Alerts) {}

add(alert: Alert) {
this.alerts.push(alert);
}

remove(index: number) {
this.alerts.splice(index, 1);
}

clear() {
this.alerts.length = 0;
}
}

export { Alert };


AppComponent:

import { Component, provide } from 'angular2/core';
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from 'angular2/router';
import { HTTP_PROVIDERS, RequestOptions } from 'angular2/http';

import { AlertComponent, Alerts } from '../alert/component'
import { UserComponent } from '../user/component';

import { ExtendedRequestOptions } from '../helpers/extensions';

@Component({
selector: 'app',
template: `<router-outlet></router-outlet>`,
directives: [
ROUTER_DIRECTIVES
],
viewProviders: [
provide(Alerts, { useValue: [] })
],
providers: [
ROUTER_PROVIDERS,
HTTP_PROVIDERS,
provide(RequestOptions, { useClass: ExtendedRequestOptions })
]
})
@RouteConfig([{
path: '/user/:action',
name: 'User',
component: UserComponent,
useAsDefault: true
}
])
export class AppComponent {}


Basically, I'm providing a singleton array of alerts that's used by every AlertComponent.

You can move the provide() to providers (instead of viewProviders) if you want to use it outside of directives, but if not, keep it simple and restrict it this way.

Hope this helps someone :)

Answer

You need to use ViewChild decorator to reference it:

@Component({
})
export class AppComponent implements OnInit {
  @ViewChild(AlertComponent)
  _alert: AlertComponent;

  ngAfterViewInit() {
    // Use _alert
    this._alert.add(new Alert('success', 'Success!'));
  }
}

@ViewChild is set before the ngAfterViewInit hook method is called.

Comments