diff --git a/README.md b/README.md index fc812b7..3e8df2c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ We also have a routing guard lab. `1-routing-guards-begin` and `1-routing-guards ### 2-Modules -`2-modules-begin` and `1-modules-end`. +`2-modules-begin` and `2-modules-end`. ### 3-rxjs diff --git a/package-lock.json b/package-lock.json index 87ceb8a..658c21b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -302,7 +302,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -323,12 +324,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -343,17 +346,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -470,7 +476,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -482,6 +489,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -496,6 +504,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -503,12 +512,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -527,6 +538,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -607,7 +619,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -619,6 +632,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -704,7 +718,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -740,6 +755,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -759,6 +775,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -802,12 +819,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -1604,6 +1623,7 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, + "optional": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -2237,6 +2257,7 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "dev": true, + "optional": true, "requires": { "hoek": "2.x.x" } @@ -2431,7 +2452,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==", - "dev": true + "dev": true, + "optional": true }, "buffer-xor": { "version": "1.0.3", @@ -2949,7 +2971,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "dev": true, + "optional": true }, "constants-browserify": { "version": "1.0.0", @@ -3377,7 +3400,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true + "dev": true, + "optional": true }, "depd": { "version": "1.1.2", @@ -4409,7 +4433,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -4430,12 +4455,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4450,17 +4477,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -4577,7 +4607,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -4589,6 +4620,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4603,6 +4635,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4610,12 +4643,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -4634,6 +4669,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -4714,7 +4750,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -4726,6 +4763,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -4811,7 +4849,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -4847,6 +4886,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4866,6 +4906,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4909,12 +4950,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -4923,6 +4966,7 @@ "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -4982,6 +5026,7 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, + "optional": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -5039,7 +5084,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true + "dev": true, + "optional": true }, "get-stream": { "version": "3.0.0", @@ -5253,7 +5299,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true + "dev": true, + "optional": true }, "has-value": { "version": "1.0.0", @@ -5346,7 +5393,8 @@ "version": "2.16.3", "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true + "dev": true, + "optional": true }, "homedir-polyfill": { "version": "1.0.1", @@ -5470,6 +5518,7 @@ "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz", "integrity": "sha1-rQFScUOi6Hc8+uapb1hla7UqNLI=", "dev": true, + "optional": true, "requires": { "httpreq": ">=0.4.22", "underscore": "~1.7.0" @@ -5479,7 +5528,8 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.4.24.tgz", "integrity": "sha1-QzX/2CzZaWaKOUZckprGHWOTYn8=", - "dev": true + "dev": true, + "optional": true }, "https-browserify": { "version": "1.0.0", @@ -6048,7 +6098,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true + "dev": true, + "optional": true }, "is-stream": { "version": "1.1.0", @@ -6648,13 +6699,15 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz", "integrity": "sha1-YjUag5VjrF/1vSbxL2Dpgwu3UeY=", - "dev": true + "dev": true, + "optional": true }, "libmime": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/libmime/-/libmime-3.0.0.tgz", "integrity": "sha1-UaGp50SOy9Ms2lRCFnW7IbwJPaY=", "dev": true, + "optional": true, "requires": { "iconv-lite": "0.4.15", "libbase64": "0.1.0", @@ -6665,7 +6718,8 @@ "version": "0.4.15", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=", - "dev": true + "dev": true, + "optional": true } } }, @@ -6673,7 +6727,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz", "integrity": "sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g=", - "dev": true + "dev": true, + "optional": true }, "license-webpack-plugin": { "version": "2.1.0", @@ -6699,6 +6754,7 @@ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -6711,7 +6767,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "optional": true } } }, @@ -6743,9 +6800,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash.assign": { "version": "4.2.0", @@ -7215,7 +7272,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true + "dev": true, + "optional": true }, "map-visit": { "version": "1.0.0", @@ -7867,13 +7925,15 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz", "integrity": "sha1-ecSQihwPXzdbc/6IjamCj23JY6Q=", - "dev": true + "dev": true, + "optional": true }, "nodemailer-shared": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz", "integrity": "sha1-z1mU4v0mjQD1zw+nZ6CBae2wfsA=", "dev": true, + "optional": true, "requires": { "nodemailer-fetch": "1.6.0" } @@ -7906,7 +7966,8 @@ "version": "0.1.10", "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz", "integrity": "sha1-WG24EB2zDLRDjrVGc3pBqtDPE9U=", - "dev": true + "dev": true, + "optional": true }, "nopt": { "version": "3.0.6", @@ -8012,6 +8073,7 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, + "optional": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -8789,7 +8851,8 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true + "dev": true, + "optional": true }, "process": { "version": "0.11.10", @@ -9186,6 +9249,7 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, + "optional": true, "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -9197,6 +9261,7 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -9207,7 +9272,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "optional": true } } }, @@ -9216,6 +9282,7 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, + "optional": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -9226,6 +9293,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, + "optional": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -9236,6 +9304,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, + "optional": true, "requires": { "pinkie-promise": "^2.0.0" } @@ -9952,6 +10021,7 @@ "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.12.0.tgz", "integrity": "sha1-1275EnyyPCJZ7bHoNJwujV4tdME=", "dev": true, + "optional": true, "requires": { "httpntlm": "1.6.1", "nodemailer-shared": "1.1.0" @@ -11228,6 +11298,7 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, + "optional": true, "requires": { "prelude-ls": "~1.1.2" } @@ -11283,7 +11354,8 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=", - "dev": true + "dev": true, + "optional": true }, "union-value": { "version": "1.0.0", @@ -12052,6 +12124,7 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, + "optional": true, "requires": { "string-width": "^1.0.2 || 2" } diff --git a/src/3-rxjs-end/app-routing.module.ts b/src/3-rxjs-end/app-routing.module.ts index 8102496..2884cef 100644 --- a/src/3-rxjs-end/app-routing.module.ts +++ b/src/3-rxjs-end/app-routing.module.ts @@ -30,6 +30,10 @@ const routes: Routes = [ { path: 'bus', loadChildren: '3-rxjs-end/bus/bus.module#BusModule' + }, + { + path: 'mapping', + loadChildren: '3-rxjs-end/mapping/mapping.module#MappingModule' } ]; diff --git a/src/3-rxjs-end/core/toolbar/toolbar.component.html b/src/3-rxjs-end/core/toolbar/toolbar.component.html index 1fcf723..625bf98 100644 --- a/src/3-rxjs-end/core/toolbar/toolbar.component.html +++ b/src/3-rxjs-end/core/toolbar/toolbar.component.html @@ -27,6 +27,9 @@ Bus + + Mapping + diff --git a/src/3-rxjs-end/mapping/addImg.ts b/src/3-rxjs-end/mapping/addImg.ts new file mode 100644 index 0000000..5604a5f --- /dev/null +++ b/src/3-rxjs-end/mapping/addImg.ts @@ -0,0 +1,83 @@ +import { defer, Observable } from 'rxjs'; +import { finalize, map, tap } from 'rxjs/operators'; + +/** + * Observable of animated image as it drops and bounces + * @param svg The SVG image to which the animated image will be appended. + * @returns object with data about the most recent frame of the animation + */ +export const addImg = (svg: any) => defer(() => { + // Animate the image + // const elem = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); + // elem.style = 'fill: red; stroke: none;'; + + const elem = document.createElementNS('http://www.w3.org/2000/svg', 'image'); + elem.setAttribute('height', '150'); + elem.setAttribute('width', '150'); + elem.setAttributeNS('http://www.w3.org/1999/xlink', 'href', '../../../assets/kitten.jpg'); + + svg.appendChild(elem); + elem.setAttribute('r', '20'); + + return animationTime(2000).pipe( + map(t => ({ + t, + x: easeLinear(t) * 600, // width of svg canvas (see mapping.component) + y: easeOutBounce(t) * 500 // height of svg canvas (see mapping.component) + })), + tap(({ x, y }) => { + // console.log(`x: ${x}, y: ${y}`); + + // For circles + // elem.setAttribute('x', x.toString()); + // elem.setAttribute('y', (y - 100).toString()); + + // For images + elem.setAttribute('x', x.toString()); + elem.setAttribute('y', (y - 100).toString()); + }), + finalize(() => elem.remove()), + ); +}); + +function animationTime(duration = 1000) { + return new Observable(observer => { + const start = Date.now(); + let id: number; + + const animate = () => { + id = requestAnimationFrame(() => { + const diff = Date.now() - start; + if (diff < duration) { + observer.next(diff / duration); + } else { + observer.next(1); + observer.complete(); + } + animate(); + }); + }; + + animate(); + + return () => { + if (id) { + cancelAnimationFrame(id); + } + }; + }); +} + +function easeOutBounce(pos: number) { + if ((pos) < (1 / 2.75)) { + return (7.5625 * pos * pos); + } else if (pos < (2 / 2.75)) { + return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); + } else if (pos < (2.5 / 2.75)) { + return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); + } else { + return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); + } +} + +function easeLinear(x: number) { return x; } diff --git a/src/3-rxjs-end/mapping/mapping.component.html b/src/3-rxjs-end/mapping/mapping.component.html new file mode 100644 index 0000000..c1ef7c4 --- /dev/null +++ b/src/3-rxjs-end/mapping/mapping.component.html @@ -0,0 +1,23 @@ +

Map Operators in Action

+ + + + +
+ + Dropped: {{results.dropped}} + Complete: {{results.completed}} +
+ + + +

Example courtesy of Ben Lesh

diff --git a/src/3-rxjs-end/mapping/mapping.component.ts b/src/3-rxjs-end/mapping/mapping.component.ts new file mode 100644 index 0000000..354d305 --- /dev/null +++ b/src/3-rxjs-end/mapping/mapping.component.ts @@ -0,0 +1,92 @@ +import { Component, ElementRef, ViewChild } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; + +import { + concatMap, exhaustMap, mergeMap, switchMap, + endWith, scan, map, startWith +} from 'rxjs/operators'; + +import { addImg } from './addImg'; + +interface Results { + dropped: number; + completed: number; +} + +// tslint:disable:member-ordering +@Component({ + templateUrl: './mapping.component.html' +}) +export class MappingComponent { + @ViewChild('svg') svg: ElementRef; + + /** Subject/Observable of clicks of the drop button */ + drop$ = new Subject(); + + /** Observable of results from clicking the drop button. */ + results$: Observable; + + constructor() { + this.resetResults$('mergeMap'); // start with default, mergeMap + } + + /** + * Reset the observable of results (results$). + * Emits results built from combining animation observables with the selected map operator. + * Animation observables are (or may be) created after each drop-click. + * @param mapOpName Name of the map operator to use + */ + resetResults$(mapOpName: string) { + + // Return an image animation observable that emits an action: { type: string } when + // 1) the image starts to drop, + // 2) after each animation frame, and + // 3) when it stops + const add = () => addImg(this.svg.nativeElement as SVGSVGElement).pipe( + // when the ball animates, emit an action + map((e) => ({ type: 'IMG_ANIMATE', e })), // for diagnostic purposes + // it starts with a IMG_START action + startWith({ type: 'IMG_START' }), + // it ends with a IMG_END action + endWith({ type: 'IMG_END' }), + ); + + // Pick the selected map operator + // tslint:disable: deprecation + const mapOperator = + mapOpName === 'exhaustMap' ? exhaustMap : + mapOpName === 'concatMap' ? concatMap : + mapOpName === 'switchMap' ? switchMap : + mergeMap; // default to mergeMap + // tslint:enable: deprecation + + // Observable of flattened add()observables, mapped with the selected map operator + const mapped$ = this.drop$.pipe( + mapOperator(add) // e.g., mergeMap(add) + ); + + + // Observable of mapped animations, transformed into results for display + this.results$ = mapped$.pipe( + // use a scan for state management (we'll just mutate state, it's okay here) + scan((state: Results, action: {type: string}) => { + switch (action.type) { + // when you get a IMG_START, increment dropped counter + case 'IMG_START': + state.dropped++; + break; + // when you get a IMG_END, increment the completed counter + case 'IMG_END': + state.completed++; + break; + // otherwise we don't care + } + return state; + }, { dropped: 0, completed: 0 }), + + // Begin with zeroed-out results each time we reset results$ + startWith({ dropped: 0, completed: 0 }) + ); + } + +} diff --git a/src/3-rxjs-end/mapping/mapping.module.ts b/src/3-rxjs-end/mapping/mapping.module.ts new file mode 100644 index 0000000..dcdb0df --- /dev/null +++ b/src/3-rxjs-end/mapping/mapping.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Routes, RouterModule } from '@angular/router'; + +import { MappingComponent } from './mapping.component'; +import { SharedModule } from '../shared/shared.module'; + +const routes: Routes = [{ path: '', pathMatch: 'full', component: MappingComponent }]; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + RouterModule.forChild(routes), + ], + declarations: [ + MappingComponent, + ] +}) +export class MappingModule {} diff --git a/src/assets/kitten.jpg b/src/assets/kitten.jpg new file mode 100644 index 0000000..6c35bb7 Binary files /dev/null and b/src/assets/kitten.jpg differ