From 994538526e8707fac3637b7f0d35047c133422e7 Mon Sep 17 00:00:00 2001 From: Diedrik De Mits Date: Thu, 22 Feb 2018 21:50:19 +0300 Subject: [PATCH 1/4] docs(operators): Add documentation for fromEvent This adds documentation for the fromEvent operator. The documentation has been partially rewritten to clarify certain parts a bit more. This change will close issue #78 --- .../operator-parameters.component.html | 2 +- src/operator-docs/creation/fromEvent.ts | 149 +++++++++++++++++- 2 files changed, 148 insertions(+), 3 deletions(-) diff --git a/src/app/operators/components/operator-parameters/operator-parameters.component.html b/src/app/operators/components/operator-parameters/operator-parameters.component.html index 75845451..700c98a0 100644 --- a/src/app/operators/components/operator-parameters/operator-parameters.component.html +++ b/src/app/operators/components/operator-parameters/operator-parameters.component.html @@ -13,7 +13,7 @@

Parameters

{{parameter.name}} {{parameter.type}} {{parameter.attribute}} - {{parameter.description}} + diff --git a/src/operator-docs/creation/fromEvent.ts b/src/operator-docs/creation/fromEvent.ts index 39ec5e39..b9f4063d 100644 --- a/src/operator-docs/creation/fromEvent.ts +++ b/src/operator-docs/creation/fromEvent.ts @@ -1,6 +1,151 @@ import { OperatorDoc } from '../operator.model'; export const fromEvent: OperatorDoc = { - 'name': 'fromEvent', - 'operatorType': 'creation' + name: 'fromEvent', + operatorType: 'creation', + signature: `static fromEvent(target, + eventName, options, selector): Observable`, + useInteractiveMarbles: true, + parameters: [ + { + name: 'target', + type: 'EventTargetLike', + attribute: '', + description: `The target to which an event handler is attached. + This target can be a DOM EventTarget, Node.js EventEmitter, JQuery-like event target, NodeList or HTMLCollection.` + }, + { + name: 'eventName', + type: 'string', + attribute: '', + description: 'The event name of interest, emitted by the target.' + }, + { + name: 'options', + type: 'EventListenerOptions', + attribute: 'optional', + /* tslint:disable:max-line-length */ + description: `Options to pass through to the addEventListener function. + This is either an options object {capture?: boolean, passive?: boolean, once?: boolean } or a boolean to represent capturing.` + /* tslint:enable:max-line-length */ + }, + { + name: 'selector', + type: 'SelectorMethodSignature', + attribute: 'optional', + description: `An optional function to map the arguments from the event handler to a single value.` + } + ], + marbleUrl: 'http://reactivex.io/rxjs/img/fromEvent.png', + shortDescription: { + description: ` + Creates an Observable that emits events of a specific type coming from the given event target. + ` + }, + walkthrough: { + description: ` +

+ Creates a new Observable based on a given target or a collection of targets, and a specific event type. + fromEvent allows an options object or a selector method to be passed for extra customization. +

+

+ The event target is an object with methods for registering event handler functions. + Valid objects are either a DOM EventTarget, a Node.js EventEmitter, + a JQuery-style event target, a DOM NodeList, or a DOM HTMLCollection. + These types are described in detail below. +

+

+ Note that the event target is checked through duck typing. In other words + no matter what kind of object you have and no matter what environment you work in, + you can safely use fromEvent on that object if it exposes any of the described methods (provided + of course they behave as described). + For example if a Node.js library exposes an event target with the same method names as a DOM EventTarget, fromEvent is still + a good choice. +

+

+ If your event target does not match any of the objects listed, + you should use fromEventPattern, which can be used on arbitrary APIs. +

+

+ Every time a subscription is made to the resulting Observable, + the next function of the Subscriber object is registered as the event handler + to the event target on the given event type. When that event fires, the value + passed as the first argument to the registered function will be emitted by that same Observable. + When the Observable gets unsubscribed, the event handler will be unregistered from the event target. +

+ +

+ Note that, without RxJS, if the event target would normally call the registered event handler with more than one argument, + the second and its following arguments will not appear in the resulting stream. In order to get access to them, + an optional selector function can be passed to fromEvent. + The selector will be called with all arguments passed to the registered event handler. + The resulting Observable will then emit the value as returned by the selector function, + instead of the usual value. +

+ +

+ If the API you use is more callback than event handler oriented (where the subscribed + callback function fires only once and thus there is no need to manually + unregister it), you should use bindCallback + or bindNodeCallback instead. +

+ +

Supported Event Target Types

+ +

DOM EventTarget

+ +

This is an object with addEventListener and removeEventListener methods.

+ +

+ In the browser, addEventListener accepts - apart from the event type string and the event + handler function arguments - an optional third parameter, which is either an object or boolean, + both used for additional configuration on how and when the passed function will be called. With + fromEvent you can also provide this optional third parameter. +

+ +

Node.js EventEmitter

+ +

This is an object with addListener and removeListener methods.

+ +

JQuery-style event target

+ +

This is an object with on and off methods.

+ +

DOM NodeList

+ +

This is a list of DOM Nodes, returned for example by document.querySelectorAll or Node.childNodes.

+ +

+ Although this collection is not an event target in itself, fromEvent will iterate over all Nodes + it contains and register an event handler function for every one of them. When the returned Observable + gets unsubscribed, the event handler function will be removed from all Nodes. +

+ +

DOM HtmlCollection

+

+ This is a collection of HTML elements or DOM nodes, very similar to the DOM NodeList. + Likewise, the event handler function gets registered and removed for each one of the elements.

+ ` + }, + examples: [ + { + name: 'On every click, emit array of most recent interval events', + code: ` + import { buffer } from 'rxjs/operators'; + import { interval } from 'rxjs/observable/interval'; + import { fromEvent } from 'rxjs/observable/fromEvent'; + + const clicks = fromEvent(document, 'click'); + const interval = interval(1000); + const buffered = interval.pipe(buffer(clicks)); + buffered.subscribe(x => console.log(x)); + `, + externalLink: { + platform: 'JSBin', + url: 'http://jsbin.com/xetemuteho/embed?js,console,output' + } + } + ], + relatedOperators: ['bindCallback', 'bindNodeCallback', 'fromEventPattern'], + additionalResources: [] }; From f6579c947555dc28a68c410f438c3b8be20b6dc7 Mon Sep 17 00:00:00 2001 From: Diedrik De Mits Date: Tue, 27 Feb 2018 16:07:57 +0100 Subject: [PATCH 2/4] docs(operators): Add extra examples for the fromEvent operator There were still a couple of examples necessary for the fromEvent operator. These have now been added --- src/operator-docs/creation/fromEvent.ts | 118 ++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 9 deletions(-) diff --git a/src/operator-docs/creation/fromEvent.ts b/src/operator-docs/creation/fromEvent.ts index b9f4063d..1791d099 100644 --- a/src/operator-docs/creation/fromEvent.ts +++ b/src/operator-docs/creation/fromEvent.ts @@ -75,8 +75,8 @@ export const fromEvent: OperatorDoc = {

- Note that, without RxJS, if the event target would normally call the registered event handler with more than one argument, - the second and its following arguments will not appear in the resulting stream. In order to get access to them, + Note that if the event target would normally (without RxJS) call the registered event handler with more than one argument, + the second and its following arguments will not appear in the resulting Observable stream. In order to get access to them, an optional selector function can be passed to fromEvent. The selector will be called with all arguments passed to the registered event handler. The resulting Observable will then emit the value as returned by the selector function, @@ -129,20 +129,120 @@ export const fromEvent: OperatorDoc = { }, examples: [ { - name: 'On every click, emit array of most recent interval events', + name: 'Emit every click made on the HTML page', code: ` - import { buffer } from 'rxjs/operators'; - import { interval } from 'rxjs/observable/interval'; import { fromEvent } from 'rxjs/observable/fromEvent'; const clicks = fromEvent(document, 'click'); - const interval = interval(1000); - const buffered = interval.pipe(buffer(clicks)); - buffered.subscribe(x => console.log(x)); + clicks.subscribe(x => console.log(x.clientX)); + + /* + Example console output + + 131 + 157 + 162 + 162 + 206 + 315 + 203 + 231 + 355 + + */ + + `, + externalLink: { + platform: 'JSBin', + url: 'http://jsbin.com/xegucig/2/embed?js,console,output' + } + }, + { + name: + 'Emit every click made on the HTML page, but select only the clientX and clientY properties.', + code: ` + import { fromEvent } from 'rxjs/observable/fromEvent'; + + const clicks = fromEvent(document, 'click', e => ({x: e.clientX, y: e.clientY})); + clicks.subscribe(x => console.log(x)); + + /* + Example console output + + [object Object] { + x: 294, + y: 89 + } + [object Object] { + x: 183, + y: 207 + } + [object Object] { + x: 301, + y: 235 + } + [object Object] { + x: 371, + y: 132 + } + + */ + + `, + externalLink: { + platform: 'JSBin', + url: 'http://jsbin.com/kaxalu/1/embed?js,console,output' + } + }, + { + name: + 'Emit a click made on a div element and look at it through bubbling (bottom -> up) and capturing (top -> down).', + code: + ` + import { fromEvent } from 'rxjs/observable/fromEvent'; +` + + /* tslint:disable:max-line-length */ + ` document.body.innerHTML += '

';` + + /* tslint:enable:max-line-length */ + ` + let divs = document.querySelectorAll('div'); + + const clicks = fromEvent(divs, 'click', + (e) => ({ bubbling: e.currentTarget.title }) ); + clicks.subscribe(x => console.log(x)); + + + const clicksCapture = fromEvent(divs, 'click', true, + (e) => ({ capture: e.currentTarget.title }) ); + clicksCapture.subscribe(x => console.log(x)); + + /* + Example console output + + [object Object] { + capture: "outer" + } + [object Object] { + capture: "middle" + } + [object Object] { + bubbling: "inner" + } + [object Object] { + capture: "inner" + } + [object Object] { + bubbling: "middle" + } + [object Object] { + bubbling: "outer" + } + */ + `, externalLink: { platform: 'JSBin', - url: 'http://jsbin.com/xetemuteho/embed?js,console,output' + url: 'http://jsbin.com/kuzajec/4/embed?js,console,output' } } ], From d09a460df9b0b55f4143968bd68dbf9f4fbef852 Mon Sep 17 00:00:00 2001 From: Diedrik De Mits Date: Tue, 27 Feb 2018 20:51:14 +0100 Subject: [PATCH 3/4] docs(operators): Add example to fromEvent documentation for an event handler with more than one argu Added a small jQuery example that creates and triggers an event handler with more than one argument. --- src/operator-docs/creation/fromEvent.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/operator-docs/creation/fromEvent.ts b/src/operator-docs/creation/fromEvent.ts index 1791d099..e4c2fcb0 100644 --- a/src/operator-docs/creation/fromEvent.ts +++ b/src/operator-docs/creation/fromEvent.ts @@ -3,8 +3,8 @@ import { OperatorDoc } from '../operator.model'; export const fromEvent: OperatorDoc = { name: 'fromEvent', operatorType: 'creation', - signature: `static fromEvent(target, - eventName, options, selector): Observable`, + signature: `public static fromEvent(target: EventTargetLike, + eventName: string, options: EventListenerOptions, selector: SelectorMethodSignature): Observable`, useInteractiveMarbles: true, parameters: [ { @@ -82,7 +82,14 @@ export const fromEvent: OperatorDoc = { The resulting Observable will then emit the value as returned by the selector function, instead of the usual value.

- +

+ Example jQuery event handler with more than one argument: +

+  $("#foo").on("custom", function(event, param1, param2) {
+    console.log(param1 + " " + param2);
+  });
+  $("#foo").trigger("custom", ["Custom", "Event"]);
+

If the API you use is more callback than event handler oriented (where the subscribed callback function fires only once and thus there is no need to manually From 7d738e9071fa60ddd5d151d5598b4c42093e6bfd Mon Sep 17 00:00:00 2001 From: Diedrik De Mits Date: Thu, 8 Mar 2018 15:12:05 +0100 Subject: [PATCH 4/4] docs(operators): Move jQuery example to keep flow of text The example of a non-one argument event handler, written with jQuery was breaking the flow ot the text. Moved it below the event targets and added a small reference in the source text to continue reading below. --- src/operator-docs/creation/fromEvent.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/operator-docs/creation/fromEvent.ts b/src/operator-docs/creation/fromEvent.ts index e4c2fcb0..8b490677 100644 --- a/src/operator-docs/creation/fromEvent.ts +++ b/src/operator-docs/creation/fromEvent.ts @@ -76,21 +76,14 @@ export const fromEvent: OperatorDoc = {

Note that if the event target would normally (without RxJS) call the registered event handler with more than one argument, - the second and its following arguments will not appear in the resulting Observable stream. In order to get access to them, - an optional selector function can be passed to fromEvent. + the second and its following arguments will not appear in the resulting Observable stream (see below for a jQuery example). + In order to get access to them, + an optional selector function can be passed to fromEvent. The selector will be called with all arguments passed to the registered event handler. The resulting Observable will then emit the value as returned by the selector function, instead of the usual value.

- Example jQuery event handler with more than one argument: -

-  $("#foo").on("custom", function(event, param1, param2) {
-    console.log(param1 + " " + param2);
-  });
-  $("#foo").trigger("custom", ["Custom", "Event"]);
-

-

If the API you use is more callback than event handler oriented (where the subscribed callback function fires only once and thus there is no need to manually unregister it), you should use bindCallback @@ -132,6 +125,15 @@ export const fromEvent: OperatorDoc = {

This is a collection of HTML elements or DOM nodes, very similar to the DOM NodeList. Likewise, the event handler function gets registered and removed for each one of the elements.

+

Example jQuery event handler with more than one argument:

+

+

+  $("#foo").on("custom", function(event, param1, param2) {
+    console.log(param1 + " " + param2);
+  });
+  $("#foo").trigger("custom", ["Custom", "Event"]);
+

+ ` }, examples: [