import { Observable } from '../Observable'; | |
import { isFunction } from '../util/isFunction'; | |
import { NodeEventHandler } from './fromEvent'; | |
import { mapOneOrManyArgs } from '../util/mapOneOrManyArgs'; | |
/* tslint:disable:max-line-length */ | |
export function fromEventPattern<T>( | |
addHandler: (handler: NodeEventHandler) => any, | |
removeHandler?: (handler: NodeEventHandler, signal?: any) => void | |
): Observable<T>; | |
export function fromEventPattern<T>( | |
addHandler: (handler: NodeEventHandler) => any, | |
removeHandler?: (handler: NodeEventHandler, signal?: any) => void, | |
resultSelector?: (...args: any[]) => T | |
): Observable<T>; | |
/* tslint:enable:max-line-length */ | |
/** | |
* Creates an Observable from an arbitrary API for registering event handlers. | |
* | |
* <span class="informal">When that method for adding event handler was something {@link fromEvent} | |
* was not prepared for.</span> | |
* | |
*  | |
* | |
* `fromEventPattern` allows you to convert into an Observable any API that supports registering handler functions | |
* for events. It is similar to {@link fromEvent}, but far | |
* more flexible. In fact, all use cases of {@link fromEvent} could be easily handled by | |
* `fromEventPattern` (although in slightly more verbose way). | |
* | |
* This operator accepts as a first argument an `addHandler` function, which will be injected with | |
* handler parameter. That handler is actually an event handler function that you now can pass | |
* to API expecting it. `addHandler` will be called whenever Observable | |
* returned by the operator is subscribed, so registering handler in API will not | |
* necessarily happen when `fromEventPattern` is called. | |
* | |
* After registration, every time an event that we listen to happens, | |
* Observable returned by `fromEventPattern` will emit value that event handler | |
* function was called with. Note that if event handler was called with more | |
* than one argument, second and following arguments will not appear in the Observable. | |
* | |
* If API you are using allows to unregister event handlers as well, you can pass to `fromEventPattern` | |
* another function - `removeHandler` - as a second parameter. It will be injected | |
* with the same handler function as before, which now you can use to unregister | |
* it from the API. `removeHandler` will be called when consumer of resulting Observable | |
* unsubscribes from it. | |
* | |
* In some APIs unregistering is actually handled differently. Method registering an event handler | |
* returns some kind of token, which is later used to identify which function should | |
* be unregistered or it itself has method that unregisters event handler. | |
* If that is the case with your API, make sure token returned | |
* by registering method is returned by `addHandler`. Then it will be passed | |
* as a second argument to `removeHandler`, where you will be able to use it. | |
* | |
* If you need access to all event handler parameters (not only the first one), | |
* or you need to transform them in any way, you can call `fromEventPattern` with optional | |
* third parameter - project function which will accept all arguments passed to | |
* event handler when it is called. Whatever is returned from project function will appear on | |
* resulting stream instead of usual event handlers first argument. This means | |
* that default project can be thought of as function that takes its first parameter | |
* and ignores the rest. | |
* | |
* ## Examples | |
* | |
* Emits clicks happening on the DOM document | |
* | |
* ```ts | |
* import { fromEventPattern } from 'rxjs'; | |
* | |
* function addClickHandler(handler) { | |
* document.addEventListener('click', handler); | |
* } | |
* | |
* function removeClickHandler(handler) { | |
* document.removeEventListener('click', handler); | |
* } | |
* | |
* const clicks = fromEventPattern( | |
* addClickHandler, | |
* removeClickHandler | |
* ); | |
* clicks.subscribe(x => console.log(x)); | |
* | |
* // Whenever you click anywhere in the browser, DOM MouseEvent | |
* // object will be logged. | |
* ``` | |
* | |
* Use with API that returns cancellation token | |
* | |
* ```ts | |
* import { fromEventPattern } from 'rxjs'; | |
* | |
* const token = someAPI.registerEventHandler(function() {}); | |
* someAPI.unregisterEventHandler(token); // this APIs cancellation method accepts | |
* // not handler itself, but special token. | |
* | |
* const someAPIObservable = fromEventPattern( | |
* function(handler) { return someAPI.registerEventHandler(handler); }, // Note that we return the token here... | |
* function(handler, token) { someAPI.unregisterEventHandler(token); } // ...to then use it here. | |
* ); | |
* ``` | |
* | |
* Use with project function | |
* | |
* ```ts | |
* import { fromEventPattern } from 'rxjs'; | |
* | |
* someAPI.registerEventHandler((eventType, eventMessage) => { | |
* console.log(eventType, eventMessage); // Logs 'EVENT_TYPE' 'EVENT_MESSAGE' to console. | |
* }); | |
* | |
* const someAPIObservable = fromEventPattern( | |
* handler => someAPI.registerEventHandler(handler), | |
* handler => someAPI.unregisterEventHandler(handler) | |
* (eventType, eventMessage) => eventType + ' --- ' + eventMessage // without that function only 'EVENT_TYPE' | |
* ); // would be emitted by the Observable | |
* | |
* someAPIObservable.subscribe(value => console.log(value)); | |
* | |
* // Logs: | |
* // 'EVENT_TYPE --- EVENT_MESSAGE' | |
* ``` | |
* | |
* @see {@link fromEvent} | |
* @see {@link bindCallback} | |
* @see {@link bindNodeCallback} | |
* | |
* @param {function(handler: Function): any} addHandler A function that takes | |
* a `handler` function as argument and attaches it somehow to the actual | |
* source of events. | |
* @param {function(handler: Function, token?: any): void} [removeHandler] A function that | |
* takes a `handler` function as an argument and removes it from the event source. If `addHandler` | |
* returns some kind of token, `removeHandler` function will have it as a second parameter. | |
* @param {function(...args: any): T} [project] A function to | |
* transform results. It takes the arguments from the event handler and | |
* should return a single value. | |
* @return {Observable<T>} Observable which, when an event happens, emits first parameter | |
* passed to registered event handler. Alternatively it emits whatever project function returns | |
* at that moment. | |
*/ | |
export function fromEventPattern<T>( | |
addHandler: (handler: NodeEventHandler) => any, | |
removeHandler?: (handler: NodeEventHandler, signal?: any) => void, | |
resultSelector?: (...args: any[]) => T | |
): Observable<T | T[]> { | |
if (resultSelector) { | |
return fromEventPattern<T>(addHandler, removeHandler).pipe(mapOneOrManyArgs(resultSelector)); | |
} | |
return new Observable<T | T[]>((subscriber) => { | |
const handler = (...e: T[]) => subscriber.next(e.length === 1 ? e[0] : e); | |
const retValue = addHandler(handler); | |
return isFunction(removeHandler) ? () => removeHandler(handler, retValue) : undefined; | |
}); | |
} | |