Angular 1 has an utility for broadcasting an event, $broadcast
and $on
. Now, Angular 2 dropped these features. Instead it has @Output
annotation and (event)
syntax. However, we can create a similar utility by using DI; Broadcaster
.
Define Broadcaster
First, we have to define Broadcaster
.
import {Subject} from 'rxjs/Subject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
interface BroadcastEvent {
key: any;
data?: any;
}
export class Broadcaster {
private _eventBus: Subject<BroadcastEvent>;
constructor() {
this._eventBus = new Subject<BroadcastEvent>();
}
broadcast(key: any, data?: any) {
this._eventBus.next({key, data});
}
on<T>(key: any): Observable<T> {
return this._eventBus.asObservable()
.filter(event => event.key === key)
.map(event => <T>event.data);
}
}
Broadcaster
has two methods; broadcast
and on
. broadcast
will be used to fire an event with event-specific key. In other hand, on
returns an observable of events which broadcasted the key.
Angular 1’s $broadcast
is used with string key.
$broadcast('MyEvent', data)
Of course, we can implement string-based propagation that uses string keys.
// app.ts (application root component)
@Component({
selector: 'app',
...
providers: [
// application-shared broadcaster (similar to $rootScope)
Broadcaster
]
})
class App {}
// child.ts
@Component({
selector: 'child'
})
export class ChildComponent {
constructor(private broadcaster: Broadcaster) {
}
registerStringBroadcast() {
this.broadcaster.on<string>('MyEvent')
.subscribe(message => {
...
});
}
emitStringBroadcast() {
this.broadcaster.broadcast('MyEvent', 'some message');
}
}
Great, we regained $rootScope.$broadcast
! But there are some problems; loosing typo-safety and type-safety.
typo-safety
If we mistake the event name, event propagation will be broken, but we cannot get any static errors.
type-safety
We cannot know a type of the event data. Any types will be accepted to the event with same name.
$broadcast('MyEvent', '100');$broadcast('MyEvent', 100);
Type-based event propagation
So, we should make a safety broadcaster, which uses types to specify an event type.
Let’s make domain-specific broadcaster, MessageEvent
!
import {Injectable} from 'angular2/core';
import {Observable} from 'rxjs/Observable';
import {Broadcaster} from './broadcast';
@Injectable()
export class MessageEvent {
constructor(private broadcaster: Broadcaster) {}
fire(data: string): void {
this.broadcaster.broadcast(MessageEvent, data);
}
on(): Observable<string> {
return this.broadcaster.on<string>(MessageEvent);
}
}
Then, we can use it with perfect type-safety! Look at new child.ts
.
import {Component} from 'angular2/core';
import {MessageEvent} from '../../services/message_event';
@Component({
selector: 'child',
...
providers: [
MessageEvent
],
})
export class Child {
constructor(private messageEvent: MessageEvent) {
}
registerTypeBroadcast() {
this.messageEvent.on()
.subscribe(message => {
...
});
}
emitTypeBroadcast() {
this.messageEvent.fire(`Message from ${this.boxID}`);
}
}
Demo is here.
Conclusion
In this article, I explained a way to implement an event propagation like Angular 1’s $broadcast
. And I introduced old string-based event system and type-based safe event system.