199f951b9dcb0d9a94ee5e35ae0bb767abc06e2955595a85297f36ebbdfe2c8ac1dcddfaeb2b82e6b717964589e9e2d3041a2bd80cdabe7c2ae22cd29122e9 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import { Observable } from '../Observable';
  2. import { isFunction } from '../util/isFunction';
  3. import { NodeEventHandler } from './fromEvent';
  4. import { mapOneOrManyArgs } from '../util/mapOneOrManyArgs';
  5. /* tslint:disable:max-line-length */
  6. export function fromEventPattern<T>(
  7. addHandler: (handler: NodeEventHandler) => any,
  8. removeHandler?: (handler: NodeEventHandler, signal?: any) => void
  9. ): Observable<T>;
  10. export function fromEventPattern<T>(
  11. addHandler: (handler: NodeEventHandler) => any,
  12. removeHandler?: (handler: NodeEventHandler, signal?: any) => void,
  13. resultSelector?: (...args: any[]) => T
  14. ): Observable<T>;
  15. /* tslint:enable:max-line-length */
  16. /**
  17. * Creates an Observable from an arbitrary API for registering event handlers.
  18. *
  19. * <span class="informal">When that method for adding event handler was something {@link fromEvent}
  20. * was not prepared for.</span>
  21. *
  22. * ![](fromEventPattern.png)
  23. *
  24. * `fromEventPattern` allows you to convert into an Observable any API that supports registering handler functions
  25. * for events. It is similar to {@link fromEvent}, but far
  26. * more flexible. In fact, all use cases of {@link fromEvent} could be easily handled by
  27. * `fromEventPattern` (although in slightly more verbose way).
  28. *
  29. * This operator accepts as a first argument an `addHandler` function, which will be injected with
  30. * handler parameter. That handler is actually an event handler function that you now can pass
  31. * to API expecting it. `addHandler` will be called whenever Observable
  32. * returned by the operator is subscribed, so registering handler in API will not
  33. * necessarily happen when `fromEventPattern` is called.
  34. *
  35. * After registration, every time an event that we listen to happens,
  36. * Observable returned by `fromEventPattern` will emit value that event handler
  37. * function was called with. Note that if event handler was called with more
  38. * than one argument, second and following arguments will not appear in the Observable.
  39. *
  40. * If API you are using allows to unregister event handlers as well, you can pass to `fromEventPattern`
  41. * another function - `removeHandler` - as a second parameter. It will be injected
  42. * with the same handler function as before, which now you can use to unregister
  43. * it from the API. `removeHandler` will be called when consumer of resulting Observable
  44. * unsubscribes from it.
  45. *
  46. * In some APIs unregistering is actually handled differently. Method registering an event handler
  47. * returns some kind of token, which is later used to identify which function should
  48. * be unregistered or it itself has method that unregisters event handler.
  49. * If that is the case with your API, make sure token returned
  50. * by registering method is returned by `addHandler`. Then it will be passed
  51. * as a second argument to `removeHandler`, where you will be able to use it.
  52. *
  53. * If you need access to all event handler parameters (not only the first one),
  54. * or you need to transform them in any way, you can call `fromEventPattern` with optional
  55. * third parameter - project function which will accept all arguments passed to
  56. * event handler when it is called. Whatever is returned from project function will appear on
  57. * resulting stream instead of usual event handlers first argument. This means
  58. * that default project can be thought of as function that takes its first parameter
  59. * and ignores the rest.
  60. *
  61. * ## Examples
  62. *
  63. * Emits clicks happening on the DOM document
  64. *
  65. * ```ts
  66. * import { fromEventPattern } from 'rxjs';
  67. *
  68. * function addClickHandler(handler) {
  69. * document.addEventListener('click', handler);
  70. * }
  71. *
  72. * function removeClickHandler(handler) {
  73. * document.removeEventListener('click', handler);
  74. * }
  75. *
  76. * const clicks = fromEventPattern(
  77. * addClickHandler,
  78. * removeClickHandler
  79. * );
  80. * clicks.subscribe(x => console.log(x));
  81. *
  82. * // Whenever you click anywhere in the browser, DOM MouseEvent
  83. * // object will be logged.
  84. * ```
  85. *
  86. * Use with API that returns cancellation token
  87. *
  88. * ```ts
  89. * import { fromEventPattern } from 'rxjs';
  90. *
  91. * const token = someAPI.registerEventHandler(function() {});
  92. * someAPI.unregisterEventHandler(token); // this APIs cancellation method accepts
  93. * // not handler itself, but special token.
  94. *
  95. * const someAPIObservable = fromEventPattern(
  96. * function(handler) { return someAPI.registerEventHandler(handler); }, // Note that we return the token here...
  97. * function(handler, token) { someAPI.unregisterEventHandler(token); } // ...to then use it here.
  98. * );
  99. * ```
  100. *
  101. * Use with project function
  102. *
  103. * ```ts
  104. * import { fromEventPattern } from 'rxjs';
  105. *
  106. * someAPI.registerEventHandler((eventType, eventMessage) => {
  107. * console.log(eventType, eventMessage); // Logs 'EVENT_TYPE' 'EVENT_MESSAGE' to console.
  108. * });
  109. *
  110. * const someAPIObservable = fromEventPattern(
  111. * handler => someAPI.registerEventHandler(handler),
  112. * handler => someAPI.unregisterEventHandler(handler)
  113. * (eventType, eventMessage) => eventType + ' --- ' + eventMessage // without that function only 'EVENT_TYPE'
  114. * ); // would be emitted by the Observable
  115. *
  116. * someAPIObservable.subscribe(value => console.log(value));
  117. *
  118. * // Logs:
  119. * // 'EVENT_TYPE --- EVENT_MESSAGE'
  120. * ```
  121. *
  122. * @see {@link fromEvent}
  123. * @see {@link bindCallback}
  124. * @see {@link bindNodeCallback}
  125. *
  126. * @param addHandler A function that takes a `handler` function as argument and attaches it
  127. * somehow to the actual source of events.
  128. * @param removeHandler A function that takes a `handler` function as an argument and removes
  129. * it from the event source. If `addHandler` returns some kind of token, `removeHandler` function
  130. * will have it as a second parameter.
  131. * @param resultSelector A function to transform results. It takes the arguments from the event
  132. * handler and should return a single value.
  133. * @return Observable which, when an event happens, emits first parameter passed to registered
  134. * event handler. Alternatively it emits whatever project function returns at that moment.
  135. */
  136. export function fromEventPattern<T>(
  137. addHandler: (handler: NodeEventHandler) => any,
  138. removeHandler?: (handler: NodeEventHandler, signal?: any) => void,
  139. resultSelector?: (...args: any[]) => T
  140. ): Observable<T | T[]> {
  141. if (resultSelector) {
  142. return fromEventPattern<T>(addHandler, removeHandler).pipe(mapOneOrManyArgs(resultSelector));
  143. }
  144. return new Observable<T | T[]>((subscriber) => {
  145. const handler = (...e: T[]) => subscriber.next(e.length === 1 ? e[0] : e);
  146. const retValue = addHandler(handler);
  147. return isFunction(removeHandler) ? () => removeHandler(handler, retValue) : undefined;
  148. });
  149. }