2012-10-14 10 views

risposta

83

Che ne dici di questo evento semplificato da utilizzare come proprietà? Più forte tipizzazione della classe proprietaria e nessun requisito eredità:

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

usato in questo modo:

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

Miglioramenti?

A gist for this

+0

se due diverse istanze associano lo stesso metodo, penso che "off" possa rimuovere quello errato. (poiché i metodi sono su prototipo quindi sono la stessa istanza per ogni istanza di oggetto) – Kikaimaru

+0

@Kikaimaru - Non capisco. Puoi elaborare? –

+0

Se si desidera rimuovere un gestore, si dovrebbe probabilmente mantenere un riferimento alla funzione di gestione, in questo modo: 'var handler1 = function (data) {alert ('handler1'); }; var handler2 = function (data) {alert ('handler2'); }; var ev = new Event(); ev.on (handler1); ev.on (handler2); ev.trigger(); ev.off (handler1); ev.handlers; alert ('Invoca round 2'); ev.trigger(); ' – VeeTheSecond

2

È possibile utilizzare eventi personalizzati in TypeScript. Non sono sicuro esattamente quello che stai cercando di fare, ma qui è un esempio:

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

Se stai cercando di fare qualcosa di più specifico per favore fatemelo sapere.

9

Penso che stai chiedendo se un'istanza di classe può implementare addEventListener() e dispatchEvent() come un elemento DOM. Se la classe non è un nodo DOM, allora dovresti scrivere il tuo bus evento. Definiresti un'interfaccia per una classe in grado di pubblicare eventi, quindi implementare l'interfaccia nelle tue classi. Ecco un esempio ingenuo;

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

Questa soluzione consente di scrivere direttamente i parametri nella chiamata di funzione invece di aver bisogno di avvolgere tutti i parametri di un oggetto.

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 
    } 

} 

Usage:

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

Giusto il voto senza spiegazione è meno utile – daslicht

-1

Se siete alla ricerca di ottenere tipo intelli-senso il controllo utilizzando il modello emettitore di serie è ora possibile effettuare le seguenti operazioni:

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

Il Strongly Typed Events for TypeScript progetto (versione 0.3) implementa 3 tipi di eventi: IEvent<TSender, TArgs>, ISimpleEvent<TArgs> e ISignal. Questo rende più facile usare il giusto tipo di evento per il tuo progetto. Nasconde anche il metodo di invio dal tuo evento, come dovrebbe essere un buon nascondiglio delle informazioni.

tipi di evento/Interfacce - Le definizioni degli eventi:

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

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

interface ISignalHandler { 
    (): void; 
} 

Esempio - Questo esempio mostra come i 3 tipi di eventi possono essere implementate utilizzando un orologio:

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

Uso - Può essere utilizzato in questo modo:

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

Per saperne di più qui: On events, dispatchers and lists (a general explanation of the system)

Tutorial
che ho scritto un paio di tutorial sul tema:

+1

grazie, molto bello! – daslicht

+1

Questo è esattamente quello che volevo grazie! – ToastyMallows

Problemi correlati