diff --git a/dist/aurelia-templating.d.ts b/dist/aurelia-templating.d.ts index 8207160b..295919de 100644 --- a/dist/aurelia-templating.d.ts +++ b/dist/aurelia-templating.d.ts @@ -34,12 +34,6 @@ import { import { TaskQueue } from 'aurelia-task-queue'; -export declare interface EventHandler { - eventName: string; - bubbles: boolean; - dispose: Function; - handler: Function; -} /** * Specifies how a view should be created. @@ -472,10 +466,29 @@ export declare class ViewEngineHooksResource { } export declare function viewEngineHooks(target?: any): any; -/** - * Dispatches subscribets to and publishes events in the DOM. - * @param element - */ + +export declare interface EventHandler { + eventName: string; + bubbles: boolean; + dispose: Function; + handler: Function; +} + +export declare interface SubscriptionHandlerConfig { + handler: Function + capture?: boolean + passive?: boolean + once?: boolean +} + +export declare interface BatchSubscriptionConfig { + [eventName: string]: Function | SubscriptionHandlerConfig +} + +export declare interface EventSubscriptions { + [eventName: string]: EventHandler +} + /** * Dispatches subscribets to and publishes events in the DOM. * @param element @@ -484,41 +497,44 @@ export declare class ElementEvents { constructor(element: EventTarget); /** - * Dispatches an Event on the context element. - * @param eventName - * @param detail - * @param bubbles - * @param cancelable - */ + * Dispatches an Event on the context element. + * @param eventName + * @param detail + * @param bubbles + * @param cancelable + */ publish(eventName: string, detail?: Object, bubbles?: boolean, cancelable?: boolean): any; /** - * Adds and Event Listener on the context element. - * @param eventName - * @param handler - * @param bubbles - * @return Returns the eventHandler containing a dispose method - */ - subscribe(eventName: string, handler: Function, bubbles?: boolean): EventHandler; + * Adds and Event Listener on the context element. + * @return Returns the eventHandler containing a dispose method + */ + subscribe(events: TEvents): EventSubscriptions; + subscribe(eventName: string, handler: Function, captureOrOptions?: boolean | AddEventListenerOptions): EventHandler; + subscribe(configOrEventName: string | BatchSubscriptionConfig, handler?: Function, bubbles?: Boolean): EventHandler | EventSubscriptions; /** - * Adds an Event Listener on the context element, that will be disposed on the first trigger. - * @param eventName - * @param handler - * @param bubbles - * @return Returns the eventHandler containing a dispose method - */ - subscribeOnce(eventName: String, handler: Function, bubbles?: Boolean): EventHandler; + * Adds an Event Listener on the context element, that will be disposed on the first trigger. + * @return Returns the eventHandler containing a dispose method + */ + subscribeOnce(events: BatchSubscriptionConfig): EventSubscriptions; + subscribeOnce(eventName: string, handler: Function, captureOrOptions?: boolean | AddEventListenerOptions): EventHandler; + subscribeOnce(configOrEventName: string | BatchSubscriptionConfig, handler?: Function, bubbles?: Boolean): EventHandler | EventSubscriptions; /** - * Removes all events that are listening to the specified eventName. - * @param eventName - */ + * Add multiple event listeners at once, with option to specify once over the whole set + */ + batchSubscribe(events: BatchSubscriptionConfig, once?: boolean): EventSubscriptions + + /** + * Removes all events that are listening to the specified eventName. + * @param eventName + */ dispose(eventName: string): void; /** - * Removes all event handlers. - */ + * Removes all event handlers. + */ disposeAll(): any; } diff --git a/src/element-events.js b/src/element-events.js index edd10496..40ab1b0b 100644 --- a/src/element-events.js +++ b/src/element-events.js @@ -8,6 +8,22 @@ interface EventHandler { handler: Function; } +// Don't extends AddEventListenerOptions, make explicit for readability +interface SubscriptionHandlerConfig { + handler: Function; + capture?: boolean; + passive?: boolean; + once?: boolean; +} + +interface BatchSubscriptionConfig { + [eventName: string]: Function | SubscriptionHandlerConfig +} + +interface EventSubscriptions { + [eventName: string]: EventHandler +} + /** * Dispatches subscribets to and publishes events in the DOM. * @param element @@ -51,26 +67,54 @@ export class ElementEvents { * Adds and Event Listener on the context element. * @return Returns the eventHandler containing a dispose method */ - subscribe(eventName: string, handler: Function, captureOrOptions?: boolean | AddEventListenerOptions = true): EventHandler { - if (typeof handler === 'function') { - const eventHandler = new EventHandlerImpl(this, eventName, handler, captureOrOptions, false); - return eventHandler; - } + subscribe(configOrEventName: string | BatchSubscriptionConfig, handler: Function, captureOrOptions?: boolean | AddEventListenerOptions = true): EventSubscriptions | EventHandler { + if (typeof configOrEventName === 'string') { + if (typeof handler === 'function') { + const eventHandler = new EventHandlerImpl(this, configOrEventName, handler, captureOrOptions, false); + return eventHandler; + } - return undefined; + return undefined; + } else { + return this.batchSubscribe(configOrEventName, false); + } } /** * Adds an Event Listener on the context element, that will be disposed on the first trigger. * @return Returns the eventHandler containing a dispose method */ - subscribeOnce(eventName: String, handler: Function, captureOrOptions?: boolean | AddEventListenerOptions = true): EventHandler { - if (typeof handler === 'function') { - const eventHandler = new EventHandlerImpl(this, eventName, handler, captureOrOptions, true); - return eventHandler; + subscribeOnce(configOrEventName: string | BatchSubscriptionConfig, handler: Function, captureOrOptions?: boolean | AddEventListenerOptions = true): EventSubscriptions | EventHandler { + if (typeof configOrEventName === 'string') { + if (typeof handler === 'function') { + const eventHandler = new EventHandlerImpl(this, configOrEventName, handler, captureOrOptions, true); + return eventHandler; + } + + return undefined; + } else { + return this.batchSubscribe(configOrEventName, true); } + } - return undefined; + batchSubscribe(config: BatchSubscriptionConfig, once: boolean = false): EventSubscriptions { + const subscriptions: EventSubscriptions = {}; + for (let eventName in config) { + let handlerOrOptions = config[eventName]; + let handler: Function; + let listenerOptions: boolean | AddEventListenerOptions; + let _once = once; + if (typeof handlerOrOptions === 'function') { + handler = handlerOrOptions; + listenerOptions = false; + } else { + handler = handlerOrOptions.handler; + listenerOptions = handlerOrOptions; + _once = handlerOrOptions.once === undefined ? _once : !!handlerOrOptions.once; + } + subscriptions[eventName] = new EventHandlerImpl(this, eventName, handler, listenerOptions, _once); + } + return subscriptions; } /** diff --git a/test/element-events.js b/test/element-events.js index 223cbb2f..d18abdb2 100644 --- a/test/element-events.js +++ b/test/element-events.js @@ -34,6 +34,53 @@ describe('ElementEvents', () => { expect(callCount).toBe(1); }); + it('should batch subscribe', () => { + let value; + let callCount = 0; + + const subscriptions = elementEvents.subscribe({ + input: () => { + callCount++; + value = input.value; + }, + blur: () => { + callCount++; + }, + focus: { + handler: () => { + callCount++; + }, + once: true + } + }); + + const newValue = 1234; + input.value = newValue; + input.dispatchEvent(new CustomEvent('input')); + + expect(value === newValue.toString()).toBe(true); + expect(callCount).toBe(1); + + subscriptions.input.dispose(); + + input.dispatchEvent(new CustomEvent('input')); + expect(callCount).toBe(1); + + input.dispatchEvent(new CustomEvent('blur')); + expect(callCount).toBe(2); + + input.dispatchEvent(new CustomEvent('focus')); + expect(callCount).toBe(3); + + input.dispatchEvent(new CustomEvent('focus')); + expect(callCount).toBe(3); + + elementEvents.disposeAll(); + + input.dispatchEvent(new CustomEvent('blur')); + expect(callCount).toBe(3); + }) + it('should subscribe once', () => { let value; let callCount = 0;