├── .vscode └── settings.json ├── README.md ├── angular.json ├── package.json └── src ├── app ├── app.component.css ├── app.component.html ├── app.component.ts ├── app.module.ts ├── todos │ ├── todo.service.ts │ ├── todo1.component.html │ ├── todo1.component.ts │ ├── todo2.component.html │ └── todo2.component.ts └── users │ └── user.service.ts ├── index.html ├── main.ts ├── polyfills.ts └── styles.css /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // "editor.theme": "vs-dark", 3 | /** 4 | * Render vertical lines at the specified columns. 5 | * Defaults to empty array. 6 | */ 7 | // "editor.rulers": [], 8 | /** 9 | * A string containing the word separators used when doing word navigation. 10 | * Defaults to `~!@#$%^&*()-=+[{]}\\|;:\'",.<>/? 11 | */ 12 | // "editor.wordSeparators": "`~!@#$%^&*()-=+[{]}\\\\|;:\\'\",.<>/?", 13 | /** 14 | * Enable Linux primary clipboard. 15 | * Defaults to true. 16 | */ 17 | // "editor.selectionClipboard": true, 18 | /** 19 | * Control the rendering of line numbers. 20 | * If it is a function, it will be invoked when rendering a line number and the return value will be rendered. 21 | * Otherwise, if it is a truey, line numbers will be rendered normally (equivalent of using an identity function). 22 | * Otherwise, line numbers will not be rendered. 23 | * Defaults to true. 24 | */ 25 | // "editor.lineNumbers": "on", 26 | /** 27 | * Should the corresponding line be selected when clicking on the line number? 28 | * Defaults to true. 29 | */ 30 | // "editor.selectOnLineNumbers": true, 31 | /** 32 | * Control the width of line numbers, by reserving horizontal space for rendering at least an amount of digits. 33 | * Defaults to 5. 34 | */ 35 | // "editor.lineNumbersMinChars": 5, 36 | /** 37 | * Enable the rendering of the glyph margin. 38 | * Defaults to true in vscode and to false in monaco-editor. 39 | */ 40 | // "editor.glyphMargin": true, 41 | /** 42 | * The width reserved for line decorations (in px). 43 | * Line decorations are placed between line numbers and the editor content. 44 | * You can pass in a string in the format floating point followed by "ch". e.g. 1.3ch. 45 | * Defaults to 10. 46 | */ 47 | // "editor.lineDecorationsWidth": 10, 48 | /** 49 | * When revealing the cursor, a virtual padding (px) is added to the cursor, turning it into a rectangle. 50 | * This virtual padding ensures that the cursor gets revealed before hitting the edge of the viewport. 51 | * Defaults to 30 (px). 52 | */ 53 | // "editor.revealHorizontalRightPadding": 30, 54 | /** 55 | * Render the editor selection with rounded borders. 56 | * Defaults to true. 57 | */ 58 | // "editor.roundedSelection": true, 59 | /** 60 | * Control the behavior and rendering of the minimap. 61 | */ 62 | // "editor.minimap": { 63 | // "enabled": false 64 | // }, 65 | /** 66 | * Display overflow widgets as `fixed`. 67 | * Defaults to `false`. 68 | */ 69 | // "editor.fixedOverflowWidgets": false, 70 | /** 71 | * The number of vertical lanes the overview ruler should render. 72 | * Defaults to 2. 73 | */ 74 | // "editor.overviewRulerLanes": 2, 75 | /** 76 | * Controls if a border should be drawn around the overview ruler. 77 | * Defaults to `true`. 78 | */ 79 | // "editor.overviewRulerBorder": true, 80 | /** 81 | * Control the cursor animation style, possible values are 'blink', 'smooth', 'phase', 'expand' and 'solid'. 82 | * Defaults to 'blink'. 83 | */ 84 | // "editor.cursorBlinking": "blink", 85 | /** 86 | * Zoom the font in the editor when using the mouse wheel in combination with holding Ctrl. 87 | * Defaults to false. 88 | */ 89 | // "editor.mouseWheelZoom": false, 90 | /** 91 | * Control the cursor style, either 'block' or 'line'. 92 | * Defaults to 'line'. 93 | */ 94 | // "editor.cursorStyle": "line", 95 | /** 96 | * Control the width of the cursor when cursorStyle is set to 'line' 97 | */ 98 | // "editor.cursorWidth": 1, 99 | /** 100 | * Enable font ligatures. 101 | * Defaults to false. 102 | */ 103 | // "editor.fontLigatures": false, 104 | /** 105 | * Disable the use of `will-change` for the editor margin and lines layers. 106 | * The usage of `will-change` acts as a hint for browsers to create an extra layer. 107 | * Defaults to false. 108 | */ 109 | // "editor.disableLayerHinting": false, 110 | /** 111 | * Disable the optimizations for monospace fonts. 112 | * Defaults to false. 113 | */ 114 | // "editor.disableMonospaceOptimizations": false, 115 | /** 116 | * Should the cursor be hidden in the overview ruler. 117 | * Defaults to false. 118 | */ 119 | // "editor.hideCursorInOverviewRuler": false, 120 | /** 121 | * Enable that scrolling can go one screen size after the last line. 122 | * Defaults to true. 123 | */ 124 | // "editor.scrollBeyondLastLine": true, 125 | /** 126 | * Enable that scrolling can go beyond the last column by a number of columns. 127 | * Defaults to 5. 128 | */ 129 | // "editor.scrollBeyondLastColumn": 5, 130 | /** 131 | * Enable that the editor animates scrolling to a position. 132 | * Defaults to false. 133 | */ 134 | // "editor.smoothScrolling": false, 135 | /** 136 | * Enable that the editor will install an interval to check if its container dom node size has changed. 137 | * Enabling this might have a severe performance impact. 138 | * Defaults to false. 139 | */ 140 | // "editor.automaticLayout": false, 141 | /** 142 | * Control the wrapping of the editor. 143 | * When `wordWrap` = "off", the lines will never wrap. 144 | * When `wordWrap` = "on", the lines will wrap at the viewport width. 145 | * When `wordWrap` = "wordWrapColumn", the lines will wrap at `wordWrapColumn`. 146 | * When `wordWrap` = "bounded", the lines will wrap at min(viewport width, wordWrapColumn). 147 | * Defaults to "off". 148 | */ 149 | // "editor.wordWrap": "on", 150 | /** 151 | * Control the wrapping of the editor. 152 | * When `wordWrap` = "off", the lines will never wrap. 153 | * When `wordWrap` = "on", the lines will wrap at the viewport width. 154 | * When `wordWrap` = "wordWrapColumn", the lines will wrap at `wordWrapColumn`. 155 | * When `wordWrap` = "bounded", the lines will wrap at min(viewport width, wordWrapColumn). 156 | * Defaults to 80. 157 | */ 158 | // "editor.wordWrapColumn": 0, 159 | /** 160 | * Force word wrapping when the text appears to be of a minified/generated file. 161 | * Defaults to true. 162 | */ 163 | // "editor.wordWrapMinified": true, 164 | /** 165 | * Control indentation of wrapped lines. Can be: 'none', 'same', 'indent' or 'deepIndent'. 166 | * Defaults to 'same' in vscode and to 'none' in monaco-editor. 167 | */ 168 | // "editor.wrappingIndent": "same", 169 | /** 170 | * Configure word wrapping characters. A break will be introduced before these characters. 171 | * Defaults to '{([+'. 172 | */ 173 | // "editor.wordWrapBreakBeforeCharacters": "{([+", 174 | /** 175 | * Configure word wrapping characters. A break will be introduced after these characters. 176 | * Defaults to ' \t})]?|&,;'. 177 | */ 178 | // "editor.wordWrapBreakAfterCharacters": " \t})]?|&,;", 179 | /** 180 | * Configure word wrapping characters. A break will be introduced after these characters only if no `wordWrapBreakBeforeCharacters` or `wordWrapBreakAfterCharacters` were found. 181 | * Defaults to '.'. 182 | */ 183 | // "editor.wordWrapBreakObtrusiveCharacters": ".", 184 | /** 185 | * Performance guard: Stop rendering a line after x characters. 186 | * Defaults to 10000. 187 | * Use -1 to never stop rendering 188 | */ 189 | // "editor.stopRenderingLineAfter": -1, 190 | /** 191 | * Configure the editor's hover. 192 | */ 193 | // "editor.hover": { 194 | /** 195 | * Enable the hover. 196 | * Defaults to true. 197 | */ 198 | // "enabled": true, 199 | /** 200 | * Delay for showing the hover. 201 | * Defaults to 300. 202 | */ 203 | // "delay": 300, 204 | /** 205 | * Is the hover sticky such that it can be clicked and its contents selected? 206 | * Defaults to true. 207 | */ 208 | // "sticky": true 209 | // }, 210 | /** 211 | * Enable detecting links and making them clickable. 212 | * Defaults to true. 213 | */ 214 | // "editor.links": true, 215 | /** 216 | * Enable inline color decorators and color picker rendering. 217 | */ 218 | // "editor.colorDecorators": true, 219 | /** 220 | * Enable custom contextmenu. 221 | * Defaults to true. 222 | */ 223 | // "editor.contextmenu": true, 224 | /** 225 | * A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events. 226 | * Defaults to 1. 227 | */ 228 | // "editor.mouseWheelScrollSensitivity": 1, 229 | /** 230 | * The modifier to be used to add multiple cursors with the mouse. 231 | * Defaults to 'alt' 232 | */ 233 | // "editor.multiCursorModifier": "alt", 234 | /** 235 | * Merge overlapping selections. 236 | * Defaults to true 237 | */ 238 | // "editor.multiCursorMergeOverlapping": true, 239 | /** 240 | * Configure the editor's accessibility support. 241 | * Defaults to 'auto'. It is best to leave this to 'auto'. 242 | */ 243 | // "editor.accessibilitySupport": "auto", 244 | /** 245 | * Suggest options. 246 | */ 247 | // "editor.suggest": { 248 | /** 249 | * Enable graceful matching. Defaults to true. 250 | */ 251 | // "filterGraceful": true, 252 | /** 253 | * Prevent quick suggestions when a snippet is active. Defaults to true. 254 | */ 255 | // "snippetsPreventQuickSuggestions": true 256 | // }, 257 | /** 258 | * Enable quick suggestions (shadow suggestions) 259 | * Defaults to true. 260 | */ 261 | // "editor.quickSuggestions": true, 262 | /** 263 | * Quick suggestions show delay (in ms) 264 | * Defaults to 500 (ms) 265 | */ 266 | // "editor.quickSuggestionsDelay": 500, 267 | /** 268 | * Parameter hint options. 269 | */ 270 | // "editor.parameterHints": true, 271 | /** 272 | * Render icons in suggestions box. 273 | * Defaults to true. 274 | */ 275 | // "editor.iconsInSuggestions": true, 276 | /** 277 | * Options for auto closing brackets. 278 | * Defaults to language defined behavior. 279 | */ 280 | // "editor.autoClosingBrackets": true, 281 | /** 282 | * Enable auto indentation adjustment. 283 | * Defaults to false. 284 | */ 285 | // "editor.autoIndent": false, 286 | /** 287 | * Enable format on type. 288 | * Defaults to false. 289 | */ 290 | // "editor.formatOnType": false, 291 | /** 292 | * Enable format on paste. 293 | * Defaults to false. 294 | */ 295 | // "editor.formatOnPaste": false, 296 | /** 297 | * Controls if the editor should allow to move selections via drag and drop. 298 | * Defaults to false. 299 | */ 300 | // "editor.dragAndDrop": false, 301 | /** 302 | * Enable the suggestion box to pop-up on trigger characters. 303 | * Defaults to true. 304 | */ 305 | // "editor.suggestOnTriggerCharacters": true, 306 | /** 307 | * Accept suggestions on ENTER. 308 | * Defaults to 'on'. 309 | */ 310 | // "editor.acceptSuggestionOnEnter": "on", 311 | /** 312 | * Accept suggestions on provider defined characters. 313 | * Defaults to true. 314 | */ 315 | // "editor.acceptSuggestionOnCommitCharacter": true, 316 | /** 317 | * Enable snippet suggestions. Default to 'true'. 318 | */ 319 | // "editor.snippetSuggestions": "top", 320 | /** 321 | * Copying without a selection copies the current line. 322 | */ 323 | // "editor.emptySelectionClipboard": true, 324 | /** 325 | * Enable word based suggestions. Defaults to 'true' 326 | */ 327 | // "editor.wordBasedSuggestions": true, 328 | /** 329 | * The history mode for suggestions. 330 | */ 331 | // "editor.suggestSelection": "recentlyUsedByPrefix", 332 | /** 333 | * Enable selection highlight. 334 | * Defaults to true. 335 | */ 336 | // "editor.selectionHighlight": true, 337 | /** 338 | * Enable semantic occurrences highlight. 339 | * Defaults to true. 340 | */ 341 | // "editor.occurrencesHighlight": true, 342 | /** 343 | * Show code lens 344 | * Defaults to true. 345 | */ 346 | // "editor.codeLens": true, 347 | /** 348 | * Control the behavior and rendering of the code action lightbulb. 349 | */ 350 | // "editor.lightbulb": { 351 | // "enabled": true 352 | // }, 353 | /** 354 | * Enable code folding 355 | * Defaults to true. 356 | */ 357 | // "editor.folding": true, 358 | /** 359 | * Selects the folding strategy. 'auto' uses the strategies contributed for the current document, 'indentation' uses the indentation based folding strategy. 360 | * Defaults to 'auto'. 361 | */ 362 | // "editor.foldingStrategy": "auto", 363 | /** 364 | * Controls whether the fold actions in the gutter stay always visible or hide unless the mouse is over the gutter. 365 | * Defaults to 'mouseover'. 366 | */ 367 | // "editor.showFoldingControls": "mouseover", 368 | /** 369 | * Enable highlighting of matching brackets. 370 | * Defaults to true. 371 | */ 372 | // "editor.matchBrackets": true, 373 | /** 374 | * Enable rendering of whitespace. 375 | * Defaults to none. 376 | */ 377 | // "editor.renderWhitespace": "none", 378 | /** 379 | * Enable rendering of control characters. 380 | * Defaults to false. 381 | */ 382 | // "editor.renderControlCharacters": false, 383 | /** 384 | * Enable rendering of indent guides. 385 | * Defaults to true. 386 | */ 387 | // "editor.renderIndentGuides": true, 388 | /** 389 | * Enable highlighting of the active indent guide. 390 | * Defaults to true. 391 | */ 392 | // "editor.highlightActiveIndentGuide": true, 393 | /** 394 | * Enable rendering of current line highlight. 395 | * Defaults to all. 396 | */ 397 | // "editor.renderLineHighlight": "all", 398 | /** 399 | * Inserting and deleting whitespace follows tab stops. 400 | */ 401 | // "editor.useTabStops": true, 402 | /** 403 | * The font family 404 | */ 405 | // "editor.fontFamily": "Fira Code, Menlo, Monaco, 'Courier New', monospace", 406 | /** 407 | * The font weight 408 | */ 409 | // "editor.fontWeight": "normal", 410 | /** 411 | * The font size 412 | */ 413 | // "editor.fontSize": 12, 414 | /** 415 | * The line height 416 | */ 417 | // "editor.lineHeight": 18, 418 | /** 419 | * The letter spacing 420 | */ 421 | // "editor.letterSpacing": 0, 422 | /** 423 | * Controls fading out of unused variables. 424 | */ 425 | // "editor.showUnused": true 426 | } 427 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-rxjs-child-deborahk 2 | 3 | Demonstrates several techniques for working with RxJS in child components. 4 | 5 | - Passing the Observable as an input property and using onPush change detection. 6 | - Passing the value of the Observable as an input property and using onPush change detection. 7 | 8 | [Edit on StackBlitz ⚡️](https://stackblitz.com/edit/angular-rxjs-child-deborahk) 9 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "demo": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/demo", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "src/tsconfig.app.json", 21 | "assets": [ 22 | "src/favicon.ico", 23 | "src/assets" 24 | ], 25 | "styles": [ 26 | "src/styles.css" 27 | ], 28 | "scripts": [] 29 | }, 30 | "configurations": { 31 | "production": { 32 | "fileReplacements": [ 33 | { 34 | "replace": "src/environments/environment.ts", 35 | "with": "src/environments/environment.prod.ts" 36 | } 37 | ], 38 | "optimization": true, 39 | "outputHashing": "all", 40 | "sourceMap": false, 41 | "extractCss": true, 42 | "namedChunks": false, 43 | "aot": true, 44 | "extractLicenses": true, 45 | "vendorChunk": false, 46 | "buildOptimizer": true 47 | } 48 | } 49 | }, 50 | "serve": { 51 | "builder": "@angular-devkit/build-angular:dev-server", 52 | "options": { 53 | "browserTarget": "demo:build" 54 | }, 55 | "configurations": { 56 | "production": { 57 | "browserTarget": "demo:build:production" 58 | } 59 | } 60 | }, 61 | "extract-i18n": { 62 | "builder": "@angular-devkit/build-angular:extract-i18n", 63 | "options": { 64 | "browserTarget": "demo:build" 65 | } 66 | }, 67 | "test": { 68 | "builder": "@angular-devkit/build-angular:karma", 69 | "options": { 70 | "main": "src/test.ts", 71 | "polyfills": "src/polyfills.ts", 72 | "tsConfig": "src/tsconfig.spec.json", 73 | "karmaConfig": "src/karma.conf.js", 74 | "styles": [ 75 | "styles.css" 76 | ], 77 | "scripts": [], 78 | "assets": [ 79 | "src/favicon.ico", 80 | "src/assets" 81 | ] 82 | } 83 | }, 84 | "lint": { 85 | "builder": "@angular-devkit/build-angular:tslint", 86 | "options": { 87 | "tsConfig": [ 88 | "src/tsconfig.app.json", 89 | "src/tsconfig.spec.json" 90 | ], 91 | "exclude": [ 92 | "**/node_modules/**" 93 | ] 94 | } 95 | } 96 | } 97 | } 98 | }, 99 | "defaultProject": "demo" 100 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "0.0.0", 4 | "private": true, 5 | "dependencies": { 6 | "@angular/common": "^8.0.0", 7 | "@angular/compiler": "^8.0.0", 8 | "@angular/core": "^8.0.0", 9 | "@angular/forms": "^8.0.0", 10 | "@angular/platform-browser": "^8.0.0", 11 | "@angular/platform-browser-dynamic": "^8.0.0", 12 | "@angular/router": "^8.0.0", 13 | "core-js": "2", 14 | "rxjs": "^6.5.2", 15 | "zone.js": "^0.9.1" 16 | }, 17 | "scripts": { 18 | "ng": "ng", 19 | "start": "ng serve", 20 | "build": "ng build", 21 | "test": "ng test", 22 | "lint": "ng lint", 23 | "e2e": "ng e2e" 24 | }, 25 | "devDependencies": { 26 | "@angular-devkit/build-angular": "~0.10.0", 27 | "@angular/cli": "~7.0.2", 28 | "@angular/compiler-cli": "~7.0.0", 29 | "@angular/language-service": "~7.0.0", 30 | "@types/node": "~8.9.4", 31 | "@types/jasmine": "~2.8.8", 32 | "@types/jasminewd2": "~2.0.3", 33 | "codelyzer": "~4.5.0", 34 | "jasmine-core": "~2.99.1", 35 | "jasmine-spec-reporter": "~4.2.1", 36 | "karma": "~3.0.0", 37 | "karma-chrome-launcher": "~2.2.0", 38 | "karma-coverage-istanbul-reporter": "~2.0.1", 39 | "karma-jasmine": "~1.1.2", 40 | "karma-jasmine-html-reporter": "^0.2.2", 41 | "protractor": "~5.4.0", 42 | "ts-node": "~7.0.0", 43 | "tslint": "~5.11.0", 44 | "typescript": "~3.1.1" 45 | } 46 | } -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | p { 2 | font-family: Lato; 3 | } -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 7 |
8 | 9 | 10 |
11 | 12 |
-------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { throwError, of, forkJoin, combineLatest, BehaviorSubject } from "rxjs"; 3 | import { catchError, tap, map, switchMap, filter, first } from "rxjs/operators"; 4 | 5 | import { UserService } from "./users/user.service"; 6 | import { TodoService } from "./todos/todo.service"; 7 | 8 | @Component({ 9 | selector: "my-app", 10 | templateUrl: "./app.component.html", 11 | styleUrls: ["./app.component.css"] 12 | }) 13 | export class AppComponent { 14 | // All of the users 15 | users$ = this.userService.users$; 16 | 17 | // TODOs for the user 18 | todos$ = this.todoService.todosForUser$; 19 | 20 | // Currently selected user 21 | selectedUserId: number; 22 | 23 | constructor( 24 | private userService: UserService, 25 | private todoService: TodoService 26 | ) {} 27 | 28 | onSelected(userId: number): void { 29 | this.selectedUserId = userId; 30 | this.userService.selectedUserChanged(userId); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { BrowserModule } from "@angular/platform-browser"; 3 | import { FormsModule } from "@angular/forms"; 4 | import { HttpClientModule } from "@angular/common/http"; 5 | 6 | import { AppComponent } from "./app.component"; 7 | import { Todo1Component } from "./todos/todo1.component"; 8 | import { Todo2Component } from "./todos/todo2.component"; 9 | 10 | @NgModule({ 11 | imports: [BrowserModule, FormsModule, HttpClientModule], 12 | declarations: [AppComponent, Todo1Component, Todo2Component], 13 | bootstrap: [AppComponent] 14 | }) 15 | export class AppModule {} 16 | -------------------------------------------------------------------------------- /src/app/todos/todo.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { HttpClient } from "@angular/common/http"; 3 | 4 | import { throwError, forkJoin } from "rxjs"; 5 | import { filter, switchMap, tap, shareReplay } from "rxjs/operators"; 6 | 7 | import { UserService } from "../users/user.service"; 8 | 9 | @Injectable({ 10 | providedIn: "root" 11 | }) 12 | export class TodoService { 13 | todoUrl = "https://jsonplaceholder.typicode.com/todos"; 14 | 15 | // Watches for changes to the selected user 16 | // Emits an item and reexecutes the pipeline every time a different user is selected 17 | // As part of the pipeline, it retrieves the todos for the user using the emitted userId as an argument 18 | todosForUser$ = this.userService.userSelectedAction$.pipe( 19 | // Handle the case of no selection 20 | filter(userId => Boolean(userId)), 21 | switchMap(userId => 22 | this.http.get(`${this.todoUrl}?userId=${userId}&completed=true`) 23 | ), 24 | shareReplay(1) 25 | ); 26 | 27 | constructor(private http: HttpClient, private userService: UserService) {} 28 | } 29 | 30 | export interface ToDo { 31 | userId: number; 32 | id: number; 33 | title: string; 34 | completed: boolean; 35 | } 36 | -------------------------------------------------------------------------------- /src/app/todos/todo1.component.html: -------------------------------------------------------------------------------- 1 |

Child 1: Passing Observable

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
User IdTitleCompleted?
{{ todo.userId }}{{ todo.title }}{{ todo.completed }}
-------------------------------------------------------------------------------- /src/app/todos/todo1.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ChangeDetectionStrategy } from '@angular/core'; 2 | 3 | import { Observable } from 'rxjs'; 4 | 5 | import { ToDo } from './todo.service'; 6 | 7 | @Component({ 8 | selector: 'appTodo1', 9 | templateUrl: './todo1.component.html', 10 | changeDetection: ChangeDetectionStrategy.OnPush 11 | }) 12 | export class Todo1Component { 13 | @Input() todos$: Observable; 14 | } 15 | -------------------------------------------------------------------------------- /src/app/todos/todo2.component.html: -------------------------------------------------------------------------------- 1 |

Child 2: Passing Emitted Value

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
User IdTitleCompleted?
{{ todo.userId }}{{ todo.title }}{{ todo.completed }}
-------------------------------------------------------------------------------- /src/app/todos/todo2.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ChangeDetectionStrategy } from "@angular/core"; 2 | 3 | import { Observable } from "rxjs"; 4 | 5 | import { ToDo } from "./todo.service"; 6 | 7 | @Component({ 8 | selector: "appTodo2", 9 | templateUrl: "./todo2.component.html", 10 | changeDetection: ChangeDetectionStrategy.OnPush 11 | }) 12 | export class Todo2Component { 13 | @Input() todos: ToDo[]; 14 | } 15 | -------------------------------------------------------------------------------- /src/app/users/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { HttpClient } from "@angular/common/http"; 3 | 4 | import { BehaviorSubject, throwError, forkJoin } from "rxjs"; 5 | import { catchError, filter, switchMap, map } from "rxjs/operators"; 6 | 7 | @Injectable({ 8 | providedIn: "root" 9 | }) 10 | export class UserService { 11 | userUrl = "https://jsonplaceholder.typicode.com/users"; 12 | 13 | // Action stream (one parameter) 14 | private userSelectedSubject = new BehaviorSubject(0); 15 | userSelectedAction$ = this.userSelectedSubject.asObservable(); 16 | 17 | // All Users 18 | users$ = this.http.get(this.userUrl) 19 | .pipe( 20 | // tap(data => console.log('users', JSON.stringify(data))), 21 | catchError(err => throwError("Error occurred")) 22 | ); 23 | 24 | constructor(private http: HttpClient) {} 25 | 26 | selectedUserChanged(userId: number): void { 27 | this.userSelectedSubject.next(userId); 28 | } 29 | } 30 | 31 | export interface User { 32 | id: number; 33 | name: string; 34 | username: string; 35 | email: string; 36 | website: string; 37 | } 38 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | loading -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import './polyfills'; 2 | 3 | import { enableProdMode } from '@angular/core'; 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 5 | 6 | import { AppModule } from './app/app.module'; 7 | 8 | platformBrowserDynamic().bootstrapModule(AppModule).then(ref => { 9 | // Ensure Angular destroys itself on hot reloads. 10 | if (window['ngRef']) { 11 | window['ngRef'].destroy(); 12 | } 13 | window['ngRef'] = ref; 14 | 15 | // Otherwise, log the boot error 16 | }).catch(err => console.error(err)); -------------------------------------------------------------------------------- /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 | * APPLICATION IMPORTS 61 | */ 62 | 63 | /** 64 | * Date, currency, decimal and percent pipes. 65 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 66 | */ 67 | // import 'intl'; // Run `npm install --save intl`. -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* Add application styles & imports to this file! */ --------------------------------------------------------------------------------