├── .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 | ![GitHub Logo](/src/assets/images/reactive-store-angular-2.jpg) 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 | ![GitHub Logo](/src/assets/images/reactivex-merge.gif) 28 | 29 | ## Follow chsakell's Blog 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 50 | 51 | 52 |
FacebookTwitter
Microsoft Web Application Development
45 | facebook 46 | 48 | twitter-small 49 |
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 | 14 |
15 | RxJS in Angular 16 | 17 | Blog cast 18 | Twitter volume_up 19 | Facebook favorite_border 20 | Github code 21 | 22 |
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 | 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 | 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 | 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 | 30 | 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 | 54 | 55 |
56 |
57 | 58 | 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 | 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 | 27 | 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 | 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 | 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 | 17 | 18 | 19 |

Different streams may emit at different times.

20 | 21 | 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 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | {{image.title}} 18 | {{image.date | date:'medium'}} 19 | 20 | 21 | 22 | 23 | 24 | 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 |

{{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 | 17 | 18 | 19 |
20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 33 | 38 | 39 |
29 | 30 | 31 | 32 | 34 | 35 | 36 | 37 |
40 |
41 |
42 | 43 |

Subject act both as an Observable and an Observer

44 | 45 | 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 | --------------------------------------------------------------------------------