inicializar - interfaces o clases typescript
¿TypeScript admite eventos en las clases? (6)
¿Qué tal este evento simplificado para ser utilizado como una propiedad? Tipeo más fuerte de la clase propietaria y sin requisito de herencia:
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;
}
}
usado como tal:
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);
}
Mejoras?
Me pregunto si en TypeScript puedes definir eventos personalizados en tus clases o interfaces.
¿A qué se parecería?
Creo que estás preguntando si una instancia de clase puede implementar addEventListener () y dispatchEvent () como un elemento DOM. Si la clase no es un nodo DOM, entonces tendría que escribir su propio bus de eventos. Definiría una interfaz para una clase que puede publicar eventos, luego implementaría la interfaz en sus clases. Aquí hay un ejemplo 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);
}
}
El evento Strongly Typed Events for TypeScript (versión 0.3 ) implementa 3 tipos de eventos: IEvent<TSender, TArgs>
, ISimpleEvent<TArgs>
e ISignal
. Esto facilita el uso del tipo de evento adecuado para su proyecto. También oculta el método de envío de su evento, como lo haría una buena ocultación de la información.
Tipos de eventos / Interfaces : las definiciones de los eventos:
interface IEventHandler<TSender, TArgs> {
(sender: TSender, args: TArgs): void
}
interface ISimpleEventHandler<TArgs> {
(args: TArgs): void
}
interface ISignalHandler {
(): void;
}
Ejemplo : este ejemplo muestra cómo se pueden implementar los 3 tipos de eventos usando un reloj de tiempo:
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 : se puede usar así:
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.`))
Lea más aquí: sobre eventos, despachadores y listas (una explicación general del sistema)
Tutoriales
He escrito algunos tutoriales sobre el tema:
- Manejadores de eventos fuertemente tipados en TypeScript (Parte 1)
- Uso de eventos fuertemente tipados en TypeScript con interfaces (Parte 2)
- Eventos fuertemente tipados en TypeScript usando una lista de eventos (Parte 3)
- Agregar eventos con nombre a sus clases (Parte 4)
- 0.2.0 Soporte de eventos simples
- 0.3.0 Soporte de señal
Esta solución le permite escribir directamente los parámetros en la llamada de función en lugar de tener que ajustar todos sus parámetros en un objeto.
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
}
}
Uso:
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");
Puede usar eventos personalizados en TypeScript. No estoy seguro exactamente de lo que intenta hacer, pero aquí hay un ejemplo:
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);
}
}
}
Si está buscando hacer algo más específico, por favor hágamelo saber.
Si está buscando obtener la verificación de tipo de inteligencia mediante el patrón de emisor estándar, ahora puede hacer lo siguiente:
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);
}