我只是想知道在TypeScript中你是否可以在类或接口上定义自定义事件?
这会是什么样的?
答案 0 :(得分:96)
这个简化事件如何用作财产?拥有类的更强类型,没有继承要求:
interface ILiteEvent<T> {
on(handler: { (data?: T): void }) : void;
off(handler: { (data?: T): void }) : void;
}
class LiteEvent<T> implements ILiteEvent<T> {
private handlers: { (data?: T): void; }[] = [];
public on(handler: { (data?: T): void }) : void {
this.handlers.push(handler);
}
public off(handler: { (data?: T): void }) : void {
this.handlers = this.handlers.filter(h => h !== handler);
}
public trigger(data?: T) {
this.handlers.slice(0).forEach(h => h(data));
}
public expose() : ILiteEvent<T> {
return this;
}
}
像这样使用:
class Security{
private readonly onLogin = new LiteEvent<string>();
private readonly onLogout = new LiteEvent<void>();
public get LoggedIn() { return this.onLogin.expose(); }
public get LoggedOut() { return this.onLogout.expose(); }
// ... onLogin.trigger('bob');
}
function Init() {
var security = new Security();
var loggedOut = () => { /* ... */ }
security.LoggedIn.on((username?) => { /* ... */ });
security.LoggedOut.on(loggedOut);
// ...
security.LoggedOut.off(loggedOut);
}
改进?
答案 1 :(得分:13)
Strongly Typed Events for TypeScript项目(版本0.3)实现了3种类型的事件:IEvent<TSender, TArgs>
,ISimpleEvent<TArgs>
和ISignal
。这样可以更轻松地为项目使用正确的事件。它还隐藏了事件中的调度方法,因为良好的信息隐藏应该这样做。
事件类型/接口 - 事件的定义:
interface IEventHandler<TSender, TArgs> {
(sender: TSender, args: TArgs): void
}
interface ISimpleEventHandler<TArgs> {
(args: TArgs): void
}
interface ISignalHandler {
(): void;
}
示例 - 此示例显示了如何使用滴答时钟实现3种类型的事件:
class Clock {
//implement events as private dispatchers:
private _onTick = new SignalDispatcher();
private _onSequenceTick = new SimpleEventDispatcher<number>();
private _onClockTick = new EventDispatcher<Clock, number>();
private _ticks: number = 0;
constructor(public name: string, timeout: number) {
window.setInterval( () => {
this.Tick();
}, timeout);
}
private Tick(): void {
this._ticks += 1;
//trigger event by calling the dispatch method and provide data
this._onTick.dispatch();
this._onSequenceTick.dispatch(this._ticks);
this._onClockTick.dispatch(this, this._ticks);
}
//expose the events through the interfaces - use the asEvent
//method to prevent exposure of the dispatch method:
public get onTick(): ISignal {
return this._onTick.asEvent();
}
public get onSequenceTick() : ISimpleEvent<number>{
return this._onSequenceTick.asEvent();
}
public get onClockTick(): IEvent<Clock, number> {
return this._onClockTick.asEvent();
}
}
用法 - 可以像这样使用:
let clock = new Clock('Smu', 1000);
//log the ticks to the console
clock.onTick.subscribe(()=> console.log('Tick!'));
//log the sequence parameter to the console
clock.onSequenceTick.subscribe((s) => console.log(`Sequence: ${s}`));
//log the name of the clock and the tick argument to the console
clock.onClockTick.subscribe((c, n) => console.log(`${c.name} ticked ${n} times.`))
在此处阅读更多内容:On events, dispatchers and lists (a general explanation of the system)
<强>教程强>
我已经写过一些关于这个主题的教程:
答案 2 :(得分:11)
我认为你在问一个类实例是否可以像DOM元素一样实现addEventListener()和dispatchEvent()。如果该类不是DOM节点,那么您必须编写自己的事件总线。您可以为可以发布事件的类定义接口,然后在类中实现接口。这是一个天真的例子;
interface IEventDispatcher{
// maintain a list of listeners
addEventListener(theEvent:string, theHandler:any);
// remove a listener
removeEventListener(theEvent:string, theHandler:any);
// remove all listeners
removeAllListeners(theEvent:string);
// dispatch event to all listeners
dispatchAll(theEvent:string);
// send event to a handler
dispatchEvent(theEvent:string, theHandler:any);
}
class EventDispatcher implement IEventDispatcher {
private _eventHandlers = {};
// maintain a list of listeners
public addEventListener(theEvent:string, theHandler:any) {
this._eventHandlers[theEvent] = this._eventHandlers[theEvent] || [];
this._eventHandlers[theEvent].push(theHandler);
}
// remove a listener
removeEventListener(theEvent:string, theHandler:any) {
// TODO
}
// remove all listeners
removeAllListeners(theEvent:string) {
// TODO
}
// dispatch event to all listeners
dispatchAll(theEvent:string) {
var theHandlers = this._eventHandlers[theEvent];
if(theHandlers) {
for(var i = 0; i < theHandlers.length; i += 1) {
dispatchEvent(theEvent, theHandlers[i]);
}
}
}
// send event to a handler
dispatchEvent(theEvent:string, theHandler:any) {
theHandler(theEvent);
}
}
答案 3 :(得分:2)
您可以在TypeScript中使用自定义事件。我不确定你想要做什么,但这是一个例子:
module Example {
export class ClassWithEvents {
public div: HTMLElement;
constructor (id: string) {
this.div = document.getElementById(id);
// Create the event
var evt = document.createEvent('Event');
evt.initEvent('customevent', true, true);
// Create a listener for the event
var listener = function (e: Event) {
var element = <HTMLElement> e.target;
element.innerHTML = 'hello';
}
// Attach the listener to the event
this.div.addEventListener('customevent', listener);
// Trigger the event
this.div.dispatchEvent(evt);
}
}
}
如果您想要做更具体的事情,请告诉我。
答案 4 :(得分:1)
您可以在YouTube处找到事件调度程序声明。观看视频后,您将拥有事件分发程序的完整版本
答案 5 :(得分:0)
此解决方案允许您直接在函数调用中编写参数,而无需将所有参数包装在对象中。
interface ISubscription {
(...args: any[]): void;
}
class PubSub<T extends ISubscription> {
protected _subscribed : ISubscriptionItem[] = [];
protected findSubscription(event : T) : ISubscriptionItem {
this._subscribed.forEach( (item : ISubscriptionItem) =>{
if (item.func==event)
return item;
} );
return null;
}
public sub(applyObject : any,event : T) {
var newItem = this.findSubscription(event);
if (!newItem) {
newItem = {object : applyObject, func : event };
this._subscribed.push(newItem);
this.doChangedEvent();
}
}
public unsub(event : T) {
for ( var i=this._subscribed.length-1 ; i>=0; i--) {
if (this._subscribed[i].func==event)
this._subscribed.splice(i,1);
}
this.doChangedEvent();
}
protected doPub(...args: any[]) {
this._subscribed.forEach((item : ISubscriptionItem)=> {
item.func.apply(item.object, args);
})
}
public get pub() : T {
var pubsub=this;
var func= (...args: any[]) => {
pubsub.doPub(args);
}
return <T>func;
}
public get pubAsync() : T {
var pubsub=this;
var func = (...args: any[]) => {
setTimeout( () => {
pubsub.doPub(args);
});
}
return <T>func;
}
public get count() : number {
return this._subscribed.length
}
}
用法:
interface ITestEvent {
(test : string): void;
}
var onTestEvent = new PubSub<ITestEvent>();
//subscribe to the event
onTestEvent.sub(monitor,(test : string) => {alert("called:"+test)});
//call the event
onTestEvent.pub("test1");
答案 6 :(得分:0)
如果您希望使用标准发射器模式进行智能类型检查,现在可以执行以下操作:
type DataEventType = "data";
type ErrorEventType = "error";
declare interface IDataStore<TResponse> extends Emitter {
on(name: DataEventType, handler : (data: TResponse) => void);
on(name: ErrorEventType, handler: (error: any) => void);
}
答案 7 :(得分:0)
这是使用TypeScript进行事件订阅的最基本实现:
class Subscription {
private unsub: () => void;
constructor(unsub: () => void) {
this.unsub = unsub;
}
public unsubscribe(): void {
if (this.unsub) {
this.unsub();
this.unsub = null; // to prevent repeated calls
}
}
}
class Observable<T = any> {
protected subs: ((data: T) => void)[] = [];
public subscribe(cb: (data: T) => void): Subscription {
this.subs.push(cb);
return new Subscription(() => {
this.subs.splice(this.subs.indexOf(cb), 1);
});
}
public next(data: T): void {
// we iterate through a safe clone, in case an un-subscribe occurs:
[...this.subs].forEach(cb => nextCall(() => cb(data)));
}
}
// for compatibility with web browsers:
const nextCall = typeof process === 'undefined' ? setTimeout : process.nextTick;
如果您喜欢简单的话,我只会在subcount包中稍加扩展;)
答案 8 :(得分:0)
您可以使用 rxjs 来实现这一点。
在您的班级中声明以下内容:
module-info.java
然后你可以这样触发事件:
export class MyClass {
private _eventSubject = new Subject();
public events = this._eventSubject.asObservable();
public dispatchEvent(data: any) {
this._eventSubject.next(data);
}
}
并通过以下方式收听此事件:
let myClassInstance = new MyClass();
myClassInstance.dispatchEvent(data);