2012-10-14 5 views
43

Ich frage mich, ob Sie in TypeScript benutzerdefinierte Ereignisse für Ihre Klassen oder Schnittstellen definieren können?Unterstützt TypeScript Ereignisse in Klassen?

Wie würde das aussehen?

+0

Check this out: - http://stackoverflow.com/questions/12756423/is-there-an-alias-for-this-in-typescript –

Antwort

83

Wie wäre es mit diesem vereinfachten Ereignis, das als Eigenschaft verwendet werden soll? Stärkere Typisierung der besitzenden Klasse und keine Vererbung Bedarf:

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; 
    } 
} 

wie so verwendet:

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); 
} 

Verbesserungen?

A gist for this

+0

Wenn zwei verschiedene Instanzen dieselbe Methode anhängen, denke ich, dass "aus" dann die inkorrekte entfernen kann. (da Methoden auf dem Prototyp sind, so sind sie die gleiche Instanz für jede Objektinstanz) – Kikaimaru

+0

@Kikaimaru - Ich verstehe nicht. Kannst du es ausarbeiten? –

+0

Wenn Sie einen Handler entfernen möchten, sollten Sie wahrscheinlich einen Verweis auf die Handler-Funktion beibehalten: 'var handler1 = function (data) {alert ('handler1'); }; var handler2 = function (data) {alert ('handler2'); }; var ev = neues Ereignis(); ev.on (handler1); ev.on (handler2); ev.trigger(); ev.off (handler1); ev.Handler; alert ('Invoke round 2'); ev.trigger(); ' – VeeTheSecond

2

Sie können benutzerdefinierte Ereignisse in TypeScript verwenden. Ich bin nicht sicher genau das, was Sie zu tun versuchen, aber hier ist ein Beispiel:

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); 
     } 
    } 
} 

Wenn Sie schauen, um etwas zu tun spezifischere lass es mich wissen.

9

Ich denke, Sie fragen, ob eine Klasseninstanz addEventListener() und dispatchEvent() wie ein DOM-Element implementieren kann. Wenn die Klasse kein DOM-Knoten ist, müssten Sie Ihren eigenen Ereignisbus schreiben. Sie würden eine Schnittstelle für eine Klasse definieren, die Ereignisse veröffentlichen kann, und dann die Schnittstelle in Ihren Klassen implementieren. Hier ist ein naives Beispiel;

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); 
    } 
} 
0

Diese Lösung ermöglicht es Ihnen, die Parameter in dem Funktionsaufruf direkt zu schreiben, anstatt benötigen alle Parameter in einem Objekt zu wickeln.

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 
    } 

} 

Verbrauch:

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"); 
+0

Just Down Abstimmung ohne Erklärung ist weniger nützlich – daslicht

-1

Wenn Sie suchen Sie die folgenden jetzt tun mit dem Standard-Emitter-Muster überprüft Intelli-Sense-Typ zu bekommen:

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);  
} 
8

The Strongly Typed Events for TypeScript Projekt (Version 0.3) implementiert 3 Arten von Ereignissen: IEvent<TSender, TArgs>, ISimpleEvent<TArgs> und ISignal. Dies macht es einfacher, die richtige Art von Ereignis für Ihr Projekt zu verwenden. Es verbirgt auch die Dispatch-Methode aus Ihrem Event, da ein gutes Ausblenden von Informationen ausreichen sollte.

Ereignistypen/Schnittstellen - Die Definitionen der Ereignisse:

interface IEventHandler<TSender, TArgs> { 
    (sender: TSender, args: TArgs): void 
} 

interface ISimpleEventHandler<TArgs> { 
    (args: TArgs): void 
} 

interface ISignalHandler { 
    (): void; 
} 

Beispiel - Dieses Beispiel zeigt, wie die drei Arten von Ereignissen mit einer tickenden Uhr durchgeführt werden können:

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(); 
    } 
} 

Verwendung - Es kann wie folgt verwendet werden:

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.`)) 

Lesen Sie mehr hier: On events, dispatchers and lists (a general explanation of the system)

Tutorials
ich ein paar Tutorials zu diesem Thema geschrieben haben:

+1

danke, sehr nett! – daslicht

+1

Genau das wollte ich dir danken! – ToastyMallows

Verwandte Themen