├── .angular-cli.json
├── .deployment
├── .editorconfig
├── .gitignore
├── .vscode
├── launch.json
└── settings.json
├── README.md
├── deploy.cmd
├── deploy.sh
├── e2e
├── app.e2e-spec.ts
├── app.po.ts
└── tsconfig.e2e.json
├── karma.conf.js
├── licence
├── npm-debug.log.3191570879
├── package-lock.json
├── package.json
├── protractor.conf.js
├── src
├── app
│ ├── app.component.css
│ ├── app.component.html
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── app.routes.ts
│ ├── create-observables
│ │ ├── create-observables.component.css
│ │ ├── create-observables.component.html
│ │ ├── create-observables.component.spec.ts
│ │ ├── create-observables.component.ts
│ │ └── sample-code.ts
│ ├── home
│ │ ├── home.component.css
│ │ ├── home.component.html
│ │ ├── home.component.spec.ts
│ │ └── home.component.ts
│ ├── hot-and-cold
│ │ ├── hot-and-cold.component.css
│ │ ├── hot-and-cold.component.html
│ │ ├── hot-and-cold.component.spec.ts
│ │ └── hot-and-cold.component.ts
│ ├── operators
│ │ ├── buffer
│ │ │ ├── buffer.component.css
│ │ │ ├── buffer.component.html
│ │ │ ├── buffer.component.spec.ts
│ │ │ ├── buffer.component.ts
│ │ │ └── sample-code.ts
│ │ ├── filter
│ │ │ ├── filter.component.css
│ │ │ ├── filter.component.html
│ │ │ ├── filter.component.spec.ts
│ │ │ ├── filter.component.ts
│ │ │ └── sample-code.ts
│ │ ├── latest
│ │ │ ├── latest.component.css
│ │ │ ├── latest.component.html
│ │ │ ├── latest.component.spec.ts
│ │ │ ├── latest.component.ts
│ │ │ └── sample-code.ts
│ │ ├── merge-map
│ │ │ ├── merge-map.component.css
│ │ │ ├── merge-map.component.html
│ │ │ ├── merge-map.component.spec.ts
│ │ │ ├── merge-map.component.ts
│ │ │ └── sample-code.ts
│ │ ├── merge
│ │ │ ├── merge.component.css
│ │ │ ├── merge.component.html
│ │ │ ├── merge.component.spec.ts
│ │ │ ├── merge.component.ts
│ │ │ └── sample-code.ts
│ │ ├── operators.component.css
│ │ ├── operators.component.html
│ │ ├── operators.component.spec.ts
│ │ ├── operators.component.ts
│ │ ├── reduce
│ │ │ ├── reduce.component.css
│ │ │ ├── reduce.component.html
│ │ │ ├── reduce.component.spec.ts
│ │ │ ├── reduce.component.ts
│ │ │ └── sample-code.ts
│ │ ├── scan
│ │ │ ├── sample-code.ts
│ │ │ ├── scan.component.css
│ │ │ ├── scan.component.html
│ │ │ ├── scan.component.spec.ts
│ │ │ └── scan.component.ts
│ │ └── switch-map
│ │ │ ├── sample-code.ts
│ │ │ ├── switch-map.component.css
│ │ │ ├── switch-map.component.html
│ │ │ ├── switch-map.component.spec.ts
│ │ │ └── switch-map.component.ts
│ ├── shared
│ │ ├── data.service.ts
│ │ ├── data.ts
│ │ ├── rxjs-operators.ts
│ │ └── utils.ts
│ ├── subjects
│ │ ├── sample-code.ts
│ │ ├── subjects.component.css
│ │ ├── subjects.component.html
│ │ ├── subjects.component.spec.ts
│ │ └── subjects.component.ts
│ └── theme.css
├── assets
│ ├── .gitkeep
│ └── images
│ │ ├── angular-2.jpg
│ │ ├── microsoft.jpg
│ │ ├── motog4plus.jpg
│ │ ├── reactive-store-angular-2.jpg
│ │ ├── reactive.jpg
│ │ ├── reactiveX.png
│ │ ├── reactivex-merge.gif
│ │ ├── samsunggalaxya72017.jpg
│ │ ├── samsunggalaxyj7prime.jpg
│ │ └── sonyxperiaxa1.jpg
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.css
├── test.ts
├── tsconfig.app.json
├── tsconfig.spec.json
└── typings.d.ts
├── tsconfig.json
└── tslint.json
/.angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "project": {
4 | "name": "rxjs-in-angular"
5 | },
6 | "apps": [
7 | {
8 | "root": "src",
9 | "outDir": "dist",
10 | "assets": [
11 | "assets",
12 | "favicon.ico"
13 | ],
14 | "index": "index.html",
15 | "main": "main.ts",
16 | "polyfills": "polyfills.ts",
17 | "test": "test.ts",
18 | "tsconfig": "tsconfig.app.json",
19 | "testTsconfig": "tsconfig.spec.json",
20 | "prefix": "app",
21 | "styles": [
22 | "styles.css"
23 | ],
24 | "scripts": [],
25 | "environmentSource": "environments/environment.ts",
26 | "environments": {
27 | "dev": "environments/environment.ts",
28 | "prod": "environments/environment.prod.ts"
29 | }
30 | }
31 | ],
32 | "e2e": {
33 | "protractor": {
34 | "config": "./protractor.conf.js"
35 | }
36 | },
37 | "lint": [
38 | {
39 | "project": "src/tsconfig.app.json"
40 | },
41 | {
42 | "project": "src/tsconfig.spec.json"
43 | },
44 | {
45 | "project": "e2e/tsconfig.e2e.json"
46 | }
47 | ],
48 | "test": {
49 | "karma": {
50 | "config": "./karma.conf.js"
51 | }
52 | },
53 | "defaults": {
54 | "styleExt": "css",
55 | "component": {}
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/.deployment:
--------------------------------------------------------------------------------
1 | [config]
2 | command = deploy.cmd
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | testem.log
34 | /typings
35 |
36 | # e2e
37 | /e2e/*.js
38 | /e2e/*.map
39 |
40 | # System Files
41 | .DS_Store
42 | Thumbs.db
43 |
44 | # npm log files
45 | npm-debug.log*
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "type": "extensionHost",
6 | "request": "launch",
7 | "name": "Launch Extension",
8 | "runtimeExecutable": "${execPath}",
9 | "args": [
10 | "--extensionDevelopmentPath=${workspaceRoot}"
11 | ],
12 | "sourceMaps": true,
13 | "outFiles": [
14 | "${workspaceRoot}/out/**/*.js"
15 | ],
16 | "preLaunchTask": "npm"
17 | },
18 | {
19 | "type": "chrome",
20 | "request": "launch",
21 | "name": "Launch Chrome against localhost",
22 | "url": "http://localhost:4200",
23 | "webRoot": "${workspaceRoot}"
24 | },
25 | {
26 | "type": "chrome",
27 | "request": "attach",
28 | "name": "Attach to Chrome",
29 | "port": 9222,
30 | "webRoot": "${workspaceRoot}"
31 | }
32 | ]
33 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ReactiveX Operators in Angular - Playground
2 |
3 | ## About
4 |
5 | Multiple examples using the most used `RxJS operators` with Angular.
6 |
7 | 
8 |
9 | ## Installation / Development server
10 |
11 | 1. npm install
12 | 1. npm start
13 | 1. navigate to `http://localhost:4200/`
14 |
15 | ## Build
16 |
17 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
18 |
19 | ## Contribution
20 |
21 | Feel free to contribute and add your own examples in the project. Just fork the repo and send me a pull request. Make sure your example follows the same patterns having the following 3 tabs:
22 |
23 | * `Example tab`: this is where you describe the use of an RxJS operator using Angular Material 2 components
24 | * `About tab` An iframe with link to operator's documentation
25 | * `Sample code tab`: the most important code lines for your example (syntax highlight is already supported in the project)
26 |
27 | 
28 |
29 | ## Follow chsakell's Blog
30 |
31 |
32 |
33 | Facebook
34 | Twitter
35 |
36 |
37 |
38 |
39 | Microsoft Web Application Development
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | License
54 | Code released under the MIT license .
55 |
--------------------------------------------------------------------------------
/deploy.cmd:
--------------------------------------------------------------------------------
1 | @if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off
2 |
3 | :: ----------------------
4 | :: KUDU Deployment Script
5 | :: Version: 1.0.9
6 | :: ----------------------
7 |
8 | :: Prerequisites
9 | :: -------------
10 |
11 | :: Verify node.js installed
12 | where node 2>nul >nul
13 | IF %ERRORLEVEL% NEQ 0 (
14 | echo Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment.
15 | goto error
16 | )
17 |
18 | :: Setup
19 | :: -----
20 |
21 | setlocal enabledelayedexpansion
22 |
23 | SET ARTIFACTS=%~dp0%..\artifacts
24 |
25 | IF NOT DEFINED DEPLOYMENT_SOURCE (
26 | SET DEPLOYMENT_SOURCE=%~dp0%.
27 | )
28 |
29 | IF NOT DEFINED DEPLOYMENT_TARGET (
30 | SET DEPLOYMENT_TARGET=%ARTIFACTS%\wwwroot
31 | )
32 |
33 | IF NOT DEFINED NEXT_MANIFEST_PATH (
34 | SET NEXT_MANIFEST_PATH=%ARTIFACTS%\manifest
35 |
36 | IF NOT DEFINED PREVIOUS_MANIFEST_PATH (
37 | SET PREVIOUS_MANIFEST_PATH=%ARTIFACTS%\manifest
38 | )
39 | )
40 |
41 | IF NOT DEFINED KUDU_SYNC_CMD (
42 | :: Install kudu sync
43 | echo Installing Kudu Sync
44 | call npm install kudusync -g --silent
45 | IF !ERRORLEVEL! NEQ 0 goto error
46 |
47 | :: Locally just running "kuduSync" would also work
48 | SET KUDU_SYNC_CMD=%appdata%\npm\kuduSync.cmd
49 | )
50 | goto Deployment
51 |
52 | :: Utility Functions
53 | :: -----------------
54 |
55 | :SelectNodeVersion
56 |
57 | IF DEFINED KUDU_SELECT_NODE_VERSION_CMD (
58 | :: The following are done only on Windows Azure Websites environment
59 | call %KUDU_SELECT_NODE_VERSION_CMD% "%DEPLOYMENT_SOURCE%" "%DEPLOYMENT_TARGET%" "%DEPLOYMENT_TEMP%"
60 | IF !ERRORLEVEL! NEQ 0 goto error
61 |
62 | IF EXIST "%DEPLOYMENT_TEMP%\__nodeVersion.tmp" (
63 | SET /p NODE_EXE=<"%DEPLOYMENT_TEMP%\__nodeVersion.tmp"
64 | IF !ERRORLEVEL! NEQ 0 goto error
65 | )
66 |
67 | IF EXIST "%DEPLOYMENT_TEMP%\__npmVersion.tmp" (
68 | SET /p NPM_JS_PATH=<"%DEPLOYMENT_TEMP%\__npmVersion.tmp"
69 | IF !ERRORLEVEL! NEQ 0 goto error
70 | )
71 |
72 | IF NOT DEFINED NODE_EXE (
73 | SET NODE_EXE=node
74 | )
75 |
76 | SET NPM_CMD="!NODE_EXE!" "!NPM_JS_PATH!"
77 | ) ELSE (
78 | SET NPM_CMD=npm
79 | SET NODE_EXE=node
80 | )
81 |
82 | goto :EOF
83 |
84 | ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
85 | :: Deployment
86 | :: ----------
87 |
88 | :Deployment
89 | echo Handling angular-cli deployment.
90 |
91 | :: 1. Select node version
92 | call :SelectNodeVersion
93 |
94 | :: 2. Install npm packages
95 | IF EXIST "%DEPLOYMENT_SOURCE%\package.json" (
96 | pushd "%DEPLOYMENT_SOURCE%"
97 | call :ExecuteCmd !NPM_CMD! install
98 | IF !ERRORLEVEL! NEQ 0 goto error
99 | popd
100 | )
101 |
102 | :: 3. Build application
103 | IF EXIST "%DEPLOYMENT_SOURCE%\angular-cli.json" (
104 | pushd "%DEPLOYMENT_SOURCE%"
105 | call :ExecuteCmd node_modules\.bin\ng build --progress false --prod
106 | IF !ERRORLEVEL! NEQ 0 goto error
107 | popd
108 | )
109 |
110 | :: 4. KuduSync
111 | IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (
112 | call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_SOURCE%/dist" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd;node_modules"
113 | IF !ERRORLEVEL! NEQ 0 goto error
114 | )
115 |
116 | ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
117 | goto end
118 |
119 | :: Execute command routine that will echo out when error
120 | :ExecuteCmd
121 | setlocal
122 | set _CMD_=%*
123 | call %_CMD_%
124 | if "%ERRORLEVEL%" NEQ "0" echo Failed exitCode=%ERRORLEVEL%, command=%_CMD_%
125 | exit /b %ERRORLEVEL%
126 |
127 | :error
128 | endlocal
129 | echo An error has occurred during web site deployment.
130 | call :exitSetErrorLevel
131 | call :exitFromFunction 2>nul
132 |
133 | :exitSetErrorLevel
134 | exit /b 1
135 |
136 | :exitFromFunction
137 | ()
138 |
139 | :end
140 | endlocal
141 | echo Finished successfully.
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # ----------------------
4 | # KUDU Deployment Script
5 | # Version: 0.2.2
6 | # ----------------------
7 |
8 | # Helpers
9 | # -------
10 |
11 | exitWithMessageOnError () {
12 | if [ ! $? -eq 0 ]; then
13 | echo "An error has occurred during web site deployment."
14 | echo $1
15 | exit 1
16 | fi
17 | }
18 |
19 | # Prerequisites
20 | # -------------
21 |
22 | # Verify node.js installed
23 | hash node 2>/dev/null
24 | exitWithMessageOnError "Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment."
25 |
26 | # Setup
27 | # -----
28 |
29 | SCRIPT_DIR="${BASH_SOURCE[0]%\\*}"
30 | SCRIPT_DIR="${SCRIPT_DIR%/*}"
31 | ARTIFACTS=$SCRIPT_DIR/../artifacts
32 | KUDU_SYNC_CMD=${KUDU_SYNC_CMD//\"}
33 |
34 | if [[ ! -n "$DEPLOYMENT_SOURCE" ]]; then
35 | DEPLOYMENT_SOURCE=$SCRIPT_DIR
36 | fi
37 |
38 | if [[ ! -n "$NEXT_MANIFEST_PATH" ]]; then
39 | NEXT_MANIFEST_PATH=$ARTIFACTS/manifest
40 |
41 | if [[ ! -n "$PREVIOUS_MANIFEST_PATH" ]]; then
42 | PREVIOUS_MANIFEST_PATH=$NEXT_MANIFEST_PATH
43 | fi
44 | fi
45 |
46 | if [[ ! -n "$DEPLOYMENT_TARGET" ]]; then
47 | DEPLOYMENT_TARGET=$ARTIFACTS/wwwroot
48 | else
49 | KUDU_SERVICE=true
50 | fi
51 |
52 | if [[ ! -n "$KUDU_SYNC_CMD" ]]; then
53 | # Install kudu sync
54 | echo Installing Kudu Sync
55 | npm install kudusync -g --silent
56 | exitWithMessageOnError "npm failed"
57 |
58 | if [[ ! -n "$KUDU_SERVICE" ]]; then
59 | # In case we are running locally this is the correct location of kuduSync
60 | KUDU_SYNC_CMD=kuduSync
61 | else
62 | # In case we are running on kudu service this is the correct location of kuduSync
63 | KUDU_SYNC_CMD=$APPDATA/npm/node_modules/kuduSync/bin/kuduSync
64 | fi
65 | fi
66 |
67 | # Node Helpers
68 | # ------------
69 |
70 | selectNodeVersion () {
71 | if [[ -n "$KUDU_SELECT_NODE_VERSION_CMD" ]]; then
72 | SELECT_NODE_VERSION="$KUDU_SELECT_NODE_VERSION_CMD \"$DEPLOYMENT_SOURCE\" \"$DEPLOYMENT_TARGET\" \"$DEPLOYMENT_TEMP\""
73 | eval $SELECT_NODE_VERSION
74 | exitWithMessageOnError "select node version failed"
75 |
76 | if [[ -e "$DEPLOYMENT_TEMP/__nodeVersion.tmp" ]]; then
77 | NODE_EXE=`cat "$DEPLOYMENT_TEMP/__nodeVersion.tmp"`
78 | exitWithMessageOnError "getting node version failed"
79 | fi
80 |
81 | if [[ -e "$DEPLOYMENT_TEMP/.tmp" ]]; then
82 | NPM_JS_PATH=`cat "$DEPLOYMENT_TEMP/__npmVersion.tmp"`
83 | exitWithMessageOnError "getting npm version failed"
84 | fi
85 |
86 | if [[ ! -n "$NODE_EXE" ]]; then
87 | NODE_EXE=node
88 | fi
89 |
90 | NPM_CMD="\"$NODE_EXE\" \"$NPM_JS_PATH\""
91 | else
92 | NPM_CMD=npm
93 | NODE_EXE=node
94 | fi
95 | }
96 |
97 | ##################################################################################################################################
98 | # Deployment
99 | # ----------
100 |
101 | echo Handling node.js deployment.
102 |
103 | # 1. KuduSync
104 | if [[ "$IN_PLACE_DEPLOYMENT" -ne "1" ]]; then
105 | "$KUDU_SYNC_CMD" -v 50 -f "$DEPLOYMENT_SOURCE" -t "$DEPLOYMENT_TARGET" -n "$NEXT_MANIFEST_PATH" -p "$PREVIOUS_MANIFEST_PATH" -i ".git;.hg;.deployment;deploy.sh"
106 | exitWithMessageOnError "Kudu Sync failed"
107 | fi
108 |
109 | # 2. Select node version
110 | selectNodeVersion
111 |
112 | # 3. Install NPM packages
113 | if [ -e "$DEPLOYMENT_TARGET/package.json" ]; then
114 | cd "$DEPLOYMENT_TARGET"
115 | eval $NPM_CMD install --production
116 | eval $NPM_CMD install --only=dev
117 | exitWithMessageOnError "npm failed"
118 | cd - > /dev/null
119 | fi
120 |
121 | # 4. Angular Prod Build
122 | if [ -e "$DEPLOYMENT_TARGET/.angular-cli.json" ]; then
123 | cd "$DEPLOYMENT_TARGET"
124 | eval ./node_modules/.bin/ng build --prod
125 | exitWithMessageOnError "Angular build failed"
126 | cd - > /dev/null
127 | fi
128 |
129 | ##################################################################################################################################
130 |
131 | # Post deployment stub
132 | if [[ -n "$POST_DEPLOYMENT_ACTION" ]]; then
133 | POST_DEPLOYMENT_ACTION=${POST_DEPLOYMENT_ACTION//\"}
134 | cd "${POST_DEPLOYMENT_ACTION_DIR%\\*}"
135 | "$POST_DEPLOYMENT_ACTION"
136 | exitWithMessageOnError "post deployment action failed"
137 | fi
138 |
139 | echo "Finished successfully. "
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { RxjsInAngularPage } from './app.po';
2 |
3 | describe('rxjs-in-angular App', () => {
4 | let page: RxjsInAngularPage;
5 |
6 | beforeEach(() => {
7 | page = new RxjsInAngularPage();
8 | });
9 |
10 | it('should display message saying app works', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('app works!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, element, by } from 'protractor';
2 |
3 | export class RxjsInAngularPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types":[
8 | "jasmine",
9 | "node"
10 | ]
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular/cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular/cli/plugins/karma')
14 | ],
15 | client:{
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | files: [
19 | { pattern: './src/test.ts', watched: false }
20 | ],
21 | preprocessors: {
22 | './src/test.ts': ['@angular/cli']
23 | },
24 | mime: {
25 | 'text/x-typescript': ['ts','tsx']
26 | },
27 | coverageIstanbulReporter: {
28 | reports: [ 'html', 'lcovonly' ],
29 | fixWebpackSourcePaths: true
30 | },
31 | angularCli: {
32 | environment: 'dev'
33 | },
34 | reporters: config.angularCli && config.angularCli.codeCoverage
35 | ? ['progress', 'coverage-istanbul']
36 | : ['progress', 'kjhtml'],
37 | port: 9876,
38 | colors: true,
39 | logLevel: config.LOG_INFO,
40 | autoWatch: true,
41 | browsers: ['Chrome'],
42 | singleRun: false
43 | });
44 | };
45 |
--------------------------------------------------------------------------------
/licence:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Christos Sakellarios
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/npm-debug.log.3191570879:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/npm-debug.log.3191570879
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rxjs-in-angular",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "ng serve",
8 | "build": "ng build",
9 | "build-prod": "ng build --prod",
10 | "test": "ng test",
11 | "lint": "ng lint",
12 | "e2e": "ng e2e"
13 | },
14 | "private": true,
15 | "dependencies": {
16 | "@angular/animations": "^4.4.6",
17 | "@angular/cdk": "^2.0.0-beta.12",
18 | "@angular/common": "^4.4.6",
19 | "@angular/compiler": "^4.4.6",
20 | "@angular/core": "^4.4.6",
21 | "@angular/forms": "^4.4.6",
22 | "@angular/http": "^4.4.6",
23 | "@angular/material": "^2.0.0-beta.12",
24 | "@angular/platform-browser": "^4.4.6",
25 | "@angular/platform-browser-dynamic": "^4.4.6",
26 | "@angular/router": "^4.4.6",
27 | "@types/lodash": "^4.14.64",
28 | "angular2-highlight-js": "^5.0.0",
29 | "core-js": "^2.5.1",
30 | "hammerjs": "^2.0.8",
31 | "lodash": "^4.17.4",
32 | "rxjs": "^5.5.0",
33 | "zone.js": "^0.8.18"
34 | },
35 | "devDependencies": {
36 | "@angular/cli": "1.4.9",
37 | "@angular/compiler-cli": "^4.4.6",
38 | "@types/jasmine": "2.5.38",
39 | "@types/node": "~8.0.46",
40 | "codelyzer": "~3.2.2",
41 | "jasmine-core": "~2.5.2",
42 | "jasmine-spec-reporter": "~3.2.0",
43 | "karma": "~1.4.1",
44 | "karma-chrome-launcher": "~2.0.0",
45 | "karma-cli": "~1.0.1",
46 | "karma-jasmine": "~1.1.0",
47 | "karma-jasmine-html-reporter": "^0.2.2",
48 | "karma-coverage-istanbul-reporter": "^0.2.0",
49 | "protractor": "~5.1.0",
50 | "ts-node": "~3.3.0",
51 | "tslint": "~5.8.0",
52 | "typescript": "~2.4.0"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './e2e/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | beforeLaunch: function() {
23 | require('ts-node').register({
24 | project: 'e2e/tsconfig.e2e.json'
25 | });
26 | },
27 | onPrepare() {
28 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/src/app/app.component.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Roboto, 'Helvetica Neue', sans-serif;
3 | }
4 |
5 | body * {
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | body .mat-sidenav {
11 | min-width: 15vw;
12 | }
13 |
14 | body .mat-sidenav .mat-button {
15 | width: 100%;
16 | position: relative;
17 | bottom: 0;
18 | margin: 24px 0;
19 | }
20 |
21 | body .demo-content {
22 | padding: 32px;
23 | box-sizing: border-box;
24 | }
25 |
26 | body .mat-toolbar .mat-icon {
27 | cursor: pointer;
28 | }
29 |
30 | body .mat-toolbar .demo-toolbar {
31 | display: flex;
32 | justify-content: space-between;
33 | width: 100%;
34 | }
35 |
36 | body h1 {
37 | font-size: 20px;
38 | }
39 |
40 | .demo-content {
41 | width: 100%;
42 | height: 100%;
43 | box-sizing: border-box;
44 | }
45 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{navItem.name}}
6 |
7 |
8 |
9 |
10 |
11 |
12 | menu
13 |
14 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 |
3 | import { AppComponent } from './app.component';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(async(() => {
7 | TestBed.configureTestingModule({
8 | declarations: [
9 | AppComponent
10 | ],
11 | }).compileComponents();
12 | }));
13 |
14 | it('should create the app', async(() => {
15 | const fixture = TestBed.createComponent(AppComponent);
16 | const app = fixture.debugElement.componentInstance;
17 | expect(app).toBeTruthy();
18 | }));
19 |
20 | it(`should have as title 'app works!'`, async(() => {
21 | const fixture = TestBed.createComponent(AppComponent);
22 | const app = fixture.debugElement.componentInstance;
23 | expect(app.title).toEqual('app works!');
24 | }));
25 |
26 | it('should render title in a h1 tag', async(() => {
27 | const fixture = TestBed.createComponent(AppComponent);
28 | fixture.detectChanges();
29 | const compiled = fixture.debugElement.nativeElement;
30 | expect(compiled.querySelector('h1').textContent).toContain('app works!');
31 | }));
32 | });
33 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewEncapsulation } from '@angular/core';
2 | import { MatSidenav, MatSidenavContainer, MatToolbar, MatList, MatListItem,
3 | MatToolbarBase } from '@angular/material'
4 |
5 | @Component({
6 | moduleId: module.id,
7 | selector: 'app-root',
8 | templateUrl: './app.component.html',
9 | styleUrls: ['./app.component.css'],
10 | // tslint:disable-next-line:use-host-property-decorator
11 | host: {
12 | '[class.unicorn-dark-theme]': 'dark',
13 | },
14 | encapsulation: ViewEncapsulation.None
15 | })
16 | export class AppComponent {
17 |
18 | dark = false;
19 |
20 | navItems = [
21 | { name: 'Create Observables', route: 'create-observables' },
22 | { name: 'Hot & Cold Observables', route: 'hot-and-cold-observables' },
23 | { name: 'Subjects', route: 'subjects' },
24 | { name: 'Latest', route: 'latest' },
25 | { name: 'Merge', route: 'merge' },
26 | { name: 'MergeMap', route: 'merge-map' },
27 | { name: 'SwitchMap', route: 'switch-map' },
28 | { name: 'Filter', route: 'filter' },
29 | { name: 'Scan', route: 'scan' },
30 | { name: 'Reduce', route: 'reduce' },
31 | { name: 'Buffer', route: 'buffer' },
32 |
33 | ];
34 | }
35 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { DataService } from './shared/data.service';
2 | import { BrowserModule } from '@angular/platform-browser';
3 | import { NgModule } from '@angular/core';
4 | import { HttpModule } from '@angular/http';
5 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
6 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
7 | import { RouterModule } from '@angular/router';
8 |
9 | import { AppComponent } from './app.component';
10 | import { CreateObservablesComponent } from './create-observables/create-observables.component';
11 | import { SubjectsComponent } from './subjects/subjects.component';
12 | import { HotAndColdComponent } from './hot-and-cold/hot-and-cold.component';
13 | import { OperatorsComponent } from './operators/operators.component';
14 | import { APP_ROUTES } from 'app/app.routes';
15 | import {
16 | MatButtonModule,
17 | MatButtonToggleModule,
18 | MatFormFieldModule,
19 | MatSidenavModule,
20 | MatDialogModule,
21 | MatCommonModule,
22 | MatInputModule,
23 | MatNativeDateModule,
24 | MatSelectModule,
25 | MatDialog,
26 | MatGridListModule,
27 | MatToolbarModule,
28 | MatCardModule,
29 | MatListModule,
30 | MatProgressBarModule,
31 | MatSliderModule,
32 | MatTabsModule,
33 | MatTooltipModule
34 | } from '@angular/material';
35 | import { OverlayContainer, FullscreenOverlayContainer } from '@angular/cdk/overlay';
36 |
37 | import { HighlightJsModule, HighlightJsService } from 'angular2-highlight-js';
38 |
39 | import 'hammerjs';
40 | import { HomeComponent } from './home/home.component';
41 | import { LatestComponent } from './operators/latest/latest.component';
42 | import { MergeComponent } from './operators/merge/merge.component';
43 | import { FilterComponent } from './operators/filter/filter.component';
44 | import { ScanComponent } from './operators/scan/scan.component';
45 | import { ReduceComponent } from './operators/reduce/reduce.component';
46 | import { BufferComponent } from './operators/buffer/buffer.component';
47 | import { MergeMapComponent } from './operators/merge-map/merge-map.component';
48 | import { SwitchMapComponent } from './operators/switch-map/switch-map.component';
49 |
50 | import './shared/rxjs-operators';
51 |
52 | @NgModule({
53 | declarations: [
54 | AppComponent,
55 | CreateObservablesComponent,
56 | SubjectsComponent,
57 | HotAndColdComponent,
58 | OperatorsComponent,
59 | HomeComponent,
60 | LatestComponent,
61 | MergeComponent,
62 | FilterComponent,
63 | ScanComponent,
64 | ReduceComponent,
65 | BufferComponent,
66 | MergeMapComponent,
67 | SwitchMapComponent
68 | ],
69 | imports: [
70 | BrowserModule,
71 | BrowserAnimationsModule,
72 | FormsModule,
73 | HttpModule,
74 | HighlightJsModule,
75 | ReactiveFormsModule,
76 | RouterModule.forRoot(APP_ROUTES),
77 | MatButtonModule,
78 | MatButtonToggleModule,
79 | MatDialogModule,
80 | MatCommonModule,
81 | MatInputModule,
82 | MatSidenavModule,
83 | MatFormFieldModule,
84 | MatNativeDateModule,
85 | MatSelectModule,
86 | MatGridListModule,
87 | MatToolbarModule,
88 | MatCardModule,
89 | MatListModule,
90 | MatProgressBarModule,
91 | MatSliderModule,
92 | MatTabsModule,
93 | MatTooltipModule
94 | ],
95 | providers: [
96 | DataService,
97 | MatDialog,
98 | HighlightJsService,
99 | { provide: OverlayContainer, useClass: FullscreenOverlayContainer }
100 | ],
101 | bootstrap: [AppComponent]
102 | })
103 | export class AppModule { }
104 |
--------------------------------------------------------------------------------
/src/app/app.routes.ts:
--------------------------------------------------------------------------------
1 | import { ReduceComponent } from './operators/reduce/reduce.component';
2 | import { ScanComponent } from './operators/scan/scan.component';
3 | import { FilterComponent } from './operators/filter/filter.component';
4 | import { MergeComponent } from './operators/merge/merge.component';
5 | import { LatestComponent } from './operators/latest/latest.component';
6 | import { OperatorsComponent } from './operators/operators.component';
7 | import { SubjectsComponent } from './subjects/subjects.component';
8 | import { HotAndColdComponent } from './hot-and-cold/hot-and-cold.component';
9 | import { CreateObservablesComponent } from './create-observables/create-observables.component';
10 | import { Routes } from '@angular/router';
11 | import { HomeComponent } from './home/home.component';
12 | import { BufferComponent } from 'app/operators/buffer/buffer.component';
13 | import { MergeMapComponent } from 'app/operators/merge-map/merge-map.component';
14 | import { SwitchMapComponent } from 'app/operators/switch-map/switch-map.component';
15 |
16 | export const APP_ROUTES: Routes = [
17 | { path: '', component: HomeComponent },
18 | { path: 'create-observables', component: CreateObservablesComponent },
19 | { path: 'hot-and-cold-observables', component: HotAndColdComponent },
20 | { path: 'subjects', component: SubjectsComponent },
21 | { path: 'operators', component: OperatorsComponent },
22 | { path: 'latest', component: LatestComponent },
23 | { path: 'merge', component: MergeComponent },
24 | { path: 'merge-map', component: MergeMapComponent },
25 | { path: 'switch-map', component: SwitchMapComponent },
26 | { path: 'filter', component: FilterComponent },
27 | { path: 'scan', component: ScanComponent },
28 | { path: 'reduce', component: ReduceComponent },
29 | { path: 'buffer', component: BufferComponent }
30 | ];
31 |
--------------------------------------------------------------------------------
/src/app/create-observables/create-observables.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/create-observables/create-observables.component.css
--------------------------------------------------------------------------------
/src/app/create-observables/create-observables.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Create Observables demo
4 |
5 |
6 |
7 | Example
8 |
9 |
10 | Create Observable from scratch
11 | The Observer function
12 |
13 | {{user.name}}
14 |
15 |
16 | Observer can emit either new values and errors or complete the observable. Each time we subscribe to the observable,
17 | the observer function fires (cold observable)
18 |
19 | Randomize
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Observable.of
29 | The use of the async
30 |
31 | {{icon}}
32 |
33 |
34 | Observable.of will emit the icons as a single item-array. The async keyword is used to subscribe and listen
35 | to values emitted by the observable
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | {{seconds}} remained to display dialog..
45 | Observable.timer
46 |
47 |
48 | Creates an Observable that emits a sequence of integers spaced by a given time interval
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | Operator Of
58 | Observable.of(array)
59 |
60 |
61 | Emitted item is array: {{ofEmittedIsArray}} Total Items:
62 | {{ofEmittedVal.length}}
63 |
64 |
65 | Converts arguments to an observable sequence.
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | Operator From
75 | Observable.from(array)
76 |
77 |
78 | Emitted item is array: {{fromEmittedIsArray}} Emitted item is Object:
79 | {{fromEmittedIsObject}}
80 |
81 | Object:
82 | {{fromEmittedVal | json}}
83 |
84 |
85 |
86 | Converts arguments to an observable sequence.
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | I'm a template dialog. I've been opened using an interval Observable!
95 |
96 | Observable.interval(5000).take(1)
97 |
98 |
99 |
100 |
101 |
102 | About
103 |
104 |
105 |
106 |
107 |
108 | Sample Code
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/src/app/create-observables/create-observables.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { CreateObservablesComponent } from './create-observables.component';
4 |
5 | describe('CreateObservablesComponent', () => {
6 | let component: CreateObservablesComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ CreateObservablesComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(CreateObservablesComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/create-observables/create-observables.component.ts:
--------------------------------------------------------------------------------
1 | import {MatDialog, MatDialogRef, MatDialogConfig, MAT_DIALOG_DATA} from '@angular/material';
2 | import { DataService } from './../shared/data.service';
3 | import { Component, OnInit, ViewChild, TemplateRef } from '@angular/core';
4 | import { Observable } from 'rxjs/Observable';
5 | import { Subscription } from 'rxjs/Subscription';
6 | import { sampleCode } from './sample-code';
7 | import * as _ from 'lodash';
8 |
9 | @Component({
10 | moduleId: module.id,
11 | selector: 'app-create-observables',
12 | templateUrl: './create-observables.component.html',
13 | styleUrls: ['./create-observables.component.css']
14 | })
15 | export class CreateObservablesComponent implements OnInit {
16 | users: any[] = [];
17 | icons$: Observable;
18 |
19 | simple$ = new Observable(observer => {
20 | console.log('Generating observable..');
21 | setTimeout(function () {
22 | observer.next('An item!');
23 | setTimeout(function () {
24 | observer.next('Another item!');
25 | observer.complete();
26 | }, 1000);
27 | }, 1000);
28 | });
29 |
30 | error$ = new Observable(observer => {
31 | observer.error(new Error('WHOA!'));
32 | });
33 |
34 | sideEffet$ = 0;
35 |
36 | @ViewChild('dialog') template: TemplateRef;
37 | seconds = 5;
38 | config: MatDialogConfig = {
39 | disableClose: false,
40 | hasBackdrop: true,
41 | backdropClass: '',
42 | width: '',
43 | height: '',
44 | position: {
45 | top: '',
46 | bottom: '',
47 | left: '',
48 | right: ''
49 | }
50 | };
51 |
52 | ofEmittedVal: any;
53 | ofEmittedIsArray: boolean;
54 | fromEmittedVal: any;
55 | fromEmittedIsArray: boolean;
56 | fromEmittedIsObject: boolean;
57 |
58 | sampleCode = sampleCode;
59 |
60 | constructor(public dialog: MatDialog, private ds: DataService) { }
61 |
62 | ngOnInit() {
63 | this.fromScratch();
64 | this.of();
65 | this.runTimer();
66 | this.openTemplate();
67 | this.ofArray();
68 | this.from();
69 | }
70 |
71 | fromScratch() {
72 | const self = this;
73 | this.users = [];
74 | const users$ = new Observable(observer => {
75 | for (let i = 0; i < 4; i++) {
76 | setTimeout(function () {
77 | const userId = Math.floor((Math.random() * 9) + 1);
78 | observer.next(self.ds.getUsersSync(userId));
79 | }, (i + 1) * 2000);
80 | }
81 | });
82 |
83 | users$.subscribe(user => {
84 | this.users.push(user);
85 | });
86 | }
87 |
88 | of() {
89 | const mdIcons: [string] = ['home', 'donut_large', 'alarm_on', 'announcement', '3d_rotation', 'copyright', 'check_circle', 'language'];
90 | this.icons$ = Observable.of(mdIcons);
91 | }
92 |
93 | ofArray() {
94 | this.ds.getAllPosts().subscribe(val => {
95 | this.ofEmittedVal = _.cloneDeep(val);
96 | this.ofEmittedIsArray = val instanceof Array;
97 | });
98 | }
99 |
100 | from() {
101 | this.ds.getUsers().subscribe(val => {
102 | this.fromEmittedVal = val;
103 | this.fromEmittedIsArray = val instanceof Array;
104 | this.fromEmittedIsObject = val instanceof Object;
105 | });
106 | }
107 |
108 | runTimer() {
109 | Observable.timer(0, 1000).take(5).subscribe(value => this.seconds--);
110 | }
111 |
112 | openTemplate() {
113 | Observable.interval(5000).take(1).subscribe(() =>
114 | this.dialog.open(this.template, this.config)
115 | );
116 | }
117 |
118 | randomize() {
119 | this.fromScratch();
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/app/create-observables/sample-code.ts:
--------------------------------------------------------------------------------
1 | export const sampleCode = `
2 |
3 |
4 | // Observer
5 | const users$ = new Observable(observer => {
6 | for (let i = 0; i < 4; i++) {
7 | setTimeout(function () {
8 | const userId = Math.floor((Math.random() * 9) + 1);
9 | observer.next(self.ds.getUsersSync(userId));
10 | }, (i + 1) * 2000);
11 | }
12 | });
13 |
14 | users$.subscribe(user => {
15 | this.users.push(user);
16 | });
17 |
18 | // Observable.Of
19 | const mdIcons: [string] = ['home', 'donut_large', 'alarm_on', 'announcement', '3d_rotation', 'copyright',
20 | 'check_circle', 'language'];
21 | this.icons$ = Observable.of(mdIcons);
22 |
23 | // Observable.timer
24 | Observable.timer(0, 1000).take(5).subscribe(value => this.seconds--);
25 |
26 | // Observable.Of(array)
27 | this.ds.getAllPosts().subscribe(val => {
28 | this.ofEmittedVal = _.cloneDeep(val);
29 | this.ofEmittedIsArray = val instanceof Array;
30 | });
31 |
32 | getAllPosts() {
33 | return Observable.of(MOCK_POSTS);
34 | }
35 |
36 | // Observable.from(array)
37 | this.ds.getUsers().subscribe(val => {
38 | this.fromEmittedVal = val;
39 | this.fromEmittedIsArray = val instanceof Array;
40 | this.fromEmittedIsObject = val instanceof Object;
41 | });
42 |
43 | getUsers(): Observable {
44 | return Observable.from(MOCK_USERS);
45 | }
46 |
47 |
48 |
49 | `;
--------------------------------------------------------------------------------
/src/app/home/home.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/home/home.component.css
--------------------------------------------------------------------------------
/src/app/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{word.letter}}
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/app/home/home.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { HomeComponent } from './home.component';
4 |
5 | describe('HomeComponent', () => {
6 | let component: HomeComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ HomeComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(HomeComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Observable } from 'rxjs/Observable';
2 | import { Subject } from 'rxjs/Subject';
3 | import { BehaviorSubject } from 'rxjs/BehaviorSubject';
4 | import { Component, OnInit } from '@angular/core';
5 | import { MatGridList, MatGridTile } from '@angular/material'
6 |
7 | @Component({
8 | selector: 'app-home',
9 | templateUrl: './home.component.html',
10 | styleUrls: ['./home.component.css']
11 | })
12 | export class HomeComponent implements OnInit {
13 |
14 | phrases: any[] = [
15 | [
16 | // tslint:disable-next-line:max-line-length
17 | { id: 0, letter: '' }, { id: 1, letter: '' }, { id: 2, letter: '' }, { id: 3, letter: '' }, { id: 4, letter: '' }, { id: 5, letter: '' }, { id: 6, letter: '' }, { id: 7, letter: '' }, { id: 8, letter: '' }, { id: 9, letter: '' }, { id: 10, letter: '' }, { id: 11, letter: '' }, { id: 12, letter: '' }
18 | ],
19 | [
20 | // tslint:disable-next-line:max-line-length
21 | { id: 0, letter: '' }, { id: 1, letter: '' }, { id: 2, letter: 'R' }, { id: 3, letter: 'e' }, { id: 4, letter: 'a' }, { id: 5, letter: 'c' }, { id: 6, letter: 't' }, { id: 7, letter: 'i' }, { id: 8, letter: 'v' }, { id: 9, letter: 'e' }, { id: 10, letter: 'X' }, { id: 11, letter: '' }, { id: 12, letter: '' }
22 | ],
23 | [
24 | // tslint:disable-next-line:max-line-length
25 | { id: 0, letter: '' }, { id: 1, letter: '' }, { id: 2, letter: 'O' }, { id: 3, letter: 'p' }, { id: 4, letter: 'e' }, { id: 5, letter: 'r' }, { id: 6, letter: 'a' }, { id: 7, letter: 't' }, { id: 8, letter: 'o' }, { id: 9, letter: 'r' }, { id: 10, letter: 's' }, { id: 11, letter: '' }, { id: 12, letter: '' }
26 | ],
27 | [
28 | // tslint:disable-next-line:max-line-length
29 | { id: 0, letter: '' }, { id: 1, letter: '' }, { id: 2, letter: '' }, { id: 3, letter: '' }, { id: 4, letter: '' }, { id: 5, letter: '', image: 'reactive.jpg' }, { id: 6, letter: '' }, { id: 7, letter: '', image: 'angular-2.jpg' }, { id: 8, letter: '' }, { id: 9, letter: '' }, { id: 10, letter: '' }, { id: 11, letter: '' }, { id: 12, letter: '' }
30 | ],
31 | [
32 | // tslint:disable-next-line:max-line-length
33 | { id: 0, letter: '' }, { id: 1, letter: '' }, { id: 2, letter: '' }, { id: 3, letter: 'A' }, { id: 4, letter: 'n' }, { id: 5, letter: 'g' }, { id: 6, letter: 'u' }, { id: 7, letter: 'l' }, { id: 8, letter: 'a' }, { id: 9, letter: 'r' }, { id: 10, letter: '' }, { id: 11, letter: '' }, { id: 12, letter: '' }
34 | ]
35 | ];
36 |
37 | constructor() { }
38 |
39 | ngOnInit() {
40 | this.start();
41 | }
42 |
43 | start() {
44 | let counter = 0;
45 | const interval$ = Observable.interval(100).take(13 * 5).map(i => i % 13);
46 | const indexSubject: Subject = new BehaviorSubject(counter);
47 |
48 | interval$.withLatestFrom(indexSubject)
49 | .subscribe(([i, j]) => {
50 | this.phrases[j][i].highlighted = true;
51 | if (i === 12) {
52 | counter++;
53 | indexSubject.next(counter);
54 | }
55 | });
56 |
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/app/hot-and-cold/hot-and-cold.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/hot-and-cold/hot-and-cold.component.css
--------------------------------------------------------------------------------
/src/app/hot-and-cold/hot-and-cold.component.html:
--------------------------------------------------------------------------------
1 |
2 | Observable fromEvent
3 | A Hot observable
4 |
5 |
6 |
7 |
8 | Hot observables emit values all the time, it's us that we listen only if we subscribe
9 |
10 | {{isListening === false ? 'Subscribe' : 'Unsubscribe'}}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Cold Observables
20 | Each subscriber get its own observable..
21 |
22 |
23 |
24 |
25 |
27 |
28 |
29 | Subscribe
30 | {{subscriber.sliderValue}}
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Hot Observables
41 | Each subscriber receives the same emitted value..
42 |
43 |
45 |
46 |
47 |
48 |
49 |
51 |
52 |
53 | Subscribe
54 | {{subscriber.sliderValue}}
55 |
56 |
57 |
58 | Connect
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/src/app/hot-and-cold/hot-and-cold.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { HotAndColdComponent } from './hot-and-cold.component';
4 |
5 | describe('HotAndColdComponent', () => {
6 | let component: HotAndColdComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ HotAndColdComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(HotAndColdComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/hot-and-cold/hot-and-cold.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
2 | import { Observable } from 'rxjs/Observable';
3 | import { Subscription } from 'rxjs/Subscription';
4 | import { MatSlider } from '@angular/material'
5 | import * as _ from 'lodash';
6 |
7 | @Component({
8 | selector: 'app-hot-and-cold',
9 | templateUrl: './hot-and-cold.component.html',
10 | styleUrls: ['./hot-and-cold.component.css']
11 | })
12 | export class HotAndColdComponent implements OnInit {
13 |
14 | @ViewChild('mouseEventCard', { read: ElementRef })
15 | mouseEventCard: ElementRef;
16 | mouseEventSubscription$: Subscription;
17 | mouseMovesEvent$: Observable;
18 | isListening = false;
19 |
20 | coldInterval$ = Observable.interval(1000).skip(1).take(10);
21 | hotInterval$ = Observable.interval(1000).skip(1).take(20).publish();
22 | hotIntervalSlider: any = {
23 | sliderValue: 0, sliderDisabled: true, sliderVisible: true
24 | };
25 |
26 | coldSubscribers: any[] = [
27 | { index: 1, sliderValue: 0, sliderDisabled: true, sliderVisible: false },
28 | { index: 2, sliderValue: 0, sliderDisabled: true, sliderVisible: false },
29 | { index: 3, sliderValue: 0, sliderDisabled: true, sliderVisible: false },
30 | { index: 4, sliderValue: 0, sliderDisabled: true, sliderVisible: false }
31 | ];
32 |
33 | hotSubscribers: any[] = [
34 | { index: 1, sliderValue: 0, sliderDisabled: true, sliderVisible: false },
35 | { index: 2, sliderValue: 0, sliderDisabled: true, sliderVisible: false },
36 | { index: 3, sliderValue: 0, sliderDisabled: true, sliderVisible: false },
37 | { index: 4, sliderValue: 0, sliderDisabled: true, sliderVisible: false }
38 | ];
39 |
40 | constructor() { }
41 |
42 | ngOnInit() { }
43 |
44 | toggleMouseMoveEvent() {
45 | this.isListening = !this.isListening;
46 | if (!this.isListening) {
47 | this.mouseEventSubscription$.unsubscribe();
48 | return;
49 | }
50 | // The hot observable
51 | const self = this;
52 | this.mouseMovesEvent$ = Observable.fromEvent(this.mouseEventCard.nativeElement, 'mousemove');
53 | const context: CanvasRenderingContext2D = this.mouseEventCard.nativeElement.getContext('2d');
54 | self.mouseEventSubscription$ = this.mouseMovesEvent$.subscribe(event => {
55 | const pos = this.getMousePos(this.mouseEventCard.nativeElement, event);
56 | const posx = pos.x;
57 | const posy = pos.y;
58 | context.fillStyle = '#3f51b5';
59 | context.fillRect(posx - 2, posy - 2, 2, 2);
60 |
61 | setTimeout(function () {
62 | context.fillStyle = 'white';
63 | context.fillRect(posx - 2, posy - 2, 2, 2);
64 | }, 2000);
65 | });
66 | }
67 |
68 | getMousePos(canvas, evt) {
69 | const rect = canvas.getBoundingClientRect(), // abs. size of element
70 | scaleX = canvas.width / rect.width, // relationship bitmap vs. element for X
71 | scaleY = canvas.height / rect.height; // relationship bitmap vs. element for Y
72 |
73 | return {
74 | x: (evt.clientX - rect.left) * scaleX, // scale mouse coordinates after they have
75 | y: (evt.clientY - rect.top) * scaleY // been adjusted to be relative to element
76 | };
77 | }
78 |
79 | unsubscribeFromMouseMoveEvent() {
80 | if (this.mouseEventSubscription$) {
81 |
82 | }
83 | }
84 |
85 | coldSubscribe(subscriberIndex: number) {
86 | const subscriber = _.find(this.coldSubscribers, s => s.index === subscriberIndex);
87 | subscriber.sliderDisabled = false;
88 | subscriber.sliderVisible = true;
89 |
90 | this.coldInterval$.subscribe(val => {
91 | subscriber.sliderValue = val;
92 | },
93 | error => console.log(error),
94 | () => {
95 | subscriber.sliderDisabled = true;
96 | });
97 | }
98 |
99 | connect() {
100 | this.hotInterval$.connect();
101 |
102 | this.hotInterval$.subscribe(val => {
103 | this.hotIntervalSlider.sliderDisabled = false;
104 | this.hotIntervalSlider.sliderValue = val;
105 | },
106 | error => console.log(error),
107 | () => this.hotIntervalSlider.sliderDisabled = true)
108 | }
109 |
110 | hotSubscribe(subscriberIndex: number) {
111 | const subscriber = _.find(this.hotSubscribers, s => s.index === subscriberIndex);
112 | subscriber.sliderDisabled = false;
113 | subscriber.sliderVisible = true;
114 |
115 | this.hotInterval$.subscribe(val => {
116 | subscriber.sliderValue = val;
117 | },
118 | error => console.log(error),
119 | () => {
120 | subscriber.sliderDisabled = true;
121 | });
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/src/app/operators/buffer/buffer.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/operators/buffer/buffer.component.css
--------------------------------------------------------------------------------
/src/app/operators/buffer/buffer.component.html:
--------------------------------------------------------------------------------
1 |
2 | Buffer demo
3 |
4 |
5 |
6 | Example
7 |
8 |
9 |
11 | Select 5 numbers to add
12 |
13 |
14 |
15 | {{button}}
16 |
17 |
18 |
19 | You have selected:
20 | {{number}}
21 | Total: {{total}}
22 |
23 |
24 |
25 |
26 | About
27 |
28 |
29 |
30 |
31 |
32 | Sample Code
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/app/operators/buffer/buffer.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { BufferComponent } from './buffer.component';
4 |
5 | describe('BufferComponent', () => {
6 | let component: BufferComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ BufferComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(BufferComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/operators/buffer/buffer.component.ts:
--------------------------------------------------------------------------------
1 | import { Observable, Subject } from 'rxjs';
2 | import { Component, OnInit } from '@angular/core';
3 | import { sampleCode } from './sample-code';
4 |
5 | @Component({
6 | selector: 'app-buffer',
7 | templateUrl: './buffer.component.html',
8 | styleUrls: ['./buffer.component.css']
9 | })
10 | export class BufferComponent implements OnInit {
11 |
12 | sampleCode = sampleCode;
13 |
14 | sliderValue = 0;
15 | numbersSubject$: Subject = new Subject();
16 | latestNumbers: number[] = [];
17 | buttons: number[] = [];
18 | total = 0;
19 |
20 | constructor() { }
21 |
22 | ngOnInit() {
23 | for (let i = 0; i < 10; i++) {
24 | this.buttons.push(i);
25 | }
26 |
27 | this.bufferCount();
28 | }
29 |
30 | addNumber(number) {
31 | this.sliderValue++;
32 | this.numbersSubject$.next(number);
33 | }
34 |
35 | bufferCount() {
36 | this.numbersSubject$.bufferCount(5).subscribe((array: number[]) => {
37 | console.log(array);
38 | this.total = 0;
39 | this.sliderValue = 0;
40 | this.latestNumbers = [];
41 | array.forEach(number => {
42 | this.total += number;
43 | this.latestNumbers.push(number);
44 | });
45 | });
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/app/operators/buffer/sample-code.ts:
--------------------------------------------------------------------------------
1 | export const sampleCode = `
2 |
3 |
4 | sliderValue = 0;
5 | numbersSubject$: Subject = new Subject();
6 | latestNumbers: number[] = [];
7 | buttons: number[] = [];
8 | total = 0;
9 |
10 | addNumber(number) {
11 | this.sliderValue++;
12 | this.numbersSubject$.next(number);
13 | }
14 |
15 | bufferCount() {
16 | this.numbersSubject$.bufferCount(5).subscribe((array: number[]) => {
17 | console.log(array);
18 | this.total = 0;
19 | this.sliderValue = 0;
20 | this.latestNumbers = [];
21 | array.forEach(number => {
22 | this.total += number;
23 | this.latestNumbers.push(number);
24 | });
25 | });
26 | }
27 |
28 |
29 | `;
30 |
--------------------------------------------------------------------------------
/src/app/operators/filter/filter.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/operators/filter/filter.component.css
--------------------------------------------------------------------------------
/src/app/operators/filter/filter.component.html:
--------------------------------------------------------------------------------
1 |
2 | Filter operator demo
3 |
4 |
5 |
6 | Example
7 |
8 |
9 |
10 |
11 | {{user.name}}
12 |
13 |
14 |
15 |
16 |
17 | {{post.title}}
18 |
19 |
20 |
21 |
22 | API call on condition...
23 |
24 |
25 |
26 |
27 | About
28 |
29 |
30 |
31 |
32 |
33 | Sample Code
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/app/operators/filter/filter.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { FilterComponent } from './filter.component';
4 |
5 | describe('FilterComponent', () => {
6 | let component: FilterComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ FilterComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(FilterComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/operators/filter/filter.component.ts:
--------------------------------------------------------------------------------
1 | import { sampleCode } from './sample-code';
2 | import { Observable, Subscription, Subject } from 'rxjs';
3 | import { DataService } from './../../shared/data.service';
4 | import { Component, OnInit } from '@angular/core';
5 |
6 | @Component({
7 | selector: 'app-filter',
8 | templateUrl: './filter.component.html',
9 | styleUrls: ['./filter.component.css']
10 | })
11 | export class FilterComponent implements OnInit {
12 |
13 | userId: number;
14 | selectedUser$: Subject = new Subject();
15 | userPosts: { [key: number]: any[] } = {};
16 | loadedPosts: any[] = [];
17 | loading = false;
18 |
19 | sampleCode = sampleCode;
20 |
21 | constructor(public ds: DataService) { }
22 |
23 | ngOnInit() {
24 | this.selectedUser$
25 | .do(userId => {
26 | if (this.userPosts[userId]) {
27 | this.loadedPosts = [...this.userPosts[userId]];
28 | }
29 | })
30 | .filter(userId => !this.userPosts[userId])
31 | .subscribe((userId) => {
32 | this.loading = true;
33 | this.loadedPosts = [];
34 | this.ds.getUserPosts(userId, 2000).subscribe((records) => {
35 | this.loading = false;
36 | this.userPosts[userId] = records;
37 | this.loadedPosts = [...records];
38 | }
39 | );
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/app/operators/filter/sample-code.ts:
--------------------------------------------------------------------------------
1 | export const sampleCode = `
2 |
3 |
4 | selectedUser$: Subject = new Subject();
5 | userPosts: { [key: number]: any[] } = {};
6 | loadedPosts: any[] = [];
7 | loading = false;
8 |
9 | this.selectedUser$
10 | .do(userId => {
11 | if (this.userPosts[userId]) {
12 | this.loadedPosts = [...this.userPosts[userId]];
13 | }
14 | })
15 | .filter(userId => !this.userPosts[userId])
16 | .subscribe((userId) => {
17 | this.loading = true;
18 | this.loadedPosts = [];
19 | this.ds.getUserPosts(userId).subscribe((records) => {
20 | this.loading = false;
21 | this.userPosts[userId] = records;
22 | this.loadedPosts = [...records];
23 | }
24 | );
25 | });
26 |
27 | getUserPosts(userId: number): Observable {
28 | return Observable.of(_.filter(MOCK_POSTS, p => p.userId === userId)).delay(2000);
29 | }
30 |
31 |
32 | `;
33 |
--------------------------------------------------------------------------------
/src/app/operators/latest/latest.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/operators/latest/latest.component.css
--------------------------------------------------------------------------------
/src/app/operators/latest/latest.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Zip operator demo
4 |
5 |
6 |
7 | Example
8 |
9 |
10 |
11 |
13 |
14 |
15 | {{user.username}}
16 | {{user.name}}
17 |
18 |
19 |
20 |
21 | The zip operator will subscribe to all inner observables, waiting for each to emit a value. Once this occurs,
22 | all values with the corresponding index will be emitted. This will continue until at least one inner observable
23 | completes.
24 |
25 |
26 | Set interval 10
27 | Set interval 5
28 |
29 |
30 |
31 |
32 | Find the lowest values among products
33 |
34 |
35 |
36 |
37 | About
38 |
39 |
40 |
41 |
42 |
43 | Sample Code
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | Observable.withLatestFrom & Observable.combineLatest operators demo
58 |
59 |
60 |
61 | Example
62 |
63 |
64 |
65 |
66 |
67 |
68 | withLatestFrom
69 |
70 |
71 | combineLatest
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | {{user.name}}
80 |
81 |
82 |
83 | info
84 |
85 |
86 |
87 |
88 |
89 | View Company
90 | card_travel
91 |
92 |
93 | View Address
94 | add_location
95 |
96 |
97 | View Posts
98 | rss_feed
99 |
100 |
101 |
102 | info
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | {{selectedUser?.company.name}}
112 | {{selectedUser?.company.catchPhrase}}
113 | {{selectedUser?.company.bs}}
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | {{selectedUser?.address.street}}
122 | {{selectedUser?.address.suite}}
123 | {{selectedUser?.address.city}}
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | {{post.title}}
133 | {{post.body}}
134 |
135 |
136 |
137 |
138 |
139 |
140 | Combines the source Observable with other Observables to create an Observable whose values are calculated from
141 | the latest values of each, only when the source emits..
142 |
143 |
144 |
145 |
146 |
147 | About
148 |
149 |
150 |
151 |
152 |
153 | Sample Code
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
--------------------------------------------------------------------------------
/src/app/operators/latest/latest.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { LatestComponent } from './latest.component';
4 |
5 | describe('LatestComponent', () => {
6 | let component: LatestComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ LatestComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(LatestComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/operators/latest/latest.component.ts:
--------------------------------------------------------------------------------
1 | import { DataService } from './../../shared/data.service';
2 | import { Component, OnInit } from '@angular/core';
3 | import { Observable } from 'rxjs/Observable';
4 | import { Subject } from 'rxjs/Subject';
5 | import { BehaviorSubject } from 'rxjs/BehaviorSubject';
6 | import { MatSlider } from '@angular/material'
7 | import * as _ from 'lodash';
8 | import { sampleCodeZip, sampleCodeCombine } from 'app/operators/latest/sample-code';
9 |
10 | @Component({
11 | selector: 'app-latest',
12 | templateUrl: './latest.component.html',
13 | styleUrls: ['./latest.component.css']
14 | })
15 | export class LatestComponent implements OnInit {
16 |
17 | // Example 1
18 | sliderValue = 0;
19 | sliderDisabled = false;
20 | users: any[] = [];
21 |
22 | // Example 2
23 | selectedUser$ = new Subject();
24 | selectedOption$ = new Subject();
25 | selectedUser: any;
26 | selectedUserPosts: any[] = [];
27 | operator = 'withLatestFrom';
28 | tooltip = this.operator + ' observable';
29 |
30 | sampleCodeZip = sampleCodeZip;
31 |
32 | sampleCodeCombine = sampleCodeCombine;
33 |
34 | constructor(public ds: DataService) { }
35 |
36 | ngOnInit() {
37 | this.startWithLatestFrom();
38 | this.startCombineLatest();
39 | }
40 |
41 | startZipping(seconds) {
42 | this.users = [];
43 | this.sliderValue = 0;
44 | this.sliderDisabled = false;
45 |
46 | const timer$ = Observable.timer(1000, 1000).take(seconds);
47 |
48 | this.ds.wsOnUser(1000, 10).zip(timer$, (user, sec) => {
49 | return { user, sec };
50 | }
51 | ).subscribe(info => {
52 | this.users.push(info.user);
53 | this.sliderValue = info.sec + 1;
54 | },
55 | error => console.log(error),
56 | () => this.sliderDisabled = true);
57 |
58 | }
59 |
60 | startWithLatestFrom() {
61 | const self = this;
62 | this.selectedOption$
63 | .withLatestFrom(this.selectedUser$)
64 | .subscribe(([option, user]) => {
65 | if (self.operator === 'withLatestFrom') {
66 | console.log('withLatestFrom');
67 | this.selectedUser = user;
68 | this.selectedUserPosts = _.filter(this.ds.getPostsSync(), (p: any) => p.userId === this.selectedUser.id);
69 | }
70 | });
71 | }
72 |
73 | startCombineLatest() {
74 | const self = this;
75 | this.selectedOption$
76 | .combineLatest(this.selectedUser$)
77 | .subscribe(([option, user]) => {
78 | if (self.operator === 'combineLatest') {
79 | console.log('combineLatest');
80 | this.selectedUser = user;
81 | this.selectedUserPosts = _.filter(this.ds.getPostsSync(), (p: any) => p.userId === this.selectedUser.id);
82 | }
83 | });
84 | }
85 |
86 | nextOption(option) {
87 | this.selectedOption$.next(option);
88 | }
89 |
90 | nextUser(user) {
91 | this.selectedUser$.next(user);
92 | }
93 |
94 | switchOperator(operator) {
95 | this.tooltip = this.operator + ' observable';
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/app/operators/latest/sample-code.ts:
--------------------------------------------------------------------------------
1 | export const sampleCodeZip = `
2 |
3 |
4 | startZipping(seconds) {
5 | this.users = [];
6 | this.sliderValue = 0;
7 | this.sliderDisabled = false;
8 |
9 | const timer$ = Observable.timer(1000, 1000).take(seconds);
10 |
11 | this.ds.wsOnUser(1000, 10).zip(timer$, (user, sec) => {
12 | return { user, sec };
13 | }).subscribe(info => {
14 | this.users.push(info.user);
15 | this.sliderValue = info.sec + 1;
16 | },
17 | error => console.log(error),
18 | () => this.sliderDisabled = true);
19 | }
20 |
21 | // onUser
22 | wsOnUser(delay, size?: number): Observable {
23 | return Observable
24 | .interval(delay).take(size === undefined ? MOCK_USERS.length : size)
25 | .map(i => MOCK_USERS[i]);
26 | }
27 |
28 |
29 | `;
30 |
31 | export const sampleCodeCombine = `
32 |
33 |
34 | startWithLatestFrom() {
35 | const self = this;
36 | this.selectedOption$
37 | .withLatestFrom(this.selectedUser$)
38 | .subscribe(([option, user]) => {
39 | if (self.operator === 'withLatestFrom') {
40 | console.log('withLatestFrom');
41 | this.selectedUser = user;
42 | this.selectedUserPosts = _.filter(this.ds.getPostsSync(), (p: any) => p.userId === this.selectedUser.id);
43 | }
44 | });
45 | }
46 |
47 | startCombineLatest() {
48 | const self = this;
49 | this.selectedOption$
50 | .combineLatest(this.selectedUser$)
51 | .subscribe(([option, user]) => {
52 | if (self.operator === 'combineLatest') {
53 | console.log('combineLatest');
54 | this.selectedUser = user;
55 | this.selectedUserPosts = _.filter(this.ds.getPostsSync(), (p: any) => p.userId === this.selectedUser.id);
56 | }
57 | });
58 | }
59 |
60 |
61 | `;
--------------------------------------------------------------------------------
/src/app/operators/merge-map/merge-map.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/operators/merge-map/merge-map.component.css
--------------------------------------------------------------------------------
/src/app/operators/merge-map/merge-map.component.html:
--------------------------------------------------------------------------------
1 |
2 | Merge Map demo
3 |
4 |
5 |
6 | Example
7 |
8 |
9 | List the different companies for all users
10 |
11 |
12 |
13 |
14 | {{company.name}}
15 |
16 |
17 |
18 |
19 |
20 |
21 | About
22 |
23 |
24 |
25 |
26 |
27 | Sample Code
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/app/operators/merge-map/merge-map.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { MergeMapComponent } from './merge-map.component';
4 |
5 | describe('MergeMapComponent', () => {
6 | let component: MergeMapComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ MergeMapComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(MergeMapComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/operators/merge-map/merge-map.component.ts:
--------------------------------------------------------------------------------
1 | import { Observable } from 'rxjs/Observable';
2 | import { DataService } from './../../shared/data.service';
3 | import { Component, OnInit } from '@angular/core';
4 | import { sampleCode } from './sample-code';
5 |
6 | @Component({
7 | selector: 'app-merge-map',
8 | templateUrl: './merge-map.component.html',
9 | styleUrls: ['./merge-map.component.css']
10 | })
11 | export class MergeMapComponent implements OnInit {
12 |
13 | companies: any[] = [];
14 | sampleCode = sampleCode;
15 |
16 | constructor(public ds: DataService) { }
17 |
18 | ngOnInit() {
19 | this.ds.getUsers()
20 | .mergeMap(user => Observable.of(user.company))
21 | .subscribe(company => this.companies.push(company));
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/app/operators/merge-map/sample-code.ts:
--------------------------------------------------------------------------------
1 | export const sampleCode = `
2 |
3 |
4 | this.ds.getUsers()
5 | .mergeMap(user => Observable.of(user.company))
6 | .subscribe(company => this.companies.push(company));
7 |
8 | // getUsers()
9 | getUsers(): Observable {
10 | return Observable.from(MOCK_USERS);
11 | }
12 |
13 | // MOCK_USERS
14 | const MOCK_USERS = [
15 | {
16 | id: 1,
17 | name: 'Leanne Graham',
18 | username: 'Bret',
19 | email: 'Sincere@april.biz',
20 | address: {
21 | street: 'Kulas Light',
22 | suite: 'Apt. 556',
23 | city: 'Gwenborough',
24 | zipcode: '92998-3874',
25 | geo: {
26 | lat: '-37.3159',
27 | lng: '81.1496'
28 | }
29 | },
30 | phone: '1-770-736-8031 x56442',
31 | website: 'hildegard.org',
32 | company: {
33 | name: 'Romaguera-Crona',
34 | catchPhrase: 'Multi-layered client-server neural-net',
35 | bs: 'harness real-time e-markets'
36 | }
37 | },
38 | //...
39 |
40 |
41 | `;
42 |
--------------------------------------------------------------------------------
/src/app/operators/merge/merge.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/operators/merge/merge.component.css
--------------------------------------------------------------------------------
/src/app/operators/merge/merge.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Merge demo
4 |
5 |
6 |
7 | Example
8 |
9 |
10 |
12 |
14 |
15 |
16 | {{user.name}}
17 |
18 |
19 | Different streams may emit at different times.
20 |
21 | Restart
22 |
23 |
24 |
25 |
26 |
27 | About
28 |
29 |
30 |
31 |
32 |
33 | Sample Code
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/app/operators/merge/merge.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { MergeComponent } from './merge.component';
4 |
5 | describe('MergeComponent', () => {
6 | let component: MergeComponent;
7 | let fixture: ComponentFixture
;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ MergeComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(MergeComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/operators/merge/merge.component.ts:
--------------------------------------------------------------------------------
1 | import { Observable, Subject } from 'rxjs';
2 | import { DataService } from './../../shared/data.service';
3 | import { Component, OnInit } from '@angular/core';
4 | import * as _ from 'lodash';
5 | import { sampleCode } from './sample-code';
6 |
7 | @Component({
8 | selector: 'app-merge',
9 | templateUrl: './merge.component.html',
10 | styleUrls: ['./merge.component.css']
11 | })
12 | export class MergeComponent implements OnInit {
13 |
14 | // Example 1
15 | users = [];
16 | enterUser$: Subject = new Subject();
17 | leaveUser$: Subject = new Subject();
18 | sliderValue = 0;
19 | sliderDisabled = false;
20 | secondSliderValue = 0;
21 | secondSliderDisabled = false;
22 |
23 | source$: Observable;
24 |
25 | sampleCode = sampleCode;
26 |
27 | constructor(public service: DataService) { }
28 |
29 | ngOnInit() {
30 | this.source$ = Observable.merge(
31 | this.enterUser$.map(user => new UserEvent('Enter', user)),
32 | this.leaveUser$.map(user => new UserEvent('Leave', user))
33 | );
34 |
35 | this.source$.subscribe((event) => this.processUser(event));
36 |
37 | this.fireSubjects();
38 | }
39 |
40 | processUser(event: UserEvent) {
41 |
42 | if (event.type === 'Enter') {
43 | event.user.color = 'primary';
44 | console.log(event.user);
45 | this.users.push(event.user);
46 | } else {
47 | const user: any = _.find(this.users, (u: any) => u.id === event.user.id);
48 | if (user) {
49 | user.color = 'warn';
50 | }
51 | }
52 | }
53 |
54 | fireSubjects() {
55 | this.sliderValue = 0;
56 | this.sliderDisabled = false;
57 | this.secondSliderValue = 0;
58 | this.secondSliderDisabled = false;
59 |
60 | this.service.wsOnUser(1000)
61 | .subscribe(user => {
62 | this.enterUser$.next(user);
63 | this.sliderValue++;
64 | },
65 | error => console.log(error),
66 | () => this.sliderDisabled = true);
67 |
68 | Observable.timer(5000, 1000).take(5)
69 | .subscribe(() => {
70 | const userId = Math.floor((Math.random() * 9) + 1);
71 | const user = this.service.getUsersSync(userId);
72 | this.leaveUser$.next(user);
73 | this.secondSliderValue++;
74 | },
75 | error => console.log(error),
76 | () => this.secondSliderDisabled = true);
77 | }
78 |
79 | restart() {
80 | this.users = [];
81 | this.fireSubjects();
82 | }
83 |
84 | }
85 |
86 | class UserEvent {
87 | type: string;
88 | user: any;
89 |
90 | constructor(type, user) {
91 | this.type = type;
92 | this.user = user;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/app/operators/merge/sample-code.ts:
--------------------------------------------------------------------------------
1 | export const sampleCode = `
2 |
3 |
4 | enterUser$: Subject = new Subject();
5 | leaveUser$: Subject = new Subject();
6 |
7 | this.source$ = Observable.merge(
8 | this.enterUser$.map(user => new UserEvent('Enter', user)),
9 | this.leaveUser$.map(user => new UserEvent('Leave', user))
10 | );
11 |
12 | this.source$.subscribe((event) => this.processUser(event));
13 |
14 | // process user
15 | processUser(event: UserEvent) {
16 |
17 | if (event.type === 'Enter') {
18 | event.user.color = 'primary';
19 | console.log(event.user);
20 | this.users.push(event.user);
21 | } else {
22 | const user: any = _.find(this.users, (u: any) => u.id === event.user.id);
23 | if (user) {
24 | user.color = 'warn';
25 | }
26 | }
27 | }
28 |
29 |
30 | `;
31 |
--------------------------------------------------------------------------------
/src/app/operators/operators.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/operators/operators.component.css
--------------------------------------------------------------------------------
/src/app/operators/operators.component.html:
--------------------------------------------------------------------------------
1 |
2 | operators works!
3 |
4 |
--------------------------------------------------------------------------------
/src/app/operators/operators.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { OperatorsComponent } from './operators.component';
4 |
5 | describe('OperatorsComponent', () => {
6 | let component: OperatorsComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ OperatorsComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(OperatorsComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/operators/operators.component.ts:
--------------------------------------------------------------------------------
1 | import { createSubscriber } from 'app/shared/utils';
2 | import { Component, OnInit } from '@angular/core';
3 | import { Observable } from 'rxjs/Observable';
4 | import { Subject } from 'rxjs/Subject';
5 | import { BehaviorSubject } from 'rxjs/BehaviorSubject';
6 |
7 | @Component({
8 | selector: 'app-operators',
9 | templateUrl: './operators.component.html',
10 | styleUrls: ['./operators.component.css']
11 | })
12 | export class OperatorsComponent implements OnInit {
13 |
14 | constructor() { }
15 |
16 | ngOnInit() {
17 | // this.do();
18 | // this.finally();
19 | // this.filter();
20 | // this.startWith();
21 | // this.merge();
22 | // this.mergeMulti();
23 | // this.concat();
24 | // this.concatInterval();
25 | // this.concatMulti();
26 | // this.mergeMap();
27 | // this.mergeMapFrom();
28 | // this.reduce();
29 | // this.scan();
30 | // this.scanLast();
31 | // this.buffer();
32 | // this.bufferTime();
33 | // this.bufferObservable();
34 | // this.collectTillStop();
35 | // this.toArray();
36 | // this.firstLastTakeSkip();
37 | // this.skipTakeWhile();
38 | // this.skipTakeUntil();
39 | // this.arrayZipTest();
40 |
41 | /*
42 | const array = this.arrayMap([1, 2, 4], a => a * a);
43 | console.log(array);
44 |
45 | const albums = [
46 | { title: 'Album 1', tracks: [{ id: 1, title: 'Track 1' }, { id: 2, title: 'Track 2' }] },
47 | { title: 'Album 2', tracks: [{ id: 1, title: 'Track 3' }, { id: 2, title: 'Track 4' }] }
48 | ];
49 | const tracks = this.arrayMergeMap(albums, a => a.tracks);
50 | console.log(tracks);
51 |
52 | const values = [1, 2, 4, 5, 6];
53 | console.log(this.arrayReduce(values, (acc, i) => acc + i, 0));
54 | const max = this.arrayReduce(
55 | values,
56 | function (acc, value) {
57 | if (value > acc) {
58 | return value;
59 | }
60 | // return Math.max(acc, value);
61 | }, 0);
62 |
63 | console.log(max);
64 | */
65 |
66 | }
67 |
68 | do() {
69 | Observable.range(1, 10)
70 | .do(a => console.log(`From do ${a}`))
71 | .map(a => a * a)
72 | .subscribe(createSubscriber('do'));
73 | }
74 |
75 | finally() {
76 | Observable.range(1, 10)
77 | .finally(() => console.log(`Finally!!`))
78 | .subscribe(createSubscriber('finally'));
79 | }
80 |
81 | startWith() {
82 | Observable.interval(1000)
83 | .startWith(-4)
84 | .subscribe(createSubscriber('interval'));
85 | }
86 |
87 | filter() {
88 | Observable.range(1, 10)
89 | .filter(a => a < 5 || a > 7)
90 | .subscribe(createSubscriber('filter'));
91 | }
92 |
93 | merge() {
94 | Observable.interval(1000)
95 | .merge(Observable.interval(500))
96 | .take(15)
97 | .subscribe(createSubscriber('merge.one'))
98 | }
99 |
100 | mergeMulti() {
101 | Observable.merge(
102 | Observable.interval(1000).map(a => `${a} seconds`),
103 | Observable.interval(500).map(a => `${a} half seconds`)
104 | ).take(10)
105 | .subscribe(createSubscriber('merge-multi'));
106 | }
107 |
108 | concat() {
109 | Observable.range(1, 5)
110 | .concat(Observable.range(10, 3))
111 | .subscribe(createSubscriber('concat'));
112 | }
113 |
114 | concatInterval() {
115 | // Move on next after the previous completes..
116 | Observable.interval(1000).take(4)
117 | .concat(Observable.range(10, 3))
118 | .subscribe(createSubscriber('concat-interval'));
119 | }
120 |
121 | concatMulti() {
122 | Observable.concat(
123 | Observable.interval(1000).map(a => `${a} seconds`).take(3),
124 | Observable.interval(500).map(a => `${a} half seconds`).take(5)
125 | ).subscribe(createSubscriber('concat-multi'));
126 | }
127 |
128 | arrayMap(array, projection) {
129 | const returnArray = [];
130 | for (const item of array) {
131 | const projected = projection(item);
132 | returnArray.push(projected);
133 | }
134 |
135 | return returnArray;
136 | }
137 |
138 | arrayMergeMap(array, projection) {
139 | const returnArray = [];
140 | for (const item of array) {
141 | const projectedArray = projection(item);
142 | for (const projected of projectedArray) {
143 | returnArray.push(projected);
144 | }
145 | }
146 |
147 | return returnArray;
148 | }
149 |
150 | mergeMap() {
151 | Observable.range(2, 3)
152 | .mergeMap((i => Observable.timer(i * 2000).map(() => `After ${i * 2} seconds`)))
153 | .subscribe(createSubscriber('mergeMap'));
154 | }
155 |
156 | mergeMapFrom() {
157 | // mergeMap will wait for the promise to resolve
158 | Observable.fromPromise(this.getTracks())
159 | .mergeMap((tracks: any) => Observable.from(tracks))
160 | .subscribe(createSubscriber('tracks'));
161 | }
162 |
163 | getTracks() {
164 | return new Promise((resolve, reject) => {
165 | setTimeout(function () {
166 | resolve(['Track 1', 'Track 2', 'Track 3']);
167 | }, 1000);
168 | });
169 | }
170 |
171 | arrayReduce(array, accumulator, startValue) {
172 | let value = startValue;
173 | for (const item of array) {
174 | value = accumulator(value, item);
175 | }
176 |
177 | return value;
178 | }
179 |
180 | reduce() {
181 | // must complete before emit
182 | Observable.range(1, 10)
183 | .reduce((acc, value) => acc + value)
184 | .subscribe(createSubscriber('reduce'));
185 | }
186 |
187 | scan() {
188 | // produces values without waiting to complete
189 | Observable.range(1, 10)
190 | .merge(Observable.never())
191 | .scan((acc, value) => acc + value)
192 | .subscribe(createSubscriber('scan'));
193 | }
194 |
195 | scanLast() {
196 | Observable.range(1, 10)
197 | .map(i => i * i)
198 | .scan(([last], current) => [current, last], [])
199 | .subscribe(createSubscriber('scan-last'));
200 | }
201 |
202 | buffer() {
203 | Observable.range(1, 100)
204 | .bufferCount(25)
205 | .subscribe(createSubscriber('buffer'));
206 | }
207 |
208 | bufferTime() {
209 | Observable.interval(200)
210 | .bufferTime(2000)
211 | .subscribe(createSubscriber('buffer-time'));
212 | }
213 |
214 | bufferObservable() {
215 | Observable.interval(500)
216 | .buffer(Observable.interval(2000))
217 | .subscribe(createSubscriber('buffer-observable'));
218 | }
219 |
220 | collectTillStop() {
221 | const stopSubject$ = new Subject();
222 | Observable.interval(500)
223 | .buffer(stopSubject$)
224 | .subscribe(createSubscriber('collect'));
225 |
226 | setTimeout(function () {
227 | stopSubject$.next();
228 | stopSubject$.complete();
229 | }, 3000);
230 | }
231 |
232 | toArray() {
233 | Observable.range(1, 10)
234 | .toArray()
235 | .subscribe(createSubscriber('to-array'));
236 | }
237 |
238 | firstLastTakeSkip() {
239 | const simple$ = new Observable(observer => {
240 | observer.next(1);
241 | observer.next(2);
242 | observer.next(3);
243 | observer.next(4);
244 | observer.next(5);
245 | observer.complete();
246 | });
247 |
248 | simple$.first().subscribe(createSubscriber('first'));
249 | simple$.last().subscribe(createSubscriber('last'));
250 | simple$.take(2).subscribe(createSubscriber('take'));
251 | simple$.skip(2).subscribe(createSubscriber('skip'));
252 | simple$
253 | .skip(2)
254 | .take(3)
255 | .subscribe(createSubscriber('skip-take'));
256 | }
257 |
258 | skipTakeWhile() {
259 | Observable.interval(500)
260 | .skipWhile(i => i < 4)
261 | .takeWhile(i => i < 10)
262 | .subscribe(createSubscriber('skip-take-while'));
263 | }
264 |
265 | skipTakeUntil() {
266 | Observable.interval(500)
267 | .skipUntil(Observable.timer(2000))
268 | .takeUntil(Observable.timer(4000))
269 | .subscribe(createSubscriber('skip-take-until'));
270 | }
271 |
272 |
273 |
274 | }
275 |
--------------------------------------------------------------------------------
/src/app/operators/reduce/reduce.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/operators/reduce/reduce.component.css
--------------------------------------------------------------------------------
/src/app/operators/reduce/reduce.component.html:
--------------------------------------------------------------------------------
1 |
2 | Reduce demo
3 |
4 |
5 |
6 | Example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
{{product.title}}
14 | {{product.price| currency:'USD':true}}
15 |
16 |
17 |
18 |
19 |
20 | Find the lowest values among products
21 |
22 |
23 |
24 |
25 | About
26 |
27 |
28 |
29 |
30 |
31 | Sample Code
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/app/operators/reduce/reduce.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ReduceComponent } from './reduce.component';
4 |
5 | describe('ReduceComponent', () => {
6 | let component: ReduceComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ ReduceComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ReduceComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/operators/reduce/reduce.component.ts:
--------------------------------------------------------------------------------
1 | import { DataService } from './../../shared/data.service';
2 | import { Component, OnInit } from '@angular/core';
3 | import { sampleCode } from './sample-code';
4 | import * as _ from 'lodash';
5 |
6 | @Component({
7 | selector: 'app-reduce',
8 | templateUrl: './reduce.component.html',
9 | styleUrls: ['./reduce.component.css']
10 | })
11 | export class ReduceComponent implements OnInit {
12 |
13 | products: any[] = [];
14 |
15 | sampleCode = sampleCode;
16 |
17 | constructor(private ds: DataService) { }
18 |
19 | ngOnInit() {
20 | this.initProducts();
21 | this.getBestPrice();
22 | }
23 |
24 | initProducts() {
25 | this.ds.getProducts().subscribe(product => this.products.push(product));
26 | }
27 |
28 | getBestPrice() {
29 | this.ds.getProducts()
30 | .reduce((acc, value) => {
31 | if (acc.price < value.price) {
32 | return acc;
33 | } else {
34 | return value;
35 | }
36 | }).subscribe(selected =>
37 | _.find(this.products, p => p.id === selected.id).selected = true
38 | );
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/app/operators/reduce/sample-code.ts:
--------------------------------------------------------------------------------
1 | export const sampleCode = `
2 |
3 |
4 | this.ds.getProducts()
5 | .reduce((acc, value) => {
6 | if (acc.price < value.price) {
7 | return acc;
8 | } else {
9 | return value;
10 | }
11 | }).subscribe(selected =>
12 | _.find(this.products, p => p.id === selected.id).selected = true
13 | );
14 |
15 | getProducts() {
16 | return Observable.from(MOCK_PRODUCTS);
17 | }
18 |
19 |
20 | `;
21 |
--------------------------------------------------------------------------------
/src/app/operators/scan/sample-code.ts:
--------------------------------------------------------------------------------
1 | export const sampleCode = `
2 |
3 |
4 | likesSubject: Subject = new Subject();
5 | likesSubscription: Subscription;
6 |
7 | dislikesSubject: Subject = new Subject();
8 | dislikesSubscription: Subscription;
9 |
10 | _.cloneDeep(this.ds.getImagesSync()).forEach(image => this.album.images.push(image));
11 |
12 | this.likesSubscription = this.likesSubject
13 | .scan((acc, value) => acc + value)
14 | .subscribe(totalLikes => this.album.likes = totalLikes);
15 |
16 | this.dislikesSubscription = this.dislikesSubject
17 | .scan((acc, value) => acc + value)
18 | .subscribe(totalDislikes => this.album.dislikes = totalDislikes);
19 |
20 |
21 | `;
22 |
--------------------------------------------------------------------------------
/src/app/operators/scan/scan.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/operators/scan/scan.component.css
--------------------------------------------------------------------------------
/src/app/operators/scan/scan.component.html:
--------------------------------------------------------------------------------
1 |
2 | Scan operator demo
3 |
4 |
5 |
6 | Example
7 |
8 |
9 | {{album.title}} :
10 | {{album.likes}} Likes thumb_up
11 | {{album.dislikes}} Dislikes thumb_down
12 |
13 |
14 |
15 |
16 |
17 | {{image.title}}
18 | {{image.date | date:'medium'}}
19 |
20 |
21 |
22 |
23 | {{image.likes}} thumb_up
24 | {{image.dislikes}} thumb_down
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
Each time a like or a dislike is being emitted, the corresponding total value is calculated
34 |
35 |
36 |
37 | About
38 |
39 |
40 |
41 |
42 |
43 | Sample Code
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/src/app/operators/scan/scan.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ScanComponent } from './scan.component';
4 |
5 | describe('ScanComponent', () => {
6 | let component: ScanComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ ScanComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ScanComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/operators/scan/scan.component.ts:
--------------------------------------------------------------------------------
1 | import { Observable } from 'rxjs/Observable';
2 | import { Subject } from 'rxjs/Subject';
3 | import { Subscription } from 'rxjs/Subscription';
4 | import { DataService } from './../../shared/data.service';
5 | import { Component, OnInit, OnDestroy } from '@angular/core';
6 | import { sampleCode } from './sample-code';
7 | import * as _ from 'lodash';
8 |
9 | @Component({
10 | selector: 'app-scan',
11 | templateUrl: './scan.component.html',
12 | styleUrls: ['./scan.component.css']
13 | })
14 | export class ScanComponent implements OnInit, OnDestroy {
15 |
16 | album: any = {
17 | title: 'RxJS Album',
18 | likes: 0,
19 | dislikes: 0,
20 | images: []
21 | };
22 |
23 | likesSubject: Subject = new Subject();
24 | likesSubscription: Subscription;
25 |
26 | dislikesSubject: Subject = new Subject();
27 | dislikesSubscription: Subscription;
28 |
29 | sampleCode = sampleCode;
30 |
31 | constructor(private ds: DataService) { }
32 |
33 | ngOnInit() {
34 |
35 | _.cloneDeep(this.ds.getImagesSync()).forEach(image => this.album.images.push(image));
36 |
37 | this.likesSubscription = this.likesSubject
38 | .scan((acc, value) => acc + value)
39 | .subscribe(totalLikes => this.album.likes = totalLikes);
40 |
41 | this.dislikesSubscription = this.dislikesSubject
42 | .scan((acc, value) => acc + value)
43 | .subscribe(totalDislikes => this.album.dislikes = totalDislikes);
44 |
45 | this.start();
46 | }
47 |
48 | start() {
49 | const interval$ = Observable.interval(1000).take(10);
50 |
51 | interval$.subscribe(i => {
52 | const like: boolean = Math.floor((Math.random() * 2) + 1) === 1;
53 | const imageId = Math.floor((Math.random() * 3) + 1);
54 | const rate = Math.floor((Math.random() * 5) + 1);
55 | const image = _.find(this.album.images, (img: any) => img.id === imageId);
56 |
57 | if (like) {
58 | image.likes += rate;
59 | this.likesSubject.next(rate);
60 | } else {
61 | image.dislikes += rate;
62 | this.dislikesSubject.next(rate);
63 | }
64 | });
65 | }
66 |
67 | ngOnDestroy() {
68 | this.likesSubscription.unsubscribe();
69 | this.dislikesSubscription.unsubscribe();
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/app/operators/switch-map/sample-code.ts:
--------------------------------------------------------------------------------
1 | export const sampleCode = `
2 |
3 |
4 | sliderValue: 0;
5 | selectedUser$: Subject = new Subject();
6 | userPosts: { [key: number]: any[] } = {};
7 | loadedPosts: any[] = [];
8 | loading = false;
9 |
10 | ngOnInit() {
11 |
12 | this.selectedUser$
13 | .do(user => console.log(user))
14 | .map(u => u.id)
15 | .switchMap((id) => this.loadUserPosts(id))
16 | .subscribe((records) => {
17 | console.log(records);
18 | this.loading = false;
19 | this.loadedPosts = [...records];
20 | });
21 | }
22 |
23 | loadUserPosts = (userId: number): Observable => {
24 | const self = this;
25 | self.sliderValue = 0;
26 | this.loading = true;
27 |
28 | return Observable.timer(0, 1000).take(10)
29 | .do(item => self.sliderValue++)
30 | .filter(item => item === 9)
31 | .switchMap(() => self.ds.getUserPosts(userId));
32 | }
33 |
34 |
35 | `;
36 |
--------------------------------------------------------------------------------
/src/app/operators/switch-map/switch-map.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/operators/switch-map/switch-map.component.css
--------------------------------------------------------------------------------
/src/app/operators/switch-map/switch-map.component.html:
--------------------------------------------------------------------------------
1 |
2 | Switch Map operator demo
3 |
4 |
5 |
6 | Example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | {{user.name}}
15 |
16 |
17 |
18 |
19 |
20 | {{post.title}}
21 |
22 |
23 |
24 |
25 | 0">{{10 - sliderValue}} seconds before REST API response..
26 | If you change selected user before the API returns, switchMap will cancel the in-flight network request
27 |
28 |
29 |
30 |
31 | About
32 |
33 |
34 |
35 |
36 |
37 | Sample Code
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/app/operators/switch-map/switch-map.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { SwitchMapComponent } from './switch-map.component';
4 |
5 | describe('SwitchMapComponent', () => {
6 | let component: SwitchMapComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ SwitchMapComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(SwitchMapComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/operators/switch-map/switch-map.component.ts:
--------------------------------------------------------------------------------
1 | import { sampleCode } from './sample-code';
2 | import { Observable, Subject } from 'rxjs';
3 | import { DataService } from './../../shared/data.service';
4 | import { Component, OnInit } from '@angular/core';
5 |
6 | @Component({
7 | selector: 'app-switch-map',
8 | templateUrl: './switch-map.component.html',
9 | styleUrls: ['./switch-map.component.css']
10 | })
11 | export class SwitchMapComponent implements OnInit {
12 |
13 | sampleCode = sampleCode;
14 |
15 | sliderValue: 0;
16 | selectedUser$: Subject = new Subject();
17 | userPosts: { [key: number]: any[] } = {};
18 | loadedPosts: any[] = [];
19 | loading = false;
20 |
21 | constructor(public ds: DataService) { }
22 |
23 | ngOnInit() {
24 |
25 | this.selectedUser$
26 | .do(user => console.log(user))
27 | .map(u => u.id)
28 | .switchMap((id) => this.loadUserPosts(id))
29 | .subscribe((records) => {
30 | console.log(records);
31 | this.loading = false;
32 | this.loadedPosts = [...records];
33 | });
34 | }
35 |
36 | loadUserPosts = (userId: number): Observable => {
37 | const self = this;
38 | self.sliderValue = 0;
39 | this.loading = true;
40 |
41 | return Observable.timer(0, 1000).take(10)
42 | .do(item => self.sliderValue++)
43 | .filter(item => item === 9)
44 | .switchMap(() => self.ds.getUserPosts(userId));
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/app/shared/data.service.ts:
--------------------------------------------------------------------------------
1 | import { Observable } from 'rxjs';
2 | import { MOCK_USERS, MOCK_POSTS, MOCK_IMAGES, MOCK_PRODUCTS } from './data';
3 | import { Injectable } from '@angular/core';
4 | import * as _ from 'lodash';
5 |
6 | @Injectable()
7 | export class DataService {
8 |
9 | constructor() { }
10 |
11 | getUsers(): Observable {
12 | return Observable.from(MOCK_USERS);
13 | }
14 |
15 | getUsersSync(id?: number) {
16 | if (id === undefined) {
17 | return MOCK_USERS;
18 | } else {
19 | return _.find(MOCK_USERS, u => u.id === id);
20 | }
21 | }
22 |
23 | getPosts(size?: number): Observable {
24 | return Observable.from(MOCK_POSTS).take(4).toArray();
25 | }
26 |
27 | getPostsSync(id?: number) {
28 | if (id === undefined) {
29 | return MOCK_POSTS;
30 | } else {
31 | return _.find(MOCK_POSTS, p => p.id === id);
32 | }
33 | }
34 |
35 | getUserPosts(userId: number, delay: number = 0): Observable {
36 | return Observable.of(_.filter(MOCK_POSTS, p => p.userId === userId)).delay(delay);
37 | }
38 |
39 | // returns an observable of an array
40 | getAllPosts() {
41 | return Observable.of(MOCK_POSTS);
42 | }
43 |
44 | wsOnUser(delay, size?: number): Observable {
45 | return Observable
46 | .interval(delay).take(size === undefined ? MOCK_USERS.length : size)
47 | .map(i => MOCK_USERS[i]);
48 | }
49 |
50 | getImagesSync() {
51 | return MOCK_IMAGES;
52 | }
53 |
54 | getProducts() {
55 | return Observable.from(MOCK_PRODUCTS);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/app/shared/data.ts:
--------------------------------------------------------------------------------
1 | export const MOCK_USERS = [
2 | {
3 | id: 1,
4 | name: 'Leanne Graham',
5 | username: 'Bret',
6 | email: 'Sincere@april.biz',
7 | address: {
8 | street: 'Kulas Light',
9 | suite: 'Apt. 556',
10 | city: 'Gwenborough',
11 | zipcode: '92998-3874',
12 | geo: {
13 | lat: '-37.3159',
14 | lng: '81.1496'
15 | }
16 | },
17 | phone: '1-770-736-8031 x56442',
18 | website: 'hildegard.org',
19 | company: {
20 | name: 'Romaguera-Crona',
21 | catchPhrase: 'Multi-layered client-server neural-net',
22 | bs: 'harness real-time e-markets'
23 | }
24 | },
25 | {
26 | id: 2,
27 | name: 'Ervin Howell',
28 | username: 'Antonette',
29 | email: 'Shanna@melissa.tv',
30 | address: {
31 | street: 'Victor Plains',
32 | suite: 'Suite 879',
33 | city: 'Wisokyburgh',
34 | zipcode: '90566-7771',
35 | geo: {
36 | lat: '-43.9509',
37 | lng: '-34.4618'
38 | }
39 | },
40 | phone: '010-692-6593 x09125',
41 | website: 'anastasia.net',
42 | company: {
43 | name: 'Deckow-Crist',
44 | catchPhrase: 'Proactive didactic contingency',
45 | bs: 'synergize scalable supply-chains'
46 | }
47 | },
48 | {
49 | id: 3,
50 | name: 'Clementine Bauch',
51 | username: 'Samantha',
52 | email: 'Nathan@yesenia.net',
53 | address: {
54 | street: 'Douglas Extension',
55 | suite: 'Suite 847',
56 | city: 'McKenziehaven',
57 | zipcode: '59590-4157',
58 | geo: {
59 | lat: '-68.6102',
60 | lng: '-47.0653'
61 | }
62 | },
63 | phone: '1-463-123-4447',
64 | website: 'ramiro.info',
65 | company: {
66 | name: 'Romaguera-Jacobson',
67 | catchPhrase: 'Face to face bifurcated interface',
68 | bs: 'e-enable strategic applications'
69 | }
70 | },
71 | {
72 | id: 4,
73 | name: 'Patricia Lebsack',
74 | username: 'Karianne',
75 | email: 'Julianne.OConner@kory.org',
76 | address: {
77 | street: 'Hoeger Mall',
78 | suite: 'Apt. 692',
79 | city: 'South Elvis',
80 | zipcode: '53919-4257',
81 | geo: {
82 | lat: '29.4572',
83 | lng: '-164.2990'
84 | }
85 | },
86 | phone: '493-170-9623 x156',
87 | website: 'kale.biz',
88 | company: {
89 | name: 'Robel-Corkery',
90 | catchPhrase: 'Multi-tiered zero tolerance productivity',
91 | bs: 'transition cutting-edge web services'
92 | }
93 | },
94 | {
95 | id: 5,
96 | name: 'Chelsey Dietrich',
97 | username: 'Kamren',
98 | email: 'Lucio_Hettinger@annie.ca',
99 | address: {
100 | street: 'Skiles Walks',
101 | suite: 'Suite 351',
102 | city: 'Roscoeview',
103 | zipcode: '33263',
104 | geo: {
105 | lat: '-31.8129',
106 | lng: '62.5342'
107 | }
108 | },
109 | phone: '(254)954-1289',
110 | website: 'demarco.info',
111 | company: {
112 | name: 'Keebler LLC',
113 | catchPhrase: 'User-centric fault-tolerant solution',
114 | bs: 'revolutionize end-to-end systems'
115 | }
116 | },
117 | {
118 | id: 6,
119 | name: 'Mrs. Dennis Schulist',
120 | username: 'Leopoldo_Corkery',
121 | email: 'Karley_Dach@jasper.info',
122 | address: {
123 | street: 'Norberto Crossing',
124 | suite: 'Apt. 950',
125 | city: 'South Christy',
126 | zipcode: '23505-1337',
127 | geo: {
128 | lat: '-71.4197',
129 | lng: '71.7478'
130 | }
131 | },
132 | phone: '1-477-935-8478 x6430',
133 | website: 'ola.org',
134 | company: {
135 | name: 'Considine-Lockman',
136 | catchPhrase: 'Synchronised bottom-line interface',
137 | bs: 'e-enable innovative applications'
138 | }
139 | },
140 | {
141 | id: 7,
142 | name: 'Kurtis Weissnat',
143 | username: 'Elwyn.Skiles',
144 | email: 'Telly.Hoeger@billy.biz',
145 | address: {
146 | street: 'Rex Trail',
147 | suite: 'Suite 280',
148 | city: 'Howemouth',
149 | zipcode: '58804-1099',
150 | geo: {
151 | lat: '24.8918',
152 | lng: '21.8984'
153 | }
154 | },
155 | phone: '210.067.6132',
156 | website: 'elvis.io',
157 | company: {
158 | name: 'Johns Group',
159 | catchPhrase: 'Configurable multimedia task-force',
160 | bs: 'generate enterprise e-tailers'
161 | }
162 | },
163 | {
164 | id: 8,
165 | name: 'Nicholas Runolfsdottir V',
166 | username: 'Maxime_Nienow',
167 | email: 'Sherwood@rosamond.me',
168 | address: {
169 | street: 'Ellsworth Summit',
170 | suite: 'Suite 729',
171 | city: 'Aliyaview',
172 | zipcode: '45169',
173 | geo: {
174 | lat: '-14.3990',
175 | lng: '-120.7677'
176 | }
177 | },
178 | phone: '586.493.6943 x140',
179 | website: 'jacynthe.com',
180 | company: {
181 | name: 'Abernathy Group',
182 | catchPhrase: 'Implemented secondary concept',
183 | bs: 'e-enable extensible e-tailers'
184 | }
185 | },
186 | {
187 | id: 9,
188 | name: 'Glenna Reichert',
189 | username: 'Delphine',
190 | email: 'Chaim_McDermott@dana.io',
191 | address: {
192 | street: 'Dayna Park',
193 | suite: 'Suite 449',
194 | city: 'Bartholomebury',
195 | zipcode: '76495-3109',
196 | geo: {
197 | lat: '24.6463',
198 | lng: '-168.8889'
199 | }
200 | },
201 | phone: '(775)976-6794 x41206',
202 | website: 'conrad.com',
203 | company: {
204 | name: 'Yost and Sons',
205 | catchPhrase: 'Switchable contextually-based project',
206 | bs: 'aggregate real-time technologies'
207 | }
208 | },
209 | {
210 | id: 10,
211 | name: 'Clementina DuBuque',
212 | username: 'Moriah.Stanton',
213 | email: 'Rey.Padberg@karina.biz',
214 | address: {
215 | street: 'Kattie Turnpike',
216 | suite: 'Suite 198',
217 | city: 'Lebsackbury',
218 | zipcode: '31428-2261',
219 | geo: {
220 | lat: '-38.2386',
221 | lng: '57.2232'
222 | }
223 | },
224 | phone: '024-648-3804',
225 | website: 'ambrose.net',
226 | company: {
227 | name: 'Hoeger LLC',
228 | catchPhrase: 'Centralized empowering task-force',
229 | bs: 'target end-to-end models'
230 | }
231 | }
232 | ];
233 |
234 | export const MOCK_POSTS = [
235 | {
236 | userId: 1,
237 | id: 1,
238 | title: "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
239 | body: "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
240 | },
241 | {
242 | userId: 1,
243 | id: 2,
244 | title: "qui est esse",
245 | body: "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
246 | },
247 | {
248 | userId: 1,
249 | id: 3,
250 | title: "ea molestias quasi exercitationem repellat qui ipsa sit aut",
251 | body: 'et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut'
252 | },
253 | {
254 | userId: 1,
255 | id: 4,
256 | title: 'eum et est occaecati',
257 | body: 'ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit'
258 | },
259 | {
260 | userId: 1,
261 | id: 5,
262 | title: 'nesciunt quas odio',
263 | body: 'repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque'
264 | },
265 | {
266 | userId: 1,
267 | id: 6,
268 | title: 'dolorem eum magni eos aperiam quia',
269 | body: 'ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae'
270 | },
271 | {
272 | userId: 1,
273 | id: 7,
274 | title: 'magnam facilis autem',
275 | body: 'dolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas'
276 | },
277 | {
278 | userId: 1,
279 | id: 8,
280 | title: 'dolorem dolore est ipsam',
281 | body: 'dignissimos aperiam dolorem qui eum\nfacilis quibusdam animi sint suscipit qui sint possimus cum\nquaerat magni maiores excepturi\nipsam ut commodi dolor voluptatum modi aut vitae'
282 | },
283 | {
284 | userId: 1,
285 | id: 9,
286 | title: 'nesciunt iure omnis dolorem tempora et accusantium',
287 | body: 'consectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas'
288 | },
289 | {
290 | userId: 1,
291 | id: 10,
292 | title: 'optio molestias id quia eum',
293 | body: 'quo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error'
294 | },
295 | {
296 | userId: 2,
297 | id: 11,
298 | title: 'et ea vero quia laudantium autem',
299 | body: 'delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi'
300 | },
301 | {
302 | userId: 2,
303 | id: 12,
304 | title: 'in quibusdam tempore odit est dolorem',
305 | body: 'itaque id aut magnam\npraesentium quia et ea odit et ea voluptas et\nsapiente quia nihil amet occaecati quia id voluptatem\nincidunt ea est distinctio odio'
306 | },
307 | {
308 | userId: 2,
309 | id: 13,
310 | title: 'dolorum ut in voluptas mollitia et saepe quo animi',
311 | body: 'aut dicta possimus sint mollitia voluptas commodi quo doloremque\niste corrupti reiciendis voluptatem eius rerum\nsit cumque quod eligendi laborum minima\nperferendis recusandae assumenda consectetur porro architecto ipsum ipsam'
312 | },
313 | {
314 | userId: 2,
315 | id: 14,
316 | title: 'voluptatem eligendi optio',
317 | body: 'fuga et accusamus dolorum perferendis illo voluptas\nnon doloremque neque facere\nad qui dolorum molestiae beatae\nsed aut voluptas totam sit illum'
318 | },
319 | {
320 | userId: 2,
321 | id: 15,
322 | title: 'eveniet quod temporibus',
323 | body: 'reprehenderit quos placeat\nvelit minima officia dolores impedit repudiandae molestiae nam\nvoluptas recusandae quis delectus\nofficiis harum fugiat vitae'
324 | },
325 | {
326 | userId: 2,
327 | id: 16,
328 | title: 'sint suscipit perspiciatis velit dolorum rerum ipsa laboriosam odio',
329 | body: 'suscipit nam nisi quo aperiam aut\nasperiores eos fugit maiores voluptatibus quia\nvoluptatem quis ullam qui in alias quia est\nconsequatur magni mollitia accusamus ea nisi voluptate dicta'
330 | },
331 | {
332 | userId: 2,
333 | id: 17,
334 | title: 'fugit voluptas sed molestias voluptatem provident',
335 | body: 'eos voluptas et aut odit natus earum\naspernatur fuga molestiae ullam\ndeserunt ratione qui eos\nqui nihil ratione nemo velit ut aut id quo'
336 | },
337 | {
338 | userId: 2,
339 | id: 18,
340 | title: 'voluptate et itaque vero tempora molestiae',
341 | body: 'eveniet quo quis\nlaborum totam consequatur non dolor\nut et est repudiandae\nest voluptatem vel debitis et magnam'
342 | },
343 | {
344 | userId: 2,
345 | id: 19,
346 | title: 'adipisci placeat illum aut reiciendis qui',
347 | body: 'illum quis cupiditate provident sit magnam\nea sed aut omnis\nveniam maiores ullam consequatur atque\nadipisci quo iste expedita sit quos voluptas'
348 | },
349 | {
350 | userId: 2,
351 | id: 20,
352 | title: 'doloribus ad provident suscipit at',
353 | body: 'qui consequuntur ducimus possimus quisquam amet similique\nsuscipit porro ipsam amet\neos veritatis officiis exercitationem vel fugit aut necessitatibus totam\nomnis rerum consequatur expedita quidem cumque explicabo'
354 | },
355 | {
356 | userId: 3,
357 | id: 21,
358 | title: 'asperiores ea ipsam voluptatibus modi minima quia sint',
359 | body: 'repellat aliquid praesentium dolorem quo\nsed totam minus non itaque\nnihil labore molestiae sunt dolor eveniet hic recusandae veniam\ntempora et tenetur expedita sunt'
360 | },
361 | {
362 | userId: 3,
363 | id: 22,
364 | title: 'dolor sint quo a velit explicabo quia nam',
365 | body: 'eos qui et ipsum ipsam suscipit aut\nsed omnis non odio\nexpedita earum mollitia molestiae aut atque rem suscipit\nnam impedit esse'
366 | },
367 | {
368 | userId: 3,
369 | id: 23,
370 | title: 'maxime id vitae nihil numquam',
371 | body: 'veritatis unde neque eligendi\nquae quod architecto quo neque vitae\nest illo sit tempora doloremque fugit quod\net et vel beatae sequi ullam sed tenetur perspiciatis'
372 | },
373 | {
374 | userId: 3,
375 | id: 24,
376 | title: 'autem hic labore sunt dolores incidunt',
377 | body: 'enim et ex nulla\nomnis voluptas quia qui\nvoluptatem consequatur numquam aliquam sunt\ntotam recusandae id dignissimos aut sed asperiores deserunt'
378 | },
379 | {
380 | userId: 3,
381 | id: 25,
382 | title: 'rem alias distinctio quo quis',
383 | body: 'ullam consequatur ut\nomnis quis sit vel consequuntur\nipsa eligendi ipsum molestiae et omnis error nostrum\nmolestiae illo tempore quia et distinctio'
384 | },
385 | {
386 | userId: 3,
387 | id: 26,
388 | title: 'est et quae odit qui non',
389 | body: 'similique esse doloribus nihil accusamus\nomnis dolorem fuga consequuntur reprehenderit fugit recusandae temporibus\nperspiciatis cum ut laudantium\nomnis aut molestiae vel vero'
390 | },
391 | {
392 | userId: 3,
393 | id: 27,
394 | title: 'quasi id et eos tenetur aut quo autem',
395 | body: 'eum sed dolores ipsam sint possimus debitis occaecati\ndebitis qui qui et\nut placeat enim earum aut odit facilis\nconsequatur suscipit necessitatibus rerum sed inventore temporibus consequatur'
396 | },
397 | {
398 | userId: 3,
399 | id: 28,
400 | title: 'delectus ullam et corporis nulla voluptas sequi',
401 | body: 'non et quaerat ex quae ad maiores\nmaiores recusandae totam aut blanditiis mollitia quas illo\nut voluptatibus voluptatem\nsimilique nostrum eum'
402 | },
403 | {
404 | userId: 3,
405 | id: 29,
406 | title: 'iusto eius quod necessitatibus culpa ea',
407 | body: 'odit magnam ut saepe sed non qui\ntempora atque nihil\naccusamus illum doloribus illo dolor\neligendi repudiandae odit magni similique sed cum maiores'
408 | },
409 | {
410 | userId: 3,
411 | id: 30,
412 | title: 'a quo magni similique perferendis',
413 | body: 'alias dolor cumque\nimpedit blanditiis non eveniet odio maxime\nblanditiis amet eius quis tempora quia autem rem\na provident perspiciatis quia'
414 | },
415 | {
416 | userId: 4,
417 | id: 31,
418 | title: 'ullam ut quidem id aut vel consequuntur',
419 | body: 'debitis eius sed quibusdam non quis consectetur vitae\nimpedit ut qui consequatur sed aut in\nquidem sit nostrum et maiores adipisci atque\nquaerat voluptatem adipisci repudiandae'
420 | },
421 | {
422 | userId: 4,
423 | id: 32,
424 | title: 'doloremque illum aliquid sunt',
425 | body: 'deserunt eos nobis asperiores et hic\nest debitis repellat molestiae optio\nnihil ratione ut eos beatae quibusdam distinctio maiores\nearum voluptates et aut adipisci ea maiores voluptas maxime'
426 | },
427 | {
428 | userId: 4,
429 | id: 33,
430 | title: 'qui explicabo molestiae dolorem',
431 | body: 'rerum ut et numquam laborum odit est sit\nid qui sint in\nquasi tenetur tempore aperiam et quaerat qui in\nrerum officiis sequi cumque quod'
432 | },
433 | {
434 | userId: 4,
435 | id: 34,
436 | title: 'magnam ut rerum iure',
437 | body: 'ea velit perferendis earum ut voluptatem voluptate itaque iusto\ntotam pariatur in\nnemo voluptatem voluptatem autem magni tempora minima in\nest distinctio qui assumenda accusamus dignissimos officia nesciunt nobis'
438 | },
439 | {
440 | userId: 4,
441 | id: 35,
442 | title: 'id nihil consequatur molestias animi provident',
443 | body: 'nisi error delectus possimus ut eligendi vitae\nplaceat eos harum cupiditate facilis reprehenderit voluptatem beatae\nmodi ducimus quo illum voluptas eligendi\net nobis quia fugit'
444 | },
445 | {
446 | userId: 4,
447 | id: 36,
448 | title: 'fuga nam accusamus voluptas reiciendis itaque',
449 | body: 'ad mollitia et omnis minus architecto odit\nvoluptas doloremque maxime aut non ipsa qui alias veniam\nblanditiis culpa aut quia nihil cumque facere et occaecati\nqui aspernatur quia eaque ut aperiam inventore'
450 | },
451 | {
452 | userId: 4,
453 | id: 37,
454 | title: 'provident vel ut sit ratione est',
455 | body: 'debitis et eaque non officia sed nesciunt pariatur vel\nvoluptatem iste vero et ea\nnumquam aut expedita ipsum nulla in\nvoluptates omnis consequatur aut enim officiis in quam qui'
456 | },
457 | {
458 | userId: 4,
459 | id: 38,
460 | title: 'explicabo et eos deleniti nostrum ab id repellendus',
461 | body: 'animi esse sit aut sit nesciunt assumenda eum voluptas\nquia voluptatibus provident quia necessitatibus ea\nrerum repudiandae quia voluptatem delectus fugit aut id quia\nratione optio eos iusto veniam iure'
462 | },
463 | {
464 | userId: 4,
465 | id: 39,
466 | title: 'eos dolorem iste accusantium est eaque quam',
467 | body: 'corporis rerum ducimus vel eum accusantium\nmaxime aspernatur a porro possimus iste omnis\nest in deleniti asperiores fuga aut\nvoluptas sapiente vel dolore minus voluptatem incidunt ex'
468 | },
469 | {
470 | userId: 4,
471 | id: 40,
472 | title: 'enim quo cumque',
473 | body: 'ut voluptatum aliquid illo tenetur nemo sequi quo facilis\nipsum rem optio mollitia quas\nvoluptatem eum voluptas qui\nunde omnis voluptatem iure quasi maxime voluptas nam'
474 | },
475 | {
476 | userId: 5,
477 | id: 41,
478 | title: 'non est facere',
479 | body: 'molestias id nostrum\nexcepturi molestiae dolore omnis repellendus quaerat saepe\nconsectetur iste quaerat tenetur asperiores accusamus ex ut\nnam quidem est ducimus sunt debitis saepe'
480 | },
481 | {
482 | userId: 5,
483 | id: 42,
484 | title: 'commodi ullam sint et excepturi error explicabo praesentium voluptas',
485 | body: 'odio fugit voluptatum ducimus earum autem est incidunt voluptatem\nodit reiciendis aliquam sunt sequi nulla dolorem\nnon facere repellendus voluptates quia\nratione harum vitae ut'
486 | },
487 | {
488 | userId: 5,
489 | id: 43,
490 | title: 'eligendi iste nostrum consequuntur adipisci praesentium sit beatae perferendis',
491 | body: 'similique fugit est\nillum et dolorum harum et voluptate eaque quidem\nexercitationem quos nam commodi possimus cum odio nihil nulla\ndolorum exercitationem magnam ex et a et distinctio debitis'
492 | },
493 | {
494 | userId: 5,
495 | id: 44,
496 | title: 'optio dolor molestias sit',
497 | body: 'temporibus est consectetur dolore\net libero debitis vel velit laboriosam quia\nipsum quibusdam qui itaque fuga rem aut\nea et iure quam sed maxime ut distinctio quae'
498 | },
499 | {
500 | userId: 5,
501 | id: 45,
502 | title: 'ut numquam possimus omnis eius suscipit laudantium iure',
503 | body: 'est natus reiciendis nihil possimus aut provident\nex et dolor\nrepellat pariatur est\nnobis rerum repellendus dolorem autem'
504 | },
505 | {
506 | userId: 5,
507 | id: 46,
508 | title: 'aut quo modi neque nostrum ducimus',
509 | body: 'voluptatem quisquam iste\nvoluptatibus natus officiis facilis dolorem\nquis quas ipsam\nvel et voluptatum in aliquid'
510 | },
511 | {
512 | userId: 5,
513 | id: 47,
514 | title: 'quibusdam cumque rem aut deserunt',
515 | body: 'voluptatem assumenda ut qui ut cupiditate aut impedit veniam\noccaecati nemo illum voluptatem laudantium\nmolestiae beatae rerum ea iure soluta nostrum\neligendi et voluptate'
516 | },
517 | {
518 | userId: 5,
519 | id: 48,
520 | title: 'ut voluptatem illum ea doloribus itaque eos',
521 | body: 'voluptates quo voluptatem facilis iure occaecati\nvel assumenda rerum officia et\nillum perspiciatis ab deleniti\nlaudantium repellat ad ut et autem reprehenderit'
522 | },
523 | {
524 | userId: 5,
525 | id: 49,
526 | title: 'laborum non sunt aut ut assumenda perspiciatis voluptas',
527 | body: 'inventore ab sint\nnatus fugit id nulla sequi architecto nihil quaerat\neos tenetur in in eum veritatis non\nquibusdam officiis aspernatur cumque aut commodi aut'
528 | },
529 | {
530 | userId: 5,
531 | id: 50,
532 | title: 'repellendus qui recusandae incidunt voluptates tenetur qui omnis exercitationem',
533 | body: 'error suscipit maxime adipisci consequuntur recusandae\nvoluptas eligendi et est et voluptates\nquia distinctio ab amet quaerat molestiae et vitae\nadipisci impedit sequi nesciunt quis consectetur'
534 | },
535 | {
536 | userId: 6,
537 | id: 51,
538 | title: 'soluta aliquam aperiam consequatur illo quis voluptas',
539 | body: 'sunt dolores aut doloribus\ndolore doloribus voluptates tempora et\ndoloremque et quo\ncum asperiores sit consectetur dolorem'
540 | },
541 | {
542 | userId: 6,
543 | id: 52,
544 | title: 'qui enim et consequuntur quia animi quis voluptate quibusdam',
545 | body: 'iusto est quibusdam fuga quas quaerat molestias\na enim ut sit accusamus enim\ntemporibus iusto accusantium provident architecto\nsoluta esse reprehenderit qui laborum'
546 | },
547 | {
548 | userId: 6,
549 | id: 53,
550 | title: 'ut quo aut ducimus alias',
551 | body: 'minima harum praesentium eum rerum illo dolore\nquasi exercitationem rerum nam\nporro quis neque quo\nconsequatur minus dolor quidem veritatis sunt non explicabo similique'
552 | },
553 | {
554 | userId: 6,
555 | id: 54,
556 | title: 'sit asperiores ipsam eveniet odio non quia',
557 | body: 'totam corporis dignissimos\nvitae dolorem ut occaecati accusamus\nex velit deserunt\net exercitationem vero incidunt corrupti mollitia'
558 | },
559 | {
560 | userId: 6,
561 | id: 55,
562 | title: 'sit vel voluptatem et non libero',
563 | body: 'debitis excepturi ea perferendis harum libero optio\neos accusamus cum fuga ut sapiente repudiandae\net ut incidunt omnis molestiae\nnihil ut eum odit'
564 | },
565 | {
566 | userId: 6,
567 | id: 56,
568 | title: 'qui et at rerum necessitatibus',
569 | body: 'aut est omnis dolores\nneque rerum quod ea rerum velit pariatur beatae excepturi\net provident voluptas corrupti\ncorporis harum reprehenderit dolores eligendi'
570 | },
571 | {
572 | userId: 6,
573 | id: 57,
574 | title: 'sed ab est est',
575 | body: 'at pariatur consequuntur earum quidem\nquo est laudantium soluta voluptatem\nqui ullam et est\net cum voluptas voluptatum repellat est'
576 | },
577 | {
578 | userId: 6,
579 | id: 58,
580 | title: 'voluptatum itaque dolores nisi et quasi',
581 | body: 'veniam voluptatum quae adipisci id\net id quia eos ad et dolorem\naliquam quo nisi sunt eos impedit error\nad similique veniam'
582 | },
583 | {
584 | userId: 6,
585 | id: 59,
586 | title: 'qui commodi dolor at maiores et quis id accusantium',
587 | body: 'perspiciatis et quam ea autem temporibus non voluptatibus qui\nbeatae a earum officia nesciunt dolores suscipit voluptas et\nanimi doloribus cum rerum quas et magni\net hic ut ut commodi expedita sunt'
588 | },
589 | {
590 | userId: 6,
591 | id: 60,
592 | title: 'consequatur placeat omnis quisquam quia reprehenderit fugit veritatis facere',
593 | body: 'asperiores sunt ab assumenda cumque modi velit\nqui esse omnis\nvoluptate et fuga perferendis voluptas\nillo ratione amet aut et omnis'
594 | },
595 | {
596 | userId: 7,
597 | id: 61,
598 | title: 'voluptatem doloribus consectetur est ut ducimus',
599 | body: 'ab nemo optio odio\ndelectus tenetur corporis similique nobis repellendus rerum omnis facilis\nvero blanditiis debitis in nesciunt doloribus dicta dolores\nmagnam minus velit'
600 | },
601 | {
602 | userId: 7,
603 | id: 62,
604 | title: 'beatae enim quia vel',
605 | body: 'enim aspernatur illo distinctio quae praesentium\nbeatae alias amet delectus qui voluptate distinctio\nodit sint accusantium autem omnis\nquo molestiae omnis ea eveniet optio'
606 | },
607 | {
608 | userId: 7,
609 | id: 63,
610 | title: 'voluptas blanditiis repellendus animi ducimus error sapiente et suscipit',
611 | body: 'enim adipisci aspernatur nemo\nnumquam omnis facere dolorem dolor ex quis temporibus incidunt\nab delectus culpa quo reprehenderit blanditiis asperiores\naccusantium ut quam in voluptatibus voluptas ipsam dicta'
612 | },
613 | {
614 | userId: 7,
615 | id: 64,
616 | title: 'et fugit quas eum in in aperiam quod',
617 | body: 'id velit blanditiis\neum ea voluptatem\nmolestiae sint occaecati est eos perspiciatis\nincidunt a error provident eaque aut aut qui'
618 | },
619 | {
620 | userId: 7,
621 | id: 65,
622 | title: 'consequatur id enim sunt et et',
623 | body: 'voluptatibus ex esse\nsint explicabo est aliquid cumque adipisci fuga repellat labore\nmolestiae corrupti ex saepe at asperiores et perferendis\nnatus id esse incidunt pariatur'
624 | },
625 | {
626 | userId: 7,
627 | id: 66,
628 | title: 'repudiandae ea animi iusto',
629 | body: 'officia veritatis tenetur vero qui itaque\nsint non ratione\nsed et ut asperiores iusto eos molestiae nostrum\nveritatis quibusdam et nemo iusto saepe'
630 | },
631 | {
632 | userId: 7,
633 | id: 67,
634 | title: 'aliquid eos sed fuga est maxime repellendus',
635 | body: 'reprehenderit id nostrum\nvoluptas doloremque pariatur sint et accusantium quia quod aspernatur\net fugiat amet\nnon sapiente et consequatur necessitatibus molestiae'
636 | },
637 | {
638 | userId: 7,
639 | id: 68,
640 | title: 'odio quis facere architecto reiciendis optio',
641 | body: 'magnam molestiae perferendis quisquam\nqui cum reiciendis\nquaerat animi amet hic inventore\nea quia deleniti quidem saepe porro velit'
642 | },
643 | {
644 | userId: 7,
645 | id: 69,
646 | title: 'fugiat quod pariatur odit minima',
647 | body: 'officiis error culpa consequatur modi asperiores et\ndolorum assumenda voluptas et vel qui aut vel rerum\nvoluptatum quisquam perspiciatis quia rerum consequatur totam quas\nsequi commodi repudiandae asperiores et saepe a'
648 | },
649 | {
650 | userId: 7,
651 | id: 70,
652 | title: 'voluptatem laborum magni',
653 | body: 'sunt repellendus quae\nest asperiores aut deleniti esse accusamus repellendus quia aut\nquia dolorem unde\neum tempora esse dolore'
654 | },
655 | {
656 | userId: 8,
657 | id: 71,
658 | title: 'et iusto veniam et illum aut fuga',
659 | body: 'occaecati a doloribus\niste saepe consectetur placeat eum voluptate dolorem et\nqui quo quia voluptas\nrerum ut id enim velit est perferendis'
660 | },
661 | {
662 | userId: 8,
663 | id: 72,
664 | title: 'sint hic doloribus consequatur eos non id',
665 | body: 'quam occaecati qui deleniti consectetur\nconsequatur aut facere quas exercitationem aliquam hic voluptas\nneque id sunt ut aut accusamus\nsunt consectetur expedita inventore velit'
666 | },
667 | {
668 | userId: 8,
669 | id: 73,
670 | title: 'consequuntur deleniti eos quia temporibus ab aliquid at',
671 | body: 'voluptatem cumque tenetur consequatur expedita ipsum nemo quia explicabo\naut eum minima consequatur\ntempore cumque quae est et\net in consequuntur voluptatem voluptates aut'
672 | },
673 | {
674 | userId: 8,
675 | id: 74,
676 | title: 'enim unde ratione doloribus quas enim ut sit sapiente',
677 | body: 'odit qui et et necessitatibus sint veniam\nmollitia amet doloremque molestiae commodi similique magnam et quam\nblanditiis est itaque\nquo et tenetur ratione occaecati molestiae tempora'
678 | },
679 | {
680 | userId: 8,
681 | id: 75,
682 | title: 'dignissimos eum dolor ut enim et delectus in',
683 | body: 'commodi non non omnis et voluptas sit\nautem aut nobis magnam et sapiente voluptatem\net laborum repellat qui delectus facilis temporibus\nrerum amet et nemo voluptate expedita adipisci error dolorem'
684 | },
685 | {
686 | userId: 8,
687 | id: 76,
688 | title: 'doloremque officiis ad et non perferendis',
689 | body: 'ut animi facere\ntotam iusto tempore\nmolestiae eum aut et dolorem aperiam\nquaerat recusandae totam odio'
690 | },
691 | {
692 | userId: 8,
693 | id: 77,
694 | title: 'necessitatibus quasi exercitationem odio',
695 | body: 'modi ut in nulla repudiandae dolorum nostrum eos\naut consequatur omnis\nut incidunt est omnis iste et quam\nvoluptates sapiente aliquam asperiores nobis amet corrupti repudiandae provident'
696 | },
697 | {
698 | userId: 8,
699 | id: 78,
700 | title: 'quam voluptatibus rerum veritatis',
701 | body: 'nobis facilis odit tempore cupiditate quia\nassumenda doloribus rerum qui ea\nillum et qui totam\naut veniam repellendus'
702 | },
703 | {
704 | userId: 8,
705 | id: 79,
706 | title: 'pariatur consequatur quia magnam autem omnis non amet',
707 | body: 'libero accusantium et et facere incidunt sit dolorem\nnon excepturi qui quia sed laudantium\nquisquam molestiae ducimus est\nofficiis esse molestiae iste et quos'
708 | },
709 | {
710 | userId: 8,
711 | id: 80,
712 | title: 'labore in ex et explicabo corporis aut quas',
713 | body: 'ex quod dolorem ea eum iure qui provident amet\nquia qui facere excepturi et repudiandae\nasperiores molestias provident\nminus incidunt vero fugit rerum sint sunt excepturi provident'
714 | },
715 | {
716 | userId: 9,
717 | id: 81,
718 | title: 'tempora rem veritatis voluptas quo dolores vero',
719 | body: 'facere qui nesciunt est voluptatum voluptatem nisi\nsequi eligendi necessitatibus ea at rerum itaque\nharum non ratione velit laboriosam quis consequuntur\nex officiis minima doloremque voluptas ut aut'
720 | },
721 | {
722 | userId: 9,
723 | id: 82,
724 | title: 'laudantium voluptate suscipit sunt enim enim',
725 | body: 'ut libero sit aut totam inventore sunt\nporro sint qui sunt molestiae\nconsequatur cupiditate qui iste ducimus adipisci\ndolor enim assumenda soluta laboriosam amet iste delectus hic'
726 | },
727 | {
728 | userId: 9,
729 | id: 83,
730 | title: 'odit et voluptates doloribus alias odio et',
731 | body: 'est molestiae facilis quis tempora numquam nihil qui\nvoluptate sapiente consequatur est qui\nnecessitatibus autem aut ipsa aperiam modi dolore numquam\nreprehenderit eius rem quibusdam'
732 | },
733 | {
734 | userId: 9,
735 | id: 84,
736 | title: 'optio ipsam molestias necessitatibus occaecati facilis veritatis dolores aut',
737 | body: 'sint molestiae magni a et quos\neaque et quasi\nut rerum debitis similique veniam\nrecusandae dignissimos dolor incidunt consequatur odio'
738 | },
739 | {
740 | userId: 9,
741 | id: 85,
742 | title: 'dolore veritatis porro provident adipisci blanditiis et sunt',
743 | body: 'similique sed nisi voluptas iusto omnis\nmollitia et quo\nassumenda suscipit officia magnam sint sed tempora\nenim provident pariatur praesentium atque animi amet ratione'
744 | },
745 | {
746 | userId: 9,
747 | id: 86,
748 | title: 'placeat quia et porro iste',
749 | body: 'quasi excepturi consequatur iste autem temporibus sed molestiae beatae\net quaerat et esse ut\nvoluptatem occaecati et vel explicabo autem\nasperiores pariatur deserunt optio'
750 | },
751 | {
752 | userId: 9,
753 | id: 87,
754 | title: 'nostrum quis quasi placeat',
755 | body: 'eos et molestiae\nnesciunt ut a\ndolores perspiciatis repellendus repellat aliquid\nmagnam sint rem ipsum est'
756 | },
757 | {
758 | userId: 9,
759 | id: 88,
760 | title: 'sapiente omnis fugit eos',
761 | body: 'consequatur omnis est praesentium\nducimus non iste\nneque hic deserunt\nvoluptatibus veniam cum et rerum sed'
762 | },
763 | {
764 | userId: 9,
765 | id: 89,
766 | title: 'sint soluta et vel magnam aut ut sed qui',
767 | body: 'repellat aut aperiam totam temporibus autem et\narchitecto magnam ut\nconsequatur qui cupiditate rerum quia soluta dignissimos nihil iure\ntempore quas est'
768 | },
769 | {
770 | userId: 9,
771 | id: 90,
772 | title: 'ad iusto omnis odit dolor voluptatibus',
773 | body: 'minus omnis soluta quia\nqui sed adipisci voluptates illum ipsam voluptatem\neligendi officia ut in\neos soluta similique molestias praesentium blanditiis'
774 | },
775 | {
776 | userId: 10,
777 | id: 91,
778 | title: 'aut amet sed',
779 | body: 'libero voluptate eveniet aperiam sed\nsunt placeat suscipit molestias\nsimilique fugit nam natus\nexpedita consequatur consequatur dolores quia eos et placeat'
780 | },
781 | {
782 | userId: 10,
783 | id: 92,
784 | title: 'ratione ex tenetur perferendis',
785 | body: 'aut et excepturi dicta laudantium sint rerum nihil\nlaudantium et at\na neque minima officia et similique libero et\ncommodi voluptate qui'
786 | },
787 | {
788 | userId: 10,
789 | id: 93,
790 | title: 'beatae soluta recusandae',
791 | body: 'dolorem quibusdam ducimus consequuntur dicta aut quo laboriosam\nvoluptatem quis enim recusandae ut sed sunt\nnostrum est odit totam\nsit error sed sunt eveniet provident qui nulla'
792 | },
793 | {
794 | userId: 10,
795 | id: 94,
796 | title: 'qui qui voluptates illo iste minima',
797 | body: 'aspernatur expedita soluta quo ab ut similique\nexpedita dolores amet\nsed temporibus distinctio magnam saepe deleniti\nomnis facilis nam ipsum natus sint similique omnis'
798 | },
799 | {
800 | userId: 10,
801 | id: 95,
802 | title: 'id minus libero illum nam ad officiis',
803 | body: 'earum voluptatem facere provident blanditiis velit laboriosam\npariatur accusamus odio saepe\ncumque dolor qui a dicta ab doloribus consequatur omnis\ncorporis cupiditate eaque assumenda ad nesciunt'
804 | },
805 | {
806 | userId: 10,
807 | id: 96,
808 | title: 'quaerat velit veniam amet cupiditate aut numquam ut sequi',
809 | body: 'in non odio excepturi sint eum\nlabore voluptates vitae quia qui et\ninventore itaque rerum\nveniam non exercitationem delectus aut'
810 | },
811 | {
812 | userId: 10,
813 | id: 97,
814 | title: 'quas fugiat ut perspiciatis vero provident',
815 | body: 'eum non blanditiis soluta porro quibusdam voluptas\nvel voluptatem qui placeat dolores qui velit aut\nvel inventore aut cumque culpa explicabo aliquid at\nperspiciatis est et voluptatem dignissimos dolor itaque sit nam'
816 | },
817 | {
818 | userId: 10,
819 | id: 98,
820 | title: 'laboriosam dolor voluptates',
821 | body: 'doloremque ex facilis sit sint culpa\nsoluta assumenda eligendi non ut eius\nsequi ducimus vel quasi\nveritatis est dolores'
822 | },
823 | {
824 | userId: 10,
825 | id: 99,
826 | title: 'temporibus sit alias delectus eligendi possimus magni',
827 | body: 'quo deleniti praesentium dicta non quod\naut est molestias\nmolestias et officia quis nihil\nitaque dolorem quia'
828 | },
829 | {
830 | userId: 10,
831 | id: 100,
832 | title: 'at nam consequatur ea labore ea harum',
833 | body: 'cupiditate quo est a modi nesciunt soluta\nipsa voluptas error itaque dicta in\nautem qui minus magnam et distinctio eum\naccusamus ratione error aut'
834 | }
835 | ];
836 |
837 | export const MOCK_IMAGES = [
838 | {
839 | id: 1,
840 | src: '/assets/images/angular-2.jpg',
841 | title: 'Sample Angular 2 title',
842 | date: new Date(2017, 10, 4),
843 | likes: 0,
844 | dislikes: 0
845 | },
846 | {
847 | id: 2,
848 | src: 'assets/images/microsoft.jpg',
849 | title: 'Sample Microsoft title',
850 | date: new Date(2017, 2, 9),
851 | likes: 0,
852 | dislikes: 0
853 | },
854 | {
855 | id: 3,
856 | src: 'assets/images/reactive.jpg',
857 | title: 'Sample Reactive title',
858 | date: new Date(2016, 5, 14),
859 | likes: 0,
860 | dislikes: 0
861 | }
862 | ];
863 |
864 | export const MOCK_PRODUCTS = [
865 | {
866 | id: 1,
867 | image: '/assets/images/sonyxperiaxa1.jpg',
868 | title: 'Sony Xperia XA1',
869 | price: 444.22
870 | },
871 | {
872 | id: 2,
873 | image: 'assets/images/samsunggalaxya72017.jpg',
874 | title: 'Samsung Galaxy A7 2017',
875 | price: 510,
876 | },
877 | {
878 | id: 3,
879 | image: 'assets/images/motog4plus.jpg',
880 | title: 'Moto G4 Plus',
881 | price: 210
882 | },
883 | {
884 | id: 4,
885 | image: 'assets/images/samsunggalaxyj7prime.jpg',
886 | title: 'Samsung Galaxy J7 Prime',
887 | price: 188.90
888 | }
889 | ];
890 |
891 | export const MOCK_ALBUMS = [
892 | {
893 | id: 1, title: 'Away from the Sun', dateReleased: 'November 12, 2002', artist: '3 Doors Down',
894 | tracks: [
895 | { title: 'When I\'m Gone', duration: '4:21' },
896 | { title: 'Away from the Sun', duration: '3:53' },
897 | { title: 'The Road I\'m On', duration: '3:59' },
898 | { title: 'Ticket to Heaven', duration: '3:27' },
899 | { title: 'Running Out of Days', duration: '3:31' },
900 | { title: 'Here Without You', duration: '3:58' },
901 | { title: 'Dangerous Game', duration: '3:36' },
902 | { title: 'Changes', duration: '3:56' },
903 | ]
904 | },
905 | {
906 | id: 2, title: 'Seventeen Days', dateReleased: 'February 8, 2005', artist: '3 Doors Down',
907 | tracks: [
908 | { title: 'Right Where I Belong', duration: '2:32' },
909 | { title: 'It\'s Not Me', duration: '3:14' },
910 | { title: 'Let Me Go', duration: '3:52' },
911 | { title: 'Be Somebody', duration: '3:15' },
912 | { title: 'Landing in London', duration: '4:31' },
913 | { title: 'The Real Life', duration: '3:52' },
914 | { title: 'Never Will I Break', duration: '3:50' },
915 | { title: 'Father\'s Son', duration: '4:12' },
916 | ]
917 | }
918 | ];
919 |
920 |
--------------------------------------------------------------------------------
/src/app/shared/rxjs-operators.ts:
--------------------------------------------------------------------------------
1 | import "rxjs/add/observable/interval";
2 | import 'rxjs/add/observable/concat';
3 | import 'rxjs/add/observable/defer';
4 | import 'rxjs/add/observable/empty';
5 | import 'rxjs/add/observable/from';
6 | import 'rxjs/add/observable/fromEvent';
7 | import 'rxjs/add/observable/merge';
8 | import 'rxjs/add/observable/of';
9 | import 'rxjs/add/observable/timer';
10 | import 'rxjs/add/operator/concatMap';
11 | import 'rxjs/add/operator/do';
12 | import 'rxjs/add/operator/expand';
13 | import 'rxjs/add/operator/filter';
14 | import 'rxjs/add/operator/first';
15 | import 'rxjs/add/operator/let';
16 | import 'rxjs/add/operator/map';
17 | import 'rxjs/add/operator/mergeMap';
18 | import 'rxjs/add/operator/publish';
19 | import 'rxjs/add/operator/publishReplay';
20 | import 'rxjs/add/operator/reduce';
21 | import 'rxjs/add/operator/share';
22 | import 'rxjs/add/operator/switchMap';
23 | import 'rxjs/add/operator/take';
24 | import 'rxjs/add/operator/takeWhile';
25 | import 'rxjs/add/operator/scan';
26 | import 'rxjs/add/operator/skip';
27 | import 'rxjs/add/operator/startWith';
28 | import 'rxjs/add/operator/withLatestFrom';
29 | import 'rxjs/add/operator/combineLatest';
--------------------------------------------------------------------------------
/src/app/shared/utils.ts:
--------------------------------------------------------------------------------
1 | import { Observable } from 'rxjs';
2 |
3 | export function createInterval$(time) {
4 | return new Observable(observer => {
5 | let index = 0;
6 | const interval = setInterval(() => {
7 | console.log(`Generating ${index}`);
8 | observer.next(index++);
9 | }, time);
10 |
11 | return () => {
12 | clearInterval(interval);
13 | };
14 | });
15 | }
16 |
17 | export function createSubscriber(tag) {
18 | return {
19 | next(item) { console.log(`${tag}.next ${item}`); },
20 | error(error) { console.log(`${tag}.error ${error.stack || error}`); },
21 | complete() { console.log(`${tag}.complete`); }
22 | };
23 | }
24 |
25 | export function take$(sourceObservable$, amount) {
26 | return new Observable(observer => {
27 | let count = 0;
28 |
29 | const subscription = sourceObservable$.subscribe({
30 | next(item) {
31 | observer.next(item);
32 | if (++count >= amount) {
33 | observer.complete();
34 | }
35 | },
36 | error(error) { observer.error(error); },
37 | complete() { observer.complete(); }
38 | });
39 |
40 | return () => subscription.unsubscribe();
41 | });
42 | }
43 |
--------------------------------------------------------------------------------
/src/app/subjects/sample-code.ts:
--------------------------------------------------------------------------------
1 | export const sampleCode = `
2 |
3 |
4 | userStatus$ = new BehaviorSubject({ user: { isLoggedIn: false, name: '' } });
5 | isLoggedIn$ = this.userStatus$.map((u: any) => u.user);
6 |
7 | trackUser() {
8 | this.isLoggedIn$.subscribe(status => console.log(status));
9 | }
10 |
11 | signin(username, password) {
12 | this.userStatus$.next({ user: { isLoggedIn: true, name: username } });
13 | }
14 |
15 | signout() {
16 | this.userStatus$.next({ user: { isLoggedIn: false, name: '' } });
17 | }
18 |
19 |
20 | `;
21 |
--------------------------------------------------------------------------------
/src/app/subjects/subjects.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/app/subjects/subjects.component.css
--------------------------------------------------------------------------------
/src/app/subjects/subjects.component.html:
--------------------------------------------------------------------------------
1 |
2 | Subjects demo
3 |
4 |
5 |
6 | Example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Logout
17 |
18 |
19 |
20 |
21 |
22 |
41 |
42 |
43 | Subject act both as an Observable and an Observer
44 |
45 | Sign In
46 |
47 |
48 |
49 |
50 |
51 |
52 | About
53 |
54 |
55 |
56 |
57 |
58 | Sample Code
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/src/app/subjects/subjects.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { SubjectsComponent } from './subjects.component';
4 |
5 | describe('SubjectsComponent', () => {
6 | let component: SubjectsComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ SubjectsComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(SubjectsComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/subjects/subjects.component.ts:
--------------------------------------------------------------------------------
1 | import { sampleCode } from './../create-observables/sample-code';
2 | import { Component, OnInit, ElementRef } from '@angular/core';
3 | import { Observable } from 'rxjs/Observable';
4 | import { Subject } from 'rxjs/Subject';
5 | import { BehaviorSubject } from 'rxjs/BehaviorSubject';
6 | import { ReplaySubject } from 'rxjs/ReplaySubject';
7 | import { AsyncSubject } from 'rxjs/AsyncSubject';
8 |
9 | import { MatFormField } from '@angular/material';
10 |
11 | @Component({
12 | selector: 'app-subjects',
13 | templateUrl: './subjects.component.html',
14 | styleUrls: ['./subjects.component.css']
15 | })
16 | export class SubjectsComponent implements OnInit {
17 |
18 | userStatus$ = new BehaviorSubject({ user: { isLoggedIn: false, name: '' } });
19 | isLoggedIn$ = this.userStatus$.map((u: any) => u.user);
20 |
21 | sampleCode = sampleCode;
22 |
23 | constructor(private el: ElementRef) { }
24 |
25 | ngOnInit() {
26 | this.trackUser();
27 | }
28 |
29 | trackUser() {
30 | this.isLoggedIn$.subscribe(status => console.log(status));
31 | }
32 |
33 | signin(username, password) {
34 | this.userStatus$.next({ user: { isLoggedIn: true, name: username } });
35 | }
36 |
37 | signout() {
38 | this.userStatus$.next({ user: { isLoggedIn: false, name: '' } });
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/assets/images/angular-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/assets/images/angular-2.jpg
--------------------------------------------------------------------------------
/src/assets/images/microsoft.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/assets/images/microsoft.jpg
--------------------------------------------------------------------------------
/src/assets/images/motog4plus.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/assets/images/motog4plus.jpg
--------------------------------------------------------------------------------
/src/assets/images/reactive-store-angular-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/assets/images/reactive-store-angular-2.jpg
--------------------------------------------------------------------------------
/src/assets/images/reactive.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/assets/images/reactive.jpg
--------------------------------------------------------------------------------
/src/assets/images/reactiveX.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/assets/images/reactiveX.png
--------------------------------------------------------------------------------
/src/assets/images/reactivex-merge.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/assets/images/reactivex-merge.gif
--------------------------------------------------------------------------------
/src/assets/images/samsunggalaxya72017.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/assets/images/samsunggalaxya72017.jpg
--------------------------------------------------------------------------------
/src/assets/images/samsunggalaxyj7prime.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/assets/images/samsunggalaxyj7prime.jpg
--------------------------------------------------------------------------------
/src/assets/images/sonyxperiaxa1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/assets/images/sonyxperiaxa1.jpg
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `.angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false
8 | };
9 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/rxjs-in-angular/aa4563495d88542529559b6a7887dca8993a19b1/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | RxJS in Angular
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Loading...
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule);
12 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/set';
35 |
36 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
37 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
38 |
39 | /** IE10 and IE11 requires the following to support `@angular/animation`. */
40 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
41 |
42 |
43 | /** Evergreen browsers require these. **/
44 | import 'core-js/es6/reflect';
45 | import 'core-js/es7/reflect';
46 |
47 |
48 | /** ALL Firefox browsers require the following to support `@angular/animation`. **/
49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
50 |
51 |
52 |
53 | /***************************************************************************************************
54 | * Zone JS is required by Angular itself.
55 | */
56 | import 'zone.js/dist/zone'; // Included with Angular CLI.
57 |
58 |
59 |
60 | /***************************************************************************************************
61 | * APPLICATION IMPORTS
62 | */
63 |
64 | /**
65 | * Date, currency, decimal and percent pipes.
66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
67 | */
68 | // import 'intl'; // Run `npm install --save intl`.
69 |
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
3 | @import '~https://fonts.googleapis.com/icon?family=Material+Icons';
4 | @import '~@angular/material/prebuilt-themes/indigo-pink.css';
5 | @import './app/theme.css';
6 | .home-image {
7 | height: 80vh;
8 | background-size: cover;
9 | }
10 |
11 | .home-phrase.mat-grid-tile {
12 | text-align: center !important;
13 | background: #3f51b5 !important;
14 | color: #3f51b5;
15 | font-style: italic;
16 | font-size: xx-large;
17 | }
18 |
19 | .home-phrase.mat-grid-tile.phrase-highlight {
20 | text-align: center !important;
21 | background: #3f51b5 !important;
22 | color: whitesmoke;
23 | font-style: italic;
24 | font-size: xx-large;
25 | }
26 |
27 | .home-phrase.mat-grid-tile.phrase-highlight .flipper,
28 | .home-phrase.mat-grid-tile.phrase-highlight .flipper {
29 | transform: rotateY(180deg);
30 | }
31 |
32 | .home-link {
33 | margin: auto auto;
34 | color: #e6e8f6;
35 | font-size: 20px;
36 | text-decoration: none;
37 | }
38 |
39 | .card-container {
40 | display: flex;
41 | flex-flow: column nowrap;
42 | }
43 |
44 | .card-container .mat-card {
45 | margin: 0 16px 16px 0;
46 | }
47 |
48 | .card-container .card-item {
49 | padding: 10px;
50 | }
51 |
52 | .demo-card-container img {
53 | background-color: gray;
54 | }
55 |
56 | .no-padding {
57 | padding: 0px !important;
58 | }
59 |
60 | .padding-16 {
61 | padding: 16px !important;
62 | }
63 |
64 | .padding-left--16 {
65 | padding-left: 16px;
66 | }
67 |
68 | .margin-right--16 {
69 | margin-right: 16px !important;
70 | }
71 |
72 | .card-container .mat-list[dense] .mat-list-item,
73 | .mat-nav-list[dense] .mat-list-item {
74 | display: inline-block;
75 | background-color: #FF5722;
76 | margin: 2px;
77 | }
78 |
79 | #mouseEventCard {
80 | box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12);
81 | }
82 |
83 | .app-slider {
84 | width: 300px;
85 | }
86 |
87 | .card-post-item {
88 | display: inline-block;
89 | width: 50%;
90 | }
91 |
92 | .mat-select--150 {
93 | width: 150px;
94 | }
95 |
96 | .margin-4 {
97 | margin: 4px !important;
98 | }
99 |
100 | .margin-bottom-4 {
101 | margin-bottom: 4px !important;
102 | }
103 |
104 | .margin-right-8 {
105 | margin-right: 8px !important;
106 | }
107 |
108 | .align-center {
109 | text-align: center;
110 | }
111 |
112 | .align-right {
113 | text-align: right;
114 | }
115 |
116 | .align-left {
117 | text-align: left;
118 | }
119 |
120 | .posts-container mat-card {
121 | display: inline-block !important;
122 | margin: 4px;
123 | background: #3f51b5;
124 | color: #e6e8f6;
125 | }
126 |
127 | .inline-block--50 {
128 | display: inline-block;
129 | width: 50%;
130 | box-sizing: border-box;
131 | margin: 4px 0 4px -4px;
132 | padding: 10px;
133 | border: 1px solid #bdbdbd;
134 | }
135 |
136 | .full-width {
137 | width: 100%;
138 | }
139 |
140 | .mat-nav-list .mat-list-item {
141 | border-bottom: 2px solid #607D8B;
142 | }
143 |
144 | .tab-content {
145 | padding: 16px;
146 | }
147 |
148 | .padding-bottom-0 {
149 | padding-bottom: 0px !important;
150 | }
151 |
152 | .margin-bottom-0 {
153 | margin-bottom: 0px !important;
154 | }
155 |
156 | .documentation {
157 | background: #F0F0F0;
158 | padding: 1.5em;
159 | font-family: serif;
160 | }
161 |
162 | .product-item-selected {
163 | background: #ff5722 !important;
164 | }
165 |
166 | .product-item img {
167 | width: 100px;
168 | }
169 |
170 | .product-details {
171 | padding: 5px;
172 | }
173 |
174 | .example-icon {
175 | padding: 0 14px;
176 | }
177 |
178 | .example-spacer {
179 | flex: 1 1 auto;
180 | }
181 |
182 | .toolbar-link {
183 | margin: 10px 10px;
184 | color: #bec4e6;
185 | text-decoration: none;
186 | }
187 |
188 | .toolbar-link:hover {
189 | color: #ff5722;
190 | }
191 |
192 | .button-buffer {
193 | background: #3f51b5;
194 | }
195 |
196 | .button-buffer:hover {
197 | background: #ff5722;
198 | cursor: pointer;
199 | }
200 |
201 | .company {
202 | background: #f44336;
203 | font-weight: 700;
204 | }
205 |
206 |
207 | /* Flip */
208 |
209 |
210 | /* entire container, keeps perspective */
211 |
212 | .flip-container {
213 | perspective: 1000px;
214 | }
215 |
216 |
217 | /* flip speed goes here */
218 |
219 | .flipper {
220 | transition: 0.6s;
221 | transform-style: preserve-3d;
222 | position: relative;
223 | height: 100%;
224 | width: 100%;
225 | background: #303030;
226 | }
227 |
228 |
229 | /* hide back of pane during swap */
230 |
231 | .front,
232 | .back {
233 | backface-visibility: hidden;
234 | position: absolute;
235 | top: 0;
236 | left: 0;
237 | }
238 |
239 |
240 | /* front pane, placed above back */
241 |
242 | .front {
243 | z-index: 2;
244 | /* for firefox 31 */
245 | transform: rotateY(0deg);
246 | }
247 |
248 |
249 | /* back, initially hidden pane */
250 |
251 | .back {
252 | transform: rotateY(180deg);
253 | }
254 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/long-stack-trace-zone';
4 | import 'zone.js/dist/proxy.js';
5 | import 'zone.js/dist/sync-test';
6 | import 'zone.js/dist/jasmine-patch';
7 | import 'zone.js/dist/async-test';
8 | import 'zone.js/dist/fake-async-test';
9 | import { getTestBed } from '@angular/core/testing';
10 | import {
11 | BrowserDynamicTestingModule,
12 | platformBrowserDynamicTesting
13 | } from '@angular/platform-browser-dynamic/testing';
14 |
15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
16 | declare var __karma__: any;
17 | declare var require: any;
18 |
19 | // Prevent Karma from running prematurely.
20 | __karma__.loaded = function () {};
21 |
22 | // First, initialize the Angular testing environment.
23 | getTestBed().initTestEnvironment(
24 | BrowserDynamicTestingModule,
25 | platformBrowserDynamicTesting()
26 | );
27 | // Then we find all the tests.
28 | const context = require.context('./', true, /\.spec\.ts$/);
29 | // And load the modules.
30 | context.keys().map(context);
31 | // Finally, start Karma to run the tests.
32 | __karma__.start();
33 |
--------------------------------------------------------------------------------
/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "module": "es2015",
6 | "baseUrl": "",
7 | "types": []
8 | },
9 | "exclude": [
10 | "test.ts",
11 | "**/*.spec.ts"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "baseUrl": "",
8 | "types": [
9 | "jasmine",
10 | "node"
11 | ]
12 | },
13 | "files": [
14 | "test.ts"
15 | ],
16 | "include": [
17 | "**/*.spec.ts",
18 | "**/*.d.ts"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /* SystemJS module definition */
2 | declare var module: NodeModule;
3 | interface NodeModule {
4 | id: string;
5 | }
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist/out-tsc",
5 | "baseUrl": "src",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "moduleResolution": "node",
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "target": "es5",
12 | "typeRoots": [
13 | "node_modules/@types"
14 | ],
15 | "lib": [
16 | "es2016",
17 | "dom"
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "callable-types": true,
7 | "class-name": true,
8 | "comment-format": [
9 | true,
10 | "check-space"
11 | ],
12 | "curly": true,
13 | "eofline": true,
14 | "forin": true,
15 | "import-blacklist": [true, "rxjs"],
16 | "import-spacing": true,
17 | "indent": [
18 | true,
19 | "spaces"
20 | ],
21 | "interface-over-type-literal": true,
22 | "label-position": true,
23 | "max-line-length": [
24 | true,
25 | 140
26 | ],
27 | "member-access": false,
28 | "member-ordering": [
29 | true,
30 | "static-before-instance",
31 | "variables-before-functions"
32 | ],
33 | "no-arg": true,
34 | "no-bitwise": true,
35 | "no-console": [
36 | true,
37 | "debug",
38 | "info",
39 | "time",
40 | "timeEnd",
41 | "trace"
42 | ],
43 | "no-construct": true,
44 | "no-debugger": true,
45 | "no-duplicate-variable": true,
46 | "no-empty": false,
47 | "no-empty-interface": true,
48 | "no-eval": true,
49 | "no-inferrable-types": [true, "ignore-params"],
50 | "no-shadowed-variable": true,
51 | "no-string-literal": false,
52 | "no-string-throw": true,
53 | "no-switch-case-fall-through": true,
54 | "no-trailing-whitespace": true,
55 | "no-unused-expression": true,
56 | "no-use-before-declare": true,
57 | "no-var-keyword": true,
58 | "object-literal-sort-keys": false,
59 | "one-line": [
60 | true,
61 | "check-open-brace",
62 | "check-catch",
63 | "check-else",
64 | "check-whitespace"
65 | ],
66 | "prefer-const": true,
67 | "quotemark": [
68 | true,
69 | "single"
70 | ],
71 | "radix": true,
72 | "semicolon": [
73 | "always"
74 | ],
75 | "triple-equals": [
76 | true,
77 | "allow-null-check"
78 | ],
79 | "typedef-whitespace": [
80 | true,
81 | {
82 | "call-signature": "nospace",
83 | "index-signature": "nospace",
84 | "parameter": "nospace",
85 | "property-declaration": "nospace",
86 | "variable-declaration": "nospace"
87 | }
88 | ],
89 | "typeof-compare": true,
90 | "unified-signatures": true,
91 | "variable-name": false,
92 | "whitespace": [
93 | true,
94 | "check-branch",
95 | "check-decl",
96 | "check-operator",
97 | "check-separator",
98 | "check-type"
99 | ],
100 |
101 | "directive-selector": [true, "attribute", "app", "camelCase"],
102 | "component-selector": [true, "element", "app", "kebab-case"],
103 | "use-input-property-decorator": true,
104 | "use-output-property-decorator": true,
105 | "use-host-property-decorator": true,
106 | "no-input-rename": true,
107 | "no-output-rename": true,
108 | "use-life-cycle-interface": true,
109 | "use-pipe-transform-interface": true,
110 | "component-class-suffix": true,
111 | "directive-class-suffix": true,
112 | "no-access-missing-member": true,
113 | "templates-use-public": true,
114 | "invoke-injectable": true
115 | }
116 | }
117 |
--------------------------------------------------------------------------------