Is there a way to check for @Output wire up from within a component in Angular?
AngularTypescriptAngular Problem Overview
In the ngOnInit
method of a component the @Input
values will have been bound so you can check those properties on the component, but there doesn't seem to be a way to check @Output
event bindings. I want to be able to know if the @Output
was wired up on the component or not.
(using Angular and TypeScript)
import {Component, Output, EventEmitter} from '@angular/core';
@Component({
selector: 'sample',
template: `<p>a sample</p>`
})
export class SampleComponent {
@Output() cancel = new EventEmitter();
ngOnInit() {
// would like to check and see if cancel was used
// on the element <sample (cancel)="doSomething()"></sample>
// or not <sample></sample>
}
}
Angular Solutions
Solution 1 - Angular
Same approach as user1448982 but without using the ObservableWrapper
that is meant to be platform code that is not exposed for use via the api.
(Using Angular 2 RC1 and TypeScript)
Note: This only started working from 2.0.0-beta.16 and greater
import {Component, Output, EventEmitter} from '@angular/core';
@Component({
selector: 'sample',
template: `<p>a sample</p>`
})
export class SampleComponent {
@Output() cancel = new EventEmitter();
private isCancelUsed = false;
ngOnInit() {
this.isCancelUsed = this.cancel.observers.length > 0;
}
}
The ObservableWrapper.hasSubscribers
method does this line internally, so you can just do the same thing here.
When using TypeScript you will get a transpile time error if Angular ever ends up changing the EventEmitter
from a Subject
(which is part Observable
, thus the .observers
property).
Update (03 / 2022)
The observers
attribute is deprecated since rxjs v7. Instead of checking the length of the observers array, you can now use a boolean that indicates if the subject is in use.
// Old approach
this.isCancelUsed = this.cancel.observers.length > 0;
// New approach
this.isCancelUsed = this.cancel.observed;
Solution 2 - Angular
Angular 12 removed the EventEmitter#observables
field so the accepted answer is no longer valid.
An alternative solution for now would be to cast to a Subject instead:
get hasOutputEventSubscriber() {
return (this.cancel as Subject).observers;
}
Note that this property is also deprecated and will be removed in rxjs v8. A future-proof solution would be to write a custom wrapper class for the EventEmitter.
Solution 3 - Angular
The following code should work:
import {Component, Output, EventEmitter, OnInit} from 'angular2/core';
import {ObservableWrapper} from 'angular2/src/facade/async';
@Component({
selector: 'sample',
template: `<p>a sample</p>`
})
export class SampleComponent implements OnInit {
@Output() cancel: EventEmitter<any> = new EventEmitter();
private isCancelUsed: boolean;
ngOnInit(): void {
this.isCancelUsed = ObservableWrapper.hasSubscribers(this.cancel);
}
}
Solution 4 - Angular
Here is a future proof example that does not rely on any internals of Angular or rxjs. This is a simple wrapper around EventEmitter
that exposes a subscriberCount
property that is automatically incremented/decremented as subscribers are added/removed.
class WatchedEventEmitter extends EventEmitter<any> {
private _subscriberCount = 0;
get subscriberCount(): number {
return this._subscriberCount;
}
subscribe(next?: (value: any) => void, error?: (error: any) => void, complete?: () => void): Subscription {
++this._subscriberCount;
return super.subscribe(next, error, complete);
}
unsubscribe() {
--this._subscriberCount;
super.unsubscribe();
}
}
Usage example:
@Component({template: `
{{click.subscriberCount|json}}
<span *ngIf="cancel.subscriberCount">
<button mat-icon-button (click)="cancel.emit()">Click Me</button>
</span>
<span *ngIf="!cancel.subscriberCount">No Subscribers</span>
`})
export class SomeComponent {
@Output() cancel = new WatchedEventEmitter();
}
Solution 5 - Angular
as of now current version Angular 13 to check if is there any observer/callback is provided you can use this.cancel.observed
which will return boolean
import {Component, Output, EventEmitter} from '@angular/core';
@Component({
selector: 'sample',
template: `<p>a sample</p>`
})
export class SampleComponent {
@Output() cancel = new EventEmitter();
private isCancelUsed = false;
ngOnInit() {
this.isCancelUsed = this.cancel.observed;
}
}