Reputation: 9574
I am trying to figure out how to create a custom event for a class in TypeScript. Examples like this one didn't help me much in understanding how to do that.
My example class looks like the following.
Cat.ts:
export class Cat {
public getName(): string {
return this.catName;
}
public setName(catName: string) {
this.catName = catName;
}
constructor(private catName: string) { }
public makeMeow() {
this.onWillMeow();
console.log("Cat meows!");
this.onDidMeow();
}
public onWillMeow() {
console.log("onWillMeow");
}
public onDidMeow() {
console.log("onDidMeow");
}
}
Now I would like to be able to declare events from outside like the following code aims to demonstrate.
const myCat: Cat = new Cat("Tikki");
myCat.onWillMeow({event => {
console.log("Tikki the cat is just about to meow!");
}});
myCat.onWillMeow({event => {
console.log("Tikki the cat did just meow!");
}});
myCat.makeMeow();
Now, I would imagine to get some output like this one:
onWillMeow
Tikki the cat is just about to meow!
Cat meows!
onDidMeow
Tikki the cat did just meow!
What do I have to do to make this work in TypeScript? How is this called exactly? Creating a custom event or creating a custom event handler?
Upvotes: 5
Views: 8172
Reputation: 25840
When implementing custom events today, it is important that the event is strongly-typed, i.e. the type of data that the event sends is inferred during transpile time, to make the code less error-prone.
There are several libraries today that can do that. Here's is how you can do it with sub-events:
import {SubEvent} from 'sub-events';
class Cat {
// strongly-typed event that expects a string:
readonly onMeow: SubEvent<string> = new SubEvent();
constructor(private catName: string) {
}
public makeMeow(message: string) {
this.onMeow.emit(`${this.catName} ${message}`);
}
}
const myCat = new Cat('Tikki');
myCat.onMeow.subscribe(message => {
// message is strongly-typed here;
console.log(message); //-> Tikki cat is doing something
});
myCat.makeMeow('cat is doing something'); // send the event
Since you are sending the same type of event that differs only by the message, you would normally generalize it into a single event. That's why in the example I simplified it to just one event onMeow
.
Also, subscribe
is pretty much the standard approach today, which let's you easily cancel the subscription whenever you want, as shown below:
const sub = myCat.onMeow.subscribe(message => {
console.log(message); // message is strongly-typed here;
});
When the event is no longer needed, you cancel the subscription:
sub.cancel();
Upvotes: 2
Reputation: 30879
Something like this:
type Handler<E> = (event: E) => void;
class EventDispatcher<E> {
private handlers: Handler<E>[] = [];
fire(event: E) {
for (let h of this.handlers)
h(event);
}
register(handler: Handler<E>) {
this.handlers.push(handler);
}
}
interface WillMeowEvent { }
interface DidMeowEvent { }
class Cat {
public getName(): string {
return this.catName;
}
public setName(catName: string) {
this.catName = catName;
}
constructor(private catName: string) { }
public makeMeow() {
this.fireWillMeow({});
console.log("Cat meows!");
this.fireDidMeow({});
}
private willMeowDispatcher = new EventDispatcher<WillMeowEvent>();
public onWillMeow(handler: Handler<WillMeowEvent>) {
this.willMeowDispatcher.register(handler);
}
private fireWillMeow(event: WillMeowEvent) {
console.log("onWillMeow");
this.willMeowDispatcher.fire(event);
}
private didMeowDispatcher = new EventDispatcher<DidMeowEvent>();
public onDidMeow(handler: Handler<DidMeowEvent>) {
this.didMeowDispatcher.register(handler);
}
private fireDidMeow(event: DidMeowEvent) {
console.log("onDidMeow");
this.didMeowDispatcher.fire(event);
}
}
const myCat: Cat = new Cat("Tikki");
myCat.onWillMeow(event => {
console.log("Tikki the cat is just about to meow!");
});
myCat.onDidMeow(event => {
console.log("Tikki the cat did just meow!");
});
myCat.makeMeow();
I'm sure there are libraries that can help. Anyone want to recommend a library in another answer?
Upvotes: 11