Compare commits

...

62 Commits

Author SHA1 Message Date
ec6ff1155b feat(core): upgrade rxjs to 6.0.0-alpha.4 2018-03-14 12:38:02 -07:00
ed78457c38 build: temporarily disable tests that depend on angular-in-memory-web-api
angular-in-memory-web-api is not yet compatible with rxjs v6 and rxjs v6 backwards compatibility package is not yet ready to be used.
2018-03-14 12:38:02 -07:00
8e3b940461 build: temporarily disable offline_compiler_test.sh 2018-03-14 12:38:02 -07:00
804d8c89d4 build: temporarily increase payload size limit for cli-hello-world until we update cli to v6 2018-03-14 12:38:02 -07:00
ea1dd3a2fc ci: improve logging when running aio/examples e2e tests 2018-03-14 12:38:02 -07:00
a5e6c64f25 build: update to zone.js@0.8.20 2018-03-14 12:38:02 -07:00
57760c8d20 build: remove obsolete rollup-test 2018-03-14 12:38:02 -07:00
683066fef1 build(aio): temporarily use RxJS from root node_modules/ when using local packages 2018-03-14 12:38:02 -07:00
f5a98f41fe fix(core): remove core animation import symbols (#22692)
This patch removes the deprecated support for animation
symbol imports from @angular/core.

BREAKING CHANGE: it is no longer possible to import
animation-related functions from @angular/core. All
animation symbols must now be imported from @angular/animations.

PR Close #22692
2018-03-14 12:23:51 -07:00
c09bd67aee fix(ivy): fix views manipulation logic (#22656)
This commit fixes a bug that would result in views insert / remove
even if a view needed only refresh operation.

The crux of the bug was that we were looking for a view to update
only in the LContainer.nextIndex position. This is incorrect as a
view with a given block id could be present later in the views
array (this happens if we about to remove a view in the middle of
the views array).

The code in this fix searches for a view to update in the views array and
can remove views in the middle of the views collection. Previously we
would remove views at the end of the collection only.

PR Close #22656
2018-03-14 12:07:15 -07:00
9df13add5d docs(aio): add ng-japan 2018 to events (#22750)
ng-japan 2018 will be held at June 16 in Tokyo, Japan! 

https://ngjapan.org/en.html
PR Close #22750
2018-03-14 10:59:55 -07:00
049757b237 fix(aio): constrain error logging to improve reporting (#22713)
The `Logger.error()` method now only accepts a single `Error` parameter
and passes this through to the error handler.
This allows the error handler to serialize the error more accurately.

The various places that use `Logger.error()` have been updated.

See #21943#issuecomment-370230047

PR Close #22713
2018-03-14 10:52:11 -07:00
1f9734315d docs: testing - highlight dispatchEvent (#22726)
PR Close #22726
2018-03-14 10:21:41 -07:00
6f0dad1710 build(aio): render doc-gen issues in overview dump (#22675)
Related to #22138

PR Close #22675
2018-03-14 10:20:30 -07:00
37fedd001c feat(core): add task tracking to Testability (#16863)
Allow passing an optional timeout to Testability's whenStable(). If
specified, if Angular is not stable before the timeout is hit, the
done callback will be invoked with a list of pending macrotasks.

Also, allows an optional update callback, which will be invoked whenever
the set of pending macrotasks changes. If this callback returns true,
the timeout will be cancelled and the done callback will not be invoked.

If the optional parameters are not passed, whenStable() will work
as it did before, whether or not the task tracking zone spec is
available.

This change also migrates the Testability unit tests off the deprecated
AsyncTestCompleter.

PR Close #16863
2018-03-14 08:48:48 -07:00
b1365d1fa8 refactor(ivy): remove directiveRefresh instruction (#22745)
PR Close #22745
2018-03-13 23:29:21 -07:00
4ac606b419 docs(compiler): fix spelling errors (#22704)
PR Close #22704
2018-03-13 21:45:13 -07:00
51027d73cc fix(ivy): Update rollup rule to prevent inlining symbols in debug. (#22747)
The new rollup rule disables inlining symbols in debug mode. This makes 
it look as if there would be more symbols but in reality these are the
symbols which are no longer being inlined.
PR Close #22747
2018-03-13 19:58:30 -07:00
48636f3e85 build(aio): compute stability and deprecate @stable tag (#22674)
Closes #22635

PR Close #22674
2018-03-13 19:55:00 -07:00
bd9d4df735 refactor(ivy): remove inputsPropertyName (#22716)
Closes #22591

PR Close #22716
2018-03-13 13:26:15 -07:00
34e355a3b0 build(bazel): ng_package rxjs/operators rollup config (#22744)
PR Close #22744
2018-03-13 12:12:11 -07:00
58b94e6f5e feat(animations): expose element and params within transition matchers (#22693)
PR Close #22693
2018-03-13 09:42:24 -07:00
db56836425 feat: tree-shakeable providers API updates (#22655)
Rename @Injectable({scope -> providedIn}).

Instead of {providedIn: APP_ROOT_SCOPE}, accept {providedIn: 'root'}.
Also, {providedIn: null} implies the injectable should not be added
to any scope.

PR Close #22655
2018-03-13 09:28:05 -07:00
21e44c6ba9 ci: add alexeagle to pullapprove root group (#22724)
PR Close #22724
2018-03-12 16:23:58 -07:00
f5d75d8efd build: fix postinstall for windows (#22720)
PR Close #22720
2018-03-12 15:11:25 -07:00
6e00410e1c fix(compiler-cli): annotate Ivy fields as @nocollapse in closure mode (#22691)
Closure has a transformation which turns:

Service.ngInjectableDef = ...;

into:

Service$ngInjectableDef = ...;

This transformation obviously breaks Ivy in a major way. The solution is
to annotate the fields as @nocollapse. However, Typescript appears to ignore
synthetic comments added to a node during a transformation, so the "right"
way to add these comments doesn't work.

As an interim measure, a post-processing step just before the compiled JS is
written to disk appends the correct comments with a regular expression.

PR Close #22691
2018-03-12 14:34:22 -07:00
f95730b8e2 fix(ivy): elements properties should not be stringified (#22683)
PR Close #22683
2018-03-12 13:16:05 -07:00
cd58c0a6d9 build(aio): remove unwanted overview headings (#22681)
PR Close #22681
2018-03-12 11:23:47 -07:00
38fef1588d build(aio): move "see also" block to export-base template (#22681)
This makes it easier for all the API docs to display "see also" links
in a consitent manner.

PR Close #22681
2018-03-12 11:23:46 -07:00
3f70aba272 fix(compiler-cli): disableTypeScriptVersionCheck should be applied even for older tsc versions (#22669)
Previously the flag would only disable the check in the case we tried to use newer tsc version.

In g3 we sometimes take a while to update tsc, but as a prerequisite of that Angular needs to be
updated first. This change enables us to update Angular and use it in g3 while g3 is being update
to the required tsc. Of course extra care is required when this check is disabled, but since we
control everything in g3, it's on us to get this right.

I don't see any preexisting tests for this, and I'm not sure how to write them right now.
I filed https://github.com/angular/angular/issues/22699

PR Close #22669
2018-03-12 09:27:24 -07:00
eb6fb2d8f9 build: switch from uglify-js to uglify-es (#22669)
This enables us to minify ES2015 code.

PR Close #22669
2018-03-12 09:27:24 -07:00
c602563589 feat(compiler-cli): require node 8 as runtime engine (#22669)
This is not expected to be a breaking change for anyone who's on Node LTS (currently v8)
and aligns @angular/compilar-cli with @angular/cli's runtime requirements.

PR Close #22669
2018-03-12 09:27:23 -07:00
8449eb8d62 build: upgrade to TypeScript 2.7 (#22669)
Fixes: #21571

PR Close #22669
2018-03-12 09:27:23 -07:00
a225b48482 build: introduce a temporary patch to make node_modules/rxjs compile with typescript 2.7 (#22669)
This patch can be removed once we update to rxjs v6. See #22573.

PR Close #22669
2018-03-12 09:27:23 -07:00
129d1e0fb1 build: add postinstall-patches.js script suitable for postinstall patching of dependencies (#22669)
Because sometimes one has to do what one has to do...

PR Close #22669
2018-03-12 09:27:23 -07:00
aa7dba244b feat(ivy): support checkNoChanges (#22710)
PR Close #22710
2018-03-11 22:16:38 -07:00
0bf6fa5b32 fix(router): correct over-encoding of URL fragment (#22687)
Relates to: #10280 #22337

PR Close #22687
2018-03-11 22:15:01 -07:00
40315bef3d fix(compiler-cli): enableResourceInlining handles both styles and styleUrls (#22688)
When both are present, the inlined styles are appended to the end of the styles

PR Close #22688
2018-03-11 22:14:32 -07:00
123efba388 fix(compiler-cli): resolve resource URLs before loading them under enableResourceInlining (#22688)
Also turn on the feature for Bazel ng_module rules

PR Close #22688
2018-03-11 22:14:31 -07:00
fa451bcd19 feat(ivy): support markForCheck (#22690)
PR Close #22690
2018-03-09 20:29:05 -08:00
0d8deb0795 fix(compiler-cli): generate proper exports.* identifiers in cjs output (#22564)
When the compiler generates a reference to an exported variable in the
same file, it inserts a synthetic ts.Identifier node. In CommonJS
output, this synthetic node would not be properly rewritten with an
`exports.` prefix.

This change sets the TS original node property on the synthetic node
we generate, which ensures TS knows to rewrite it in CommonJS output.

PR Close #22564
2018-03-09 13:09:57 -08:00
e8326e600d fix: overloading a function doesn't generate all of the signatures (#22569)
PR Close #22569
2018-03-09 13:07:36 -08:00
b5be18f405 feat(compiler-cli): add resource inlining to ngc (#22615)
When angularCompilerOptions { enableResourceInlining: true }, we replace all templateUrl and styleUrls properties in @Component with template/styles

PR Close #22615
2018-03-09 09:15:12 -08:00
1e6cc42a01 test: migrate remaining public-api tests to Bazel (#22639)
We now create npm packages to cover all the public api assertions in tools/public_api_guard.
We no longer depend on ts-api-guardian from npm - it is now stale since the repository was archived.
There is no longer a gulp task to enforce or accept the public API, this is in CircleCI as part of running all bazel test targets.

PR Close #22639
2018-03-09 09:11:40 -08:00
b26a90567c feat(ivy): support attaching and detaching views from change detection (#22670)
PR Close #22670
2018-03-08 23:44:33 -08:00
b0b9ca3386 feat(ivy): produce Renderer2 back-patching and factories. (#22506)
Produces back-patch as described in the #22235 and referenced in #22480.

This just contains the compiler implementations and the corresponding unit
tests. Connecting the dots as described in #22480 will be in a follow on
change.

PR Close #22506
2018-03-08 22:39:07 -08:00
5412e10bcd docs(core): replace ancient live demos (#22665)
PR Close #22665
2018-03-08 16:43:14 -08:00
489fec1299 feat: update tslib to 1.9.0 (#22667)
BREAKING CHANGE: after this change, npm and yarn will issue incompatible peerDependencies warning

We don't expect this to actually break an application, but the application/library package.json
will need to be updated to provide tslib 1.9.0 or higher.

PR Close #22667
2018-03-08 16:42:34 -08:00
2fee5cc095 test(ivy): add injection canonical specs (#22595)
PR Close #22595
2018-03-08 12:09:39 -08:00
f13f4db9dc refactor(ivy): fix rebase error (#22661)
PR Close #22661
2018-03-08 11:37:42 -08:00
2ca77d80ec docs: refactor revert() and call to lifecylce hook, edit doc to changes (#22094)
PR Close #22094
2018-03-08 10:58:42 -08:00
73c203fda9 feat(ivy): support host attribute and property bindings (#22334)
PR Close #22334
2018-03-08 10:57:30 -08:00
c499c8f4db build: update angular-bot g3sync paths (#22637)
We already trimmed these paths from the g3sync

PR Close #22637
2018-03-08 10:55:15 -08:00
4c089c1d93 feat(ivy): support ChangeDetectorRef.detectChanges (#22614)
PR Close #22614
2018-03-07 21:08:25 -08:00
d485346d3c fix(ivy): lifecycle hooks should be queued for root component (#22614)
PR Close #22614
2018-03-07 21:08:25 -08:00
8407fcc979 ci: double our cores on CircleCI (#22641)
This should cut our build time in ~half, assuming it's widely parallel.
See
https://circleci.com/docs/2.0/configuration-reference/#resource_class

Also enable bazel repository caching, and store the external
repositories in the CircleCI cache for later builds.

PR Close #22641
2018-03-07 21:00:03 -08:00
f64ee15487 feat(ivy): implement pipes (#22254)
PR Close #22254
2018-03-07 20:58:48 -08:00
5d4fa7f0c8 test(ivy): add canonical examples of bindings on elements (#22403)
PR Close #22403
2018-03-07 20:55:49 -08:00
8fb34bcd43 refactor(forms): deprecate ngModel usage on same field as formControl (#22633)
Support for using the `ngModel` input property and `ngModelChange`
event with reactive form directives has been deprecated in
Angular v6 and will be removed in Angular v7.

Now deprecated:
```html
<input [formControl]="control" [(ngModel)]="value">
```

```ts
this.value = 'some value';
```

This has been deprecated for a few reasons. First, developers have
found this pattern confusing. It seems like the actual `ngModel`
directive is being used, but in fact it's an input/output property
named `ngModel` on the reactive form directive that simply approximates
(some of) its behavior. Specifically, it allows getting/setting the
value and intercepting value events. However, some of `ngModel`'s other
features - like delaying updates with`ngModelOptions` or exporting the
directive - simply don't work, which has understandably caused some
confusion.

In addition, this pattern mixes template-driven and reactive forms
strategies, which we generally don't recommend because it doesn't take
advantage of the full benefits of either strategy. Setting the value in
the template violates the template-agnostic principles behind reactive
forms, whereas adding a FormControl/FormGroup layer in the class removes
the convenience of defining forms in the template.

To update your code before v7, you'll want to decide whether to stick
with reactive form directives (and get/set values using reactive forms
patterns) or switch over to template-driven directives.

After (choice 1 - use reactive forms):

```html
<input [formControl]="control">
```

```ts
this.control.setValue('some value');
```

After (choice 2 - use template-driven forms):

```html
<input [(ngModel)]="value">
```

```ts
this.value = 'some value';
```

You can also choose to silence this warning by providing a config for
`ReactiveFormsModule` at import time:

```ts
imports: [
  ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'});
]
```

Alternatively, you can choose to surface a separate warning for each
instance of this pattern with a config value of `"always"`. This may
help to track down where in the code the pattern is being used as the
code is being updated.

Note: `warnOnNgModelWithFormControl` is set up as deprecated so that it
can be removed in v7 when it is no longer needed. This will not display
properly in API docs yet because dgeni doesn't yet support deprecating
properties in object literals, but we have an open issue to resolve the
discrepancy here: https://github.com/angular/angular/issues/22640.

PR Close #22633
2018-03-07 20:47:53 -08:00
6d1367d297 feat(ivy): provide sanitization methods which can be tree shaken (#22540)
By providing a top level sanitization methods (rather than service) the
compiler can generate calls into the methods only when needed. This makes
the methods tree shakable.

PR Close #22540
2018-03-07 18:24:07 -08:00
538f1d980f refactor(core): move sanitization into core (#22540)
This is in preparation of having Ivy have sanitization inline.

PR Close #22540
2018-03-07 18:24:06 -08:00
065bcc5aad docs: add HeroService to code tabs and fix headers (#22373)
PR Close #22373
2018-03-07 18:20:53 -08:00
549 changed files with 18122 additions and 4830 deletions

View File

@ -15,11 +15,16 @@ build --experimental_remote_spawn_cache --remote_rest_cache=http://localhost:764
# Prevent unstable environment variables from tainting cache keys # Prevent unstable environment variables from tainting cache keys
build --experimental_strict_action_env build --experimental_strict_action_env
# Save downloaded repositories such as the go toolchain
# This directory can then be included in the CircleCI cache
# It should save time running the first build
build --experimental_repository_cache=/home/circleci/bazel_repository_cache
# Workaround https://github.com/bazelbuild/bazel/issues/3645 # Workaround https://github.com/bazelbuild/bazel/issues/3645
# Bazel doesn't calculate the memory ceiling correctly when running under Docker. # Bazel doesn't calculate the memory ceiling correctly when running under Docker.
# Limit Bazel to consuming resources that fit in CircleCI "medium" class which is the default: # Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
# https://circleci.com/docs/2.0/configuration-reference/#resource_class # https://circleci.com/docs/2.0/configuration-reference/#resource_class
build --local_resources=3072,2.0,1.0 build --local_resources=14336,8.0,1.0
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309 # Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
test --flaky_test_attempts=2 test --flaky_test_attempts=2

View File

@ -13,7 +13,7 @@
# If you change the `docker_image` version, also change the `cache_key` suffix and the version of # If you change the `docker_image` version, also change the `cache_key` suffix and the version of
# `com_github_bazelbuild_buildtools` in the `/WORKSPACE` file. # `com_github_bazelbuild_buildtools` in the `/WORKSPACE` file.
var_1: &docker_image angular/ngcontainer:0.1.0 var_1: &docker_image angular/ngcontainer:0.1.0
var_2: &cache_key angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.1.0 var_2: &cache_key v2-angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.1.0
# See remote cache documentation in /docs/BAZEL.md # See remote cache documentation in /docs/BAZEL.md
var_3: &setup-bazel-remote-cache var_3: &setup-bazel-remote-cache
@ -59,7 +59,7 @@ jobs:
build: build:
<<: *job_defaults <<: *job_defaults
resource_class: large resource_class: xlarge
steps: steps:
- checkout: - checkout:
<<: *post_checkout <<: *post_checkout
@ -71,6 +71,7 @@ jobs:
- restore_cache: - restore_cache:
key: *cache_key key: *cache_key
- run: ls /home/circleci/bazel_repository_cache || true
- run: bazel info release - run: bazel info release
- run: bazel run @yarn//:yarn - run: bazel run @yarn//:yarn
# Use bazel query so that we explicitly ask for all buildable targets to be built as well # Use bazel query so that we explicitly ask for all buildable targets to be built as well
@ -91,6 +92,7 @@ jobs:
key: *cache_key key: *cache_key
paths: paths:
- "node_modules" - "node_modules"
- "~/bazel_repository_cache"
workflows: workflows:
version: 2 version: 2

View File

@ -26,9 +26,7 @@ merge:
# list of patterns to check for the files changed by the PR # list of patterns to check for the files changed by the PR
# this list must be manually kept in sync with google3/third_party/javascript/angular2/copy.bara.sky # this list must be manually kept in sync with google3/third_party/javascript/angular2/copy.bara.sky
include: include:
- "BUILD.bazel"
- "LICENSE" - "LICENSE"
- "WORKSPACE"
- "modules/**" - "modules/**"
- "packages/**" - "packages/**"
# list of patterns to ignore for the files changed by the PR # list of patterns to ignore for the files changed by the PR
@ -36,6 +34,11 @@ merge:
- "packages/language-service/**" - "packages/language-service/**"
- "**/.gitignore" - "**/.gitignore"
- "**/.gitkeep" - "**/.gitkeep"
- "**/tsconfig-build.json"
- "**/tsconfig.json"
- "**/rollup.config.js"
- "**/BUILD.bazel"
- "packages/**/test/**"
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable # comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges. mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.

View File

@ -68,6 +68,7 @@ groups:
- "packages/*" - "packages/*"
- "tools/*" - "tools/*"
users: users:
- alexeagle
- IgorMinar - IgorMinar
- mhevery - mhevery

View File

@ -57,6 +57,7 @@ filegroup(
"//:node_modules/zone.js/dist/async-test.js", "//:node_modules/zone.js/dist/async-test.js",
"//:node_modules/zone.js/dist/sync-test.js", "//:node_modules/zone.js/dist/sync-test.js",
"//:node_modules/zone.js/dist/fake-async-test.js", "//:node_modules/zone.js/dist/fake-async-test.js",
"//:node_modules/zone.js/dist/task-tracking.js",
"//:node_modules/zone.js/dist/proxy.js", "//:node_modules/zone.js/dist/proxy.js",
"//:node_modules/zone.js/dist/jasmine-patch.js", "//:node_modules/zone.js/dist/jasmine-patch.js",
], ],

View File

@ -2,9 +2,9 @@ workspace(name = "angular")
http_archive( http_archive(
name = "build_bazel_rules_nodejs", name = "build_bazel_rules_nodejs",
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.5.1.zip", url = "https://github.com/bazelbuild/rules_nodejs/archive/f03c8b5df155da2a640b6775afdd4fe4aa6fec72.zip",
strip_prefix = "rules_nodejs-0.5.1", strip_prefix = "rules_nodejs-f03c8b5df155da2a640b6775afdd4fe4aa6fec72",
sha256 = "dabd1a596a6f00939875762dcb1de93b5ada0515069244198aa6792bc37bb92a", sha256 = "9d541f49af8cf60c73efb102186bfa5670ee190a088ce52638dcdf90cd9e2de6",
) )
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories") load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories")

View File

@ -2,7 +2,7 @@
import { Component, Input, OnDestroy } from '@angular/core'; import { Component, Input, OnDestroy } from '@angular/core';
import { MissionService } from './mission.service'; import { MissionService } from './mission.service';
import { Subscription } from 'rxjs/Subscription'; import { Subscription } from 'rxjs';
@Component({ @Component({
selector: 'app-astronaut', selector: 'app-astronaut',

View File

@ -1,6 +1,6 @@
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject'; import { Subject } from 'rxjs';
@Injectable() @Injectable()
export class MissionService { export class MissionService {

View File

@ -1,6 +1,6 @@
// #docregion // #docregion
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { Hero, HeroTaxReturn } from './hero'; import { Hero, HeroTaxReturn } from './hero';
import { HeroesService } from './heroes.service'; import { HeroesService } from './heroes.service';

View File

@ -1,7 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable, Observer } from 'rxjs';
import { Observer } from 'rxjs/Observer';
import { Hero, HeroTaxReturn } from './hero'; import { Hero, HeroTaxReturn } from './hero';

View File

@ -1,6 +1,6 @@
// #docregion // #docregion
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { Villain, VillainsService } from './villains.service'; import { Villain, VillainsService } from './villains.service';

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { of } from 'rxjs/observable/of'; import { of } from 'rxjs';
export interface Villain { id: number; name: string; } export interface Villain { id: number; name: string; }

View File

@ -28,7 +28,10 @@ let checkLogForMessage = (message: string) => {
expect(page.logList.getText()).toContain(message); expect(page.logList.getText()).toContain(message);
}; };
describe('Http Tests', function() { // TODO(i): temorarily disable these tests because angular-in-memory-web-api is not compatible with rxjs v6 yet
// and we don't have the backwards compatibility package yet.
// Reenable after rxjs v6 compatibility package is out or angular-in-memory-web-api is compatible with rxjs v6
xdescribe('Http Tests', function() {
beforeEach(() => { beforeEach(() => {
browser.get(''); browser.get('');
}); });

View File

@ -30,7 +30,7 @@ export class ConfigComponent {
this.configService.getConfig() this.configService.getConfig()
// #enddocregion v1, v2 // #enddocregion v1, v2
.subscribe( .subscribe(
data => this.config = { ...data }, // success path (data: Config) => this.config = { ...data }, // success path
error => this.error = error // error path error => this.error = error // error path
); );
} }
@ -39,7 +39,7 @@ export class ConfigComponent {
showConfig_v1() { showConfig_v1() {
this.configService.getConfig_1() this.configService.getConfig_1()
// #docregion v1, v1_callback // #docregion v1, v1_callback
.subscribe(data => this.config = { .subscribe((data: Config) => this.config = {
heroesUrl: data['heroesUrl'], heroesUrl: data['heroesUrl'],
textfile: data['textfile'] textfile: data['textfile']
}); });
@ -51,7 +51,7 @@ export class ConfigComponent {
this.configService.getConfig() this.configService.getConfig()
// #docregion v2, v2_callback // #docregion v2, v2_callback
// clone the data object, using its known Config shape // clone the data object, using its known Config shape
.subscribe(data => this.config = { ...data }); .subscribe((data: Config) => this.config = { ...data });
// #enddocregion v2_callback // #enddocregion v2_callback
} }
// #enddocregion v2 // #enddocregion v2

View File

@ -6,8 +6,7 @@ import { HttpClient } from '@angular/common/http';
import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
// #docregion rxjs-imports // #docregion rxjs-imports
import { Observable } from 'rxjs/Observable'; import { Observable, throwError } from 'rxjs';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
import { catchError, retry } from 'rxjs/operators'; import { catchError, retry } from 'rxjs/operators';
// #enddocregion rxjs-imports // #enddocregion rxjs-imports
@ -82,8 +81,8 @@ export class ConfigService {
`Backend returned code ${error.status}, ` + `Backend returned code ${error.status}, ` +
`body was: ${error.error}`); `body was: ${error.error}`);
} }
// return an ErrorObservable with a user-facing error message // return an observable with a user-facing error message
return new ErrorObservable( return throwError(
'Something bad happened; please try again later.'); 'Something bad happened; please try again later.');
}; };
// #enddocregion handleError // #enddocregion handleError

View File

@ -6,8 +6,7 @@ import { HttpHeaders } from '@angular/common/http';
// #enddocregion http-options // #enddocregion http-options
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { of } from 'rxjs/observable/of';
import { catchError } from 'rxjs/operators'; import { catchError } from 'rxjs/operators';
import { Hero } from './hero'; import { Hero } from './hero';

View File

@ -1,8 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import { of } from 'rxjs/observable/of';
import { MessageService } from './message.service'; import { MessageService } from './message.service';

View File

@ -4,8 +4,6 @@ import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http'; } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
// #docregion // #docregion
import { AuthService } from '../auth.service'; import { AuthService } from '../auth.service';

View File

@ -5,8 +5,7 @@ import {
HttpInterceptor, HttpHandler HttpInterceptor, HttpHandler
} from '@angular/common/http'; } from '@angular/common/http';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import { of } from 'rxjs/observable/of';
import { startWith, tap } from 'rxjs/operators'; import { startWith, tap } from 'rxjs/operators';
import { RequestCache } from '../request-cache.service'; import { RequestCache } from '../request-cache.service';

View File

@ -3,7 +3,7 @@ import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http'; } from '@angular/common/http';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
@Injectable() @Injectable()
export class EnsureHttpsInterceptor implements HttpInterceptor { export class EnsureHttpsInterceptor implements HttpInterceptor {

View File

@ -4,7 +4,6 @@ import {
HttpRequest, HttpResponse HttpRequest, HttpResponse
} from '@angular/common/http'; } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
// #docregion excerpt // #docregion excerpt
import { finalize, tap } from 'rxjs/operators'; import { finalize, tap } from 'rxjs/operators';
import { MessageService } from '../message.service'; import { MessageService } from '../message.service';

View File

@ -3,7 +3,7 @@ import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http'; } from '@angular/common/http';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
/** Pass untouched request through to the next request handler. */ /** Pass untouched request through to the next request handler. */
@Injectable() @Injectable()

View File

@ -3,7 +3,7 @@ import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http'; } from '@angular/common/http';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
@Injectable() @Injectable()
export class TrimNameInterceptor implements HttpInterceptor { export class TrimNameInterceptor implements HttpInterceptor {

View File

@ -5,8 +5,7 @@ import {
HttpEventType, HttpProgressEvent HttpEventType, HttpProgressEvent
} from '@angular/common/http'; } from '@angular/common/http';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { of } from 'rxjs/observable/of';
/** Simulate server replying to file upload request */ /** Simulate server replying to file upload request */
@Injectable() @Injectable()

View File

@ -1,7 +1,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable, Subject } from 'rxjs';
import { Subject } from 'rxjs/Subject';
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
import { NpmPackageInfo, PackageSearchService } from './package-search.service'; import { NpmPackageInfo, PackageSearchService } from './package-search.service';

View File

@ -1,8 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import { of } from 'rxjs/observable/of';
import { catchError, map } from 'rxjs/operators'; import { catchError, map } from 'rxjs/operators';
import { HttpErrorHandler, HandleError } from '../http-error-handler.service'; import { HttpErrorHandler, HandleError } from '../http-error-handler.service';

View File

@ -4,7 +4,7 @@ import {
HttpRequest, HttpResponse, HttpErrorResponse HttpRequest, HttpResponse, HttpErrorResponse
} from '@angular/common/http'; } from '@angular/common/http';
import { of } from 'rxjs/observable/of'; import { of } from 'rxjs';
import { catchError, last, map, tap } from 'rxjs/operators'; import { catchError, last, map, tap } from 'rxjs/operators';
import { MessageService } from '../message.service'; import { MessageService } from '../message.service';

View File

@ -2,8 +2,7 @@
// #docregion // #docregion
import { Injectable, OnDestroy } from '@angular/core'; import { Injectable, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import { of } from 'rxjs/observable/of';
import { delay } from 'rxjs/operators'; import { delay } from 'rxjs/operators';
export class Contact { export class Contact {

View File

@ -1,5 +1,5 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { Crisis, import { Crisis,
CrisisService } from './crisis.service'; CrisisService } from './crisis.service';

View File

@ -1,7 +1,6 @@
import { Injectable, OnDestroy } from '@angular/core'; import { Injectable, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import { of } from 'rxjs/observable/of';
import { delay } from 'rxjs/operators'; import { delay } from 'rxjs/operators';
export class Crisis { export class Crisis {

View File

@ -1,5 +1,5 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { Hero, import { Hero,
HeroService } from './hero.service'; HeroService } from './hero.service';

View File

@ -1,7 +1,6 @@
import { Injectable, OnDestroy } from '@angular/core'; import { Injectable, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import { of } from 'rxjs/observable/of';
import { delay } from 'rxjs/operators'; import { delay } from 'rxjs/operators';
export class Hero { export class Hero {

View File

@ -1,8 +1,7 @@
import { Injectable, OnDestroy } from '@angular/core'; import { Injectable, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import { of } from 'rxjs/observable/of'; import { delay } from 'rxjs/operators';
import { delay } from 'rxjs/operator/delay';
export class Contact { export class Contact {
constructor(public id: number, public name: string) { } constructor(public id: number, public name: string) { }
@ -24,12 +23,12 @@ export class ContactService implements OnDestroy {
ngOnDestroy() { console.log('ContactService instance destroyed.'); } ngOnDestroy() { console.log('ContactService instance destroyed.'); }
getContacts(): Observable<Contact[]> { getContacts(): Observable<Contact[]> {
return delay.call(of(CONTACTS), FETCH_LATENCY); return of(CONTACTS).pipe(delay(FETCH_LATENCY));
} }
getContact(id: number | string): Observable<Contact> { getContact(id: number | string): Observable<Contact> {
const contact$ = of(CONTACTS.find(contact => contact.id === +id)); const contact$ = of(CONTACTS.find(contact => contact.id === +id));
return delay.call(contact$, FETCH_LATENCY); return contact$.pipe(delay(FETCH_LATENCY));
} }
} }

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { Customer, import { Customer,
CustomersService } from './customers.service'; CustomersService } from './customers.service';

View File

@ -1,8 +1,7 @@
import { Injectable, OnDestroy } from '@angular/core'; import { Injectable, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import { of } from 'rxjs/observable/of'; import { delay } from 'rxjs/operators';
import { delay } from 'rxjs/operator/delay';
export class Customer { export class Customer {
constructor(public id: number, public name: string) { } constructor(public id: number, public name: string) { }
@ -27,11 +26,11 @@ export class CustomersService implements OnDestroy {
ngOnDestroy() { console.log('CustomersService instance destroyed.'); } ngOnDestroy() { console.log('CustomersService instance destroyed.'); }
getCustomers(): Observable<Customer[]> { getCustomers(): Observable<Customer[]> {
return delay.call(of(CUSTOMERS), FETCH_LATENCY); return of(CUSTOMERS).pipe(delay(FETCH_LATENCY));
} }
getCustomer(id: number | string): Observable<Customer> { getCustomer(id: number | string): Observable<Customer> {
const customer$ = of(CUSTOMERS.find(customer => customer.id === +id)); const customer$ = of(CUSTOMERS.find(customer => customer.id === +id));
return delay.call(customer$, FETCH_LATENCY); return customer$.pipe(delay(FETCH_LATENCY));
} }
} }

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Observable }from 'rxjs/Observable'; import { Observable }from 'rxjs';
import { Item, import { Item,
ItemService } from './items.service'; ItemService } from './items.service';

View File

@ -1,6 +1,7 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { ItemsComponent } from './items.component';
import { ItemsListComponent } from './items-list.component'; import { ItemsListComponent } from './items-list.component';
import { ItemsDetailComponent } from './items-detail.component'; import { ItemsDetailComponent } from './items-detail.component';
import { ItemService } from './items.service'; import { ItemService } from './items.service';
@ -8,7 +9,7 @@ import { ItemsRoutingModule } from './items-routing.module';
@NgModule({ @NgModule({
imports: [ CommonModule, ItemsRoutingModule ], imports: [ CommonModule, ItemsRoutingModule ],
declarations: [ ItemsDetailComponent, ItemsListComponent ], declarations: [ ItemsComponent, ItemsDetailComponent, ItemsListComponent ],
providers: [ ItemService ] providers: [ ItemService ]
}) })
export class ItemsModule {} export class ItemsModule {}

View File

@ -1,8 +1,7 @@
import { Injectable, OnDestroy } from '@angular/core'; import { Injectable, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import { of } from 'rxjs/observable/of'; import { delay } from 'rxjs/operators';
import { delay } from 'rxjs/operator/delay';
export class Item { export class Item {
constructor(public id: number, public name: string) { } constructor(public id: number, public name: string) { }
@ -25,12 +24,12 @@ export class ItemService implements OnDestroy {
ngOnDestroy() { console.log('ItemService instance destroyed.'); } ngOnDestroy() { console.log('ItemService instance destroyed.'); }
getItems(): Observable<Item[]> { getItems(): Observable<Item[]> {
return delay.call(of(ITEMS), FETCH_LATENCY); return of(ITEMS).pipe(delay(FETCH_LATENCY));
} }
getItem(id: number | string): Observable<Item> { getItem(id: number | string): Observable<Item> {
const item$ = of(ITEMS.find(item => item.id === +id)); const item$ = of(ITEMS.find(item => item.id === +id));
return delay.call(item$, FETCH_LATENCY); return item$.pipe(delay(FETCH_LATENCY));
} }
} }

View File

@ -1,6 +1,6 @@
import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core'; import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
// #docregion eventemitter // #docregion eventemitter

View File

@ -1,5 +1,5 @@
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
// #docregion subscriber // #docregion subscriber

View File

@ -1,4 +1,4 @@
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
// #docregion // #docregion

View File

@ -1,5 +1,5 @@
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
// #docregion delay_sequence // #docregion delay_sequence

View File

@ -1,6 +1,5 @@
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import 'rxjs/add/observable/of';
// #docregion observer // #docregion observer

View File

@ -2,7 +2,7 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http'; import { HttpClientModule } from '@angular//common/http';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { import {
@ -26,7 +26,7 @@ import { ExponentialStrengthPipe } from './exponential-strength.pipe';
imports: [ imports: [
BrowserModule, BrowserModule,
FormsModule, FormsModule,
HttpModule HttpClientModule
], ],
declarations: [ declarations: [
AppComponent, AppComponent,

View File

@ -1,9 +1,6 @@
// #docregion // #docregion
import { Pipe, PipeTransform } from '@angular/core'; import { Pipe, PipeTransform } from '@angular/core';
import { Http } from '@angular/http'; import { HttpClient } from '@angular/common/http';
import 'rxjs/add/operator/map';
// #docregion pipe-metadata // #docregion pipe-metadata
@Pipe({ @Pipe({
name: 'fetch', name: 'fetch',
@ -14,15 +11,13 @@ export class FetchJsonPipe implements PipeTransform {
private cachedData: any = null; private cachedData: any = null;
private cachedUrl = ''; private cachedUrl = '';
constructor(private http: Http) { } constructor(private http: HttpClient) { }
transform(url: string): any { transform(url: string): any {
if (url !== this.cachedUrl) { if (url !== this.cachedUrl) {
this.cachedData = null; this.cachedData = null;
this.cachedUrl = url; this.cachedUrl = url;
this.http.get(url) this.http.get(url).subscribe( result => this.cachedData = result );
.map( result => result.json() )
.subscribe( result => this.cachedData = result );
} }
return this.cachedData; return this.cachedData;

View File

@ -1,10 +1,8 @@
// #docregion // #docregion
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable, interval } from 'rxjs';
import 'rxjs/add/observable/interval'; import { map, take } from 'rxjs/operators';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';
@Component({ @Component({
selector: 'app-hero-message', selector: 'app-hero-message',
@ -25,14 +23,17 @@ export class HeroAsyncMessageComponent {
constructor() { this.resend(); } constructor() { this.resend(); }
resend() { resend() {
this.message$ = Observable.interval(500) this.message$ = interval(500).pipe(
.map(i => this.messages[i]) map(i => this.messages[i]),
.take(this.messages.length); take(this.messages.length)
);
} }
} }
// #enddocregion // #enddocregion
// Alternative message$ formula: // Alternative message$ formula:
// this.message$ = Observable.fromArray(this.messages) // this.message$ = fromArray(this.messages).pipe(
// .map(message => Observable.timer(500).map(() => message)) // map(message => timer(500),
// .concatAll(); // map(() => message)),
// concatAll()
// );

View File

@ -1,9 +1,7 @@
import { ajax } from 'rxjs/observable/dom/ajax'; import { pipe, range, timer, zip } from 'rxjs';
import { range } from 'rxjs/observable/range'; import { ajax } from 'rxjs/ajax';
import { timer } from 'rxjs/observable/timer'; import { retryWhen, map, mergeMap } from 'rxjs/operators';
import { pipe } from 'rxjs/util/pipe';
import { retryWhen, zip, map, mergeMap } from 'rxjs/operators';
function backoff(maxTries, ms) { function backoff(maxTries, ms) {
return pipe( return pipe(

View File

@ -1,6 +1,6 @@
import { fromEvent } from 'rxjs/observable/fromEvent'; import { fromEvent } from 'rxjs';
import { ajax } from 'rxjs/observable/dom/ajax'; import { ajax } from 'rxjs/ajax';
import { map, filter, debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; import { map, filter, debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
const searchBox = document.getElementById('search-box'); const searchBox = document.getElementById('search-box');

View File

@ -6,32 +6,38 @@ import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms'; // <-- #1 import module import { ReactiveFormsModule } from '@angular/forms'; // <-- #1 import module
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component'; // <-- #1 import component import { HeroDetailComponent } from './hero-detail/hero-detail.component';
// #enddocregion v1 // #enddocregion v1
// #docregion hero-service-list
// add JavaScript imports
import { HeroListComponent } from './hero-list/hero-list.component'; import { HeroListComponent } from './hero-list/hero-list.component';
import { HeroService } from './hero.service';
import { HeroService } from './hero.service'; // <-- #1 import service
// #docregion v1 // #docregion v1
@NgModule({ @NgModule({
imports: [
BrowserModule,
ReactiveFormsModule // <-- #2 add to @NgModule imports
],
declarations: [ declarations: [
AppComponent, AppComponent,
HeroDetailComponent, HeroDetailComponent,
// #enddocregion v1 // #enddocregion v1
HeroListComponent HeroListComponent // <--declare HeroListComponent
// #docregion v1 // #docregion v1
], ],
// #enddocregion v1 // #enddocregion hero-service-list
exports: [ // export for the DemoModule imports: [
BrowserModule,
ReactiveFormsModule // <-- #2 add to @NgModule imports
],
// #enddocregion v1
// export for the DemoModule
// #docregion hero-service-list
// ...
exports: [
AppComponent, AppComponent,
HeroDetailComponent, HeroDetailComponent,
HeroListComponent HeroListComponent // <-- export HeroListComponent
], ],
providers: [ HeroService ], // <-- #4 provide HeroService providers: [ HeroService ], // <-- provide HeroService
// #enddocregion hero-service-list
// #docregion v1 // #docregion v1
bootstrap: [ AppComponent ] bootstrap: [ AppComponent ]
}) })

View File

@ -1,6 +1,7 @@
/* tslint:disable:member-ordering */ /* tslint:disable:member-ordering */
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { Hero } from './data-model'; import { Hero } from './data-model';
import { HeroService } from './hero.service'; import { HeroService } from './hero.service';
@ -33,8 +34,9 @@ export class DemoComponent {
getHeroes() { getHeroes() {
this.isLoading = true; this.isLoading = true;
this.heroes = this.heroService.getHeroes() this.heroes = this.heroService.getHeroes().pipe(
.finally(() => this.isLoading = false); finalize(() => this.isLoading = false)
);
this.selectedHero = undefined; this.selectedHero = undefined;
} }

View File

@ -1,7 +1,7 @@
<!-- #docregion basic-form--> <!-- #docregion basic-form-->
<h2>Hero Detail</h2> <h2>Hero Detail</h2>
<h3><i>FormControl in a FormGroup</i></h3> <h3><i>FormControl in a FormGroup</i></h3>
<form [formGroup]="heroForm" novalidate> <form [formGroup]="heroForm">
<div class="form-group"> <div class="form-group">
<label class="center-block">Name: <label class="center-block">Name:
<input class="form-control" formControlName="name"> <input class="form-control" formControlName="name">

View File

@ -1,7 +1,7 @@
<!-- #docregion basic-form--> <!-- #docregion basic-form-->
<h2>Hero Detail</h2> <h2>Hero Detail</h2>
<h3><i>A FormGroup with a single FormControl using FormBuilder</i></h3> <h3><i>A FormGroup with a single FormControl using FormBuilder</i></h3>
<form [formGroup]="heroForm" novalidate> <form [formGroup]="heroForm">
<div class="form-group"> <div class="form-group">
<label class="center-block">Name: <label class="center-block">Name:
<input class="form-control" formControlName="name"> <input class="form-control" formControlName="name">
@ -13,4 +13,4 @@
<!-- #docregion form-value-json --> <!-- #docregion form-value-json -->
<p>Form value: {{ heroForm.value | json }}</p> <p>Form value: {{ heroForm.value | json }}</p>
<p>Form status: {{ heroForm.status | json }}</p> <p>Form status: {{ heroForm.status | json }}</p>
<!-- #enddocregion form-value-json --> <!-- #enddocregion form-value-json -->

View File

@ -1,7 +1,7 @@
<!-- #docregion --> <!-- #docregion -->
<h2>Hero Detail</h2> <h2>Hero Detail</h2>
<h3><i>A FormGroup with multiple FormControls</i></h3> <h3><i>A FormGroup with multiple FormControls</i></h3>
<form [formGroup]="heroForm" novalidate> <form [formGroup]="heroForm">
<div class="form-group"> <div class="form-group">
<label class="center-block">Name: <label class="center-block">Name:
<input class="form-control" formControlName="name"> <input class="form-control" formControlName="name">

View File

@ -1,5 +1,5 @@
<form [formGroup]="heroForm" novalidate> <form [formGroup]="heroForm">
<div class="form-group"> <div class="form-group">
<label class="center-block">Name: <label class="center-block">Name:
<input class="form-control" formControlName="name"> <input class="form-control" formControlName="name">

View File

@ -1,7 +1,7 @@
<!-- #docregion --> <!-- #docregion -->
<h2>Hero Detail</h2> <h2>Hero Detail</h2>
<h3><i>PatchValue to initialize a value</i></h3> <h3><i>PatchValue to initialize a value</i></h3>
<form [formGroup]="heroForm" novalidate> <form [formGroup]="heroForm">
<div class="form-group"> <div class="form-group">
<label class="center-block">Name: <label class="center-block">Name:
<input class="form-control" formControlName="name"> <input class="form-control" formControlName="name">

View File

@ -44,7 +44,13 @@ export class HeroDetailComponent6 implements OnChanges {
} }
// #docregion patch-value-on-changes // #docregion patch-value-on-changes
ngOnChanges() { // <-- wrap patchValue in ngOnChanges ngOnChanges() { // <-- call rebuildForm in ngOnChanges
this.rebuildForm();
}
// #enddocregion patch-value-on-changes
// #docregion patch-value-rebuildform
rebuildForm() { // <-- wrap patchValue in rebuildForm
this.heroForm.reset(); this.heroForm.reset();
// #docregion patch-value // #docregion patch-value
this.heroForm.patchValue({ this.heroForm.patchValue({
@ -52,7 +58,9 @@ export class HeroDetailComponent6 implements OnChanges {
}); });
// #enddocregion patch-value // #enddocregion patch-value
} }
// #enddocregion patch-value-on-changes // #enddocregion patch-value-rebuildform
} }
// #enddocregion v6 // #enddocregion v6

View File

@ -1,7 +1,7 @@
<!-- #docregion --> <!-- #docregion -->
<h2>Hero Detail</h2> <h2>Hero Detail</h2>
<h3><i>A FormGroup with multiple FormControls</i></h3> <h3><i>A FormGroup with multiple FormControls</i></h3>
<form [formGroup]="heroForm" novalidate> <form [formGroup]="heroForm">
<div class="form-group"> <div class="form-group">
<label class="center-block">Name: <label class="center-block">Name:
<input class="form-control" formControlName="name"> <input class="form-control" formControlName="name">

View File

@ -38,32 +38,31 @@ export class HeroDetailComponent7 implements OnChanges {
// #docregion ngOnChanges // #docregion ngOnChanges
ngOnChanges() { ngOnChanges() {
this.rebuildForm();
}
// #enddocregion ngOnChanges
// #docregion rebuildForm
rebuildForm() {
this.heroForm.reset({ this.heroForm.reset({
name: this.hero.name, name: this.hero.name,
address: this.hero.addresses[0] || new Address()
});
}
// #enddocregion ngOnChanges
/* First version of ngOnChanges
// #docregion ngOnChanges-1
ngOnChanges()
// #enddocregion ngOnChanges-1
*/
ngOnChanges1() {
// #docregion reset
this.heroForm.reset();
// #enddocregion reset
// #docregion ngOnChanges-1
// #docregion set-value
this.heroForm.setValue({
name: this.hero.name,
// #docregion set-value-address // #docregion set-value-address
address: this.hero.addresses[0] || new Address() address: this.hero.addresses[0] || new Address()
// #enddocregion set-value-address // #enddocregion set-value-address
}); });
}
// #enddocregion rebuildForm
/* First version of rebuildForm */
rebuildForm1() {
// #docregion reset
this.heroForm.reset();
// #enddocregion reset
// #docregion set-value
this.heroForm.setValue({
name: this.hero.name,
address: this.hero.addresses[0] || new Address()
});
// #enddocregion set-value // #enddocregion set-value
} }
// #enddocregion ngOnChanges-1
} }

View File

@ -1,7 +1,7 @@
<!-- #docplaster--> <!-- #docplaster-->
<h3><i>Using FormArray to add groups</i></h3> <h3><i>Using FormArray to add groups</i></h3>
<form [formGroup]="heroForm" novalidate> <form [formGroup]="heroForm">
<p>Form Changed: {{ heroForm.dirty }}</p> <p>Form Changed: {{ heroForm.dirty }}</p>
<div class="form-group"> <div class="form-group">
@ -11,7 +11,9 @@
</div> </div>
<!-- #docregion form-array--> <!-- #docregion form-array-->
<!-- #docregion form-array-skeleton --> <!-- #docregion form-array-skeleton -->
<!-- #docregion form-array-name -->
<div formArrayName="secretLairs" class="well well-lg"> <div formArrayName="secretLairs" class="well well-lg">
<!-- #enddocregion form-array-name -->
<div *ngFor="let address of secretLairs.controls; let i=index" [formGroupName]="i" > <div *ngFor="let address of secretLairs.controls; let i=index" [formGroupName]="i" >
<!-- The repeated address template --> <!-- The repeated address template -->
<!-- #enddocregion form-array-skeleton --> <!-- #enddocregion form-array-skeleton -->

View File

@ -39,12 +39,18 @@ export class HeroDetailComponent8 implements OnChanges {
// #docregion onchanges // #docregion onchanges
ngOnChanges() { ngOnChanges() {
this.rebuildForm();
}
// #enddocregion onchanges
// #docregion rebuildform
rebuildForm() {
this.heroForm.reset({ this.heroForm.reset({
name: this.hero.name name: this.hero.name
}); });
this.setAddresses(this.hero.addresses); this.setAddresses(this.hero.addresses);
} }
// #enddocregion onchanges // #enddocregion rebuildform
// #docregion get-secret-lairs // #docregion get-secret-lairs
get secretLairs(): FormArray { get secretLairs(): FormArray {

View File

@ -1,11 +1,11 @@
<!-- #docplaster --> <!-- #docplaster -->
<!-- #docregion --> <!-- #docregion -->
<!-- #docregion buttons --> <!-- #docregion buttons -->
<form [formGroup]="heroForm" (ngSubmit)="onSubmit()" novalidate> <form [formGroup]="heroForm" (ngSubmit)="onSubmit()">
<div style="margin-bottom: 1em"> <div style="margin-bottom: 1em">
<button type="submit" <button type="submit"
[disabled]="heroForm.pristine" class="btn btn-success">Save</button> &nbsp; [disabled]="heroForm.pristine" class="btn btn-success">Save</button> &nbsp;
<button type="reset" (click)="revert()" <button type="button" (click)="revert()"
[disabled]="heroForm.pristine" class="btn btn-danger">Revert</button> [disabled]="heroForm.pristine" class="btn btn-danger">Revert</button>
</div> </div>

View File

@ -13,7 +13,10 @@ import { HeroService } from '../hero.service';
templateUrl: './hero-detail.component.html', templateUrl: './hero-detail.component.html',
styleUrls: ['./hero-detail.component.css'] styleUrls: ['./hero-detail.component.css']
}) })
// #docregion onchanges-implementation
export class HeroDetailComponent implements OnChanges { export class HeroDetailComponent implements OnChanges {
// #enddocregion onchanges-implementation
@Input() hero: Hero; @Input() hero: Hero;
heroForm: FormGroup; heroForm: FormGroup;
@ -42,6 +45,10 @@ export class HeroDetailComponent implements OnChanges {
} }
ngOnChanges() { ngOnChanges() {
this.rebuildForm();
}
rebuildForm() {
this.heroForm.reset({ this.heroForm.reset({
name: this.hero.name name: this.hero.name
}); });
@ -66,7 +73,7 @@ export class HeroDetailComponent implements OnChanges {
onSubmit() { onSubmit() {
this.hero = this.prepareSaveHero(); this.hero = this.prepareSaveHero();
this.heroService.updateHero(this.hero).subscribe(/* error handling */); this.heroService.updateHero(this.hero).subscribe(/* error handling */);
this.ngOnChanges(); this.rebuildForm();
} }
// #enddocregion on-submit // #enddocregion on-submit
@ -92,7 +99,7 @@ export class HeroDetailComponent implements OnChanges {
// #enddocregion prepare-save-hero // #enddocregion prepare-save-hero
// #docregion revert // #docregion revert
revert() { this.ngOnChanges(); } revert() { this.rebuildForm(); }
// #enddocregion revert // #enddocregion revert
// #docregion log-name-change // #docregion log-name-change

View File

@ -1,7 +1,7 @@
// #docregion // #docregion
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import 'rxjs/add/operator/finally'; import { finalize } from 'rxjs/operators';
import { Hero } from '../data-model'; import { Hero } from '../data-model';
import { HeroService } from '../hero.service'; import { HeroService } from '../hero.service';
@ -24,7 +24,7 @@ export class HeroListComponent implements OnInit {
this.isLoading = true; this.isLoading = true;
this.heroes = this.heroService.getHeroes() this.heroes = this.heroService.getHeroes()
// Todo: error handling // Todo: error handling
.finally(() => this.isLoading = false); .pipe(finalize(() => this.isLoading = false));
this.selectedHero = undefined; this.selectedHero = undefined;
} }

View File

@ -1,9 +1,8 @@
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import { of } from 'rxjs/observable/of'; import { delay } from 'rxjs/operators';
import 'rxjs/add/operator/delay';
import { Hero, heroes } from './data-model'; import { Hero, heroes } from './data-model';
@ -14,13 +13,13 @@ export class HeroService {
// Fake server get; assume nothing can go wrong // Fake server get; assume nothing can go wrong
getHeroes(): Observable<Hero[]> { getHeroes(): Observable<Hero[]> {
return of(heroes).delay(this.delayMs); // simulate latency with delay return of(heroes).pipe(delay(this.delayMs)); // simulate latency with delay
} }
// Fake server update; assume nothing can go wrong // Fake server update; assume nothing can go wrong
updateHero(hero: Hero): Observable<Hero> { updateHero(hero: Hero): Observable<Hero> {
const oldHero = heroes.find(h => h.id === hero.id); const oldHero = heroes.find(h => h.id === hero.id);
const newHero = Object.assign(oldHero, hero); // Demo: mutate cached hero const newHero = Object.assign(oldHero, hero); // Demo: mutate cached hero
return of(newHero).delay(this.delayMs); // simulate latency with delay return of(newHero).pipe(delay(this.delayMs)); // simulate latency with delay
} }
} }

View File

@ -1,8 +1,8 @@
// #docregion // #docregion
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import 'rxjs/add/operator/map'; import { map } from 'rxjs/operators';
@Component({ @Component({
template: ` template: `
@ -23,11 +23,11 @@ export class AdminDashboardComponent implements OnInit {
// Capture the session ID if available // Capture the session ID if available
this.sessionId = this.route this.sessionId = this.route
.queryParamMap .queryParamMap
.map(params => params.get('session_id') || 'None'); .pipe(map(params => params.get('session_id') || 'None'));
// Capture the fragment if available // Capture the fragment if available
this.token = this.route this.token = this.route
.fragment .fragment
.map(fragment => fragment || 'None'); .pipe(map(fragment => fragment || 'None'));
} }
} }

View File

@ -1,11 +1,11 @@
// #docregion // #docregion
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { SelectivePreloadingStrategy } from '../selective-preloading-strategy'; import { SelectivePreloadingStrategy } from '../selective-preloading-strategy';
import 'rxjs/add/operator/map';
@Component({ @Component({
template: ` template: `
@ -37,11 +37,11 @@ export class AdminDashboardComponent implements OnInit {
// Capture the session ID if available // Capture the session ID if available
this.sessionId = this.route this.sessionId = this.route
.queryParamMap .queryParamMap
.map(params => params.get('session_id') || 'None'); .pipe(map(params => params.get('session_id') || 'None'));
// Capture the fragment if available // Capture the fragment if available
this.token = this.route this.token = this.route
.fragment .fragment
.map(fragment => fragment || 'None'); .pipe(map(fragment => fragment || 'None'));
} }
} }

View File

@ -1,8 +1,8 @@
// #docregion // #docregion
import { animate, AnimationEntryMetadata, state, style, transition, trigger } from '@angular/core'; import { animate, state, style, transition, trigger } from '@angular/animations';
// Component transition animations // Component transition animations
export const slideInDownAnimation: AnimationEntryMetadata = export const slideInDownAnimation =
trigger('routeAnimation', [ trigger('routeAnimation', [
state('*', state('*',
style({ style({

View File

@ -1,10 +1,8 @@
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import 'rxjs/add/observable/of'; import { tap, delay } from 'rxjs/operators';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/delay';
@Injectable() @Injectable()
export class AuthService { export class AuthService {
@ -14,7 +12,10 @@ export class AuthService {
redirectUrl: string; redirectUrl: string;
login(): Observable<boolean> { login(): Observable<boolean> {
return Observable.of(true).delay(1000).do(val => this.isLoggedIn = true); return of(true).pipe(
delay(1000),
tap(val => this.isLoggedIn = true)
);
} }
logout(): void { logout(): void {

View File

@ -1,6 +1,6 @@
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { CanDeactivate, import { CanDeactivate,
ActivatedRouteSnapshot, ActivatedRouteSnapshot,
RouterStateSnapshot } from '@angular/router'; RouterStateSnapshot } from '@angular/router';

View File

@ -1,7 +1,7 @@
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router'; import { CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
export interface CanComponentDeactivate { export interface CanComponentDeactivate {
canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean; canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;

View File

@ -1,10 +1,9 @@
// #docregion // #docregion
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Router, Resolve, RouterStateSnapshot, import { Router, Resolve, RouterStateSnapshot,
ActivatedRouteSnapshot } from '@angular/router'; ActivatedRouteSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { Crisis, CrisisService } from './crisis.service'; import { Crisis, CrisisService } from './crisis.service';
@ -15,13 +14,16 @@ export class CrisisDetailResolver implements Resolve<Crisis> {
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Crisis> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Crisis> {
let id = route.paramMap.get('id'); let id = route.paramMap.get('id');
return this.cs.getCrisis(id).take(1).map(crisis => { return this.cs.getCrisis(id).pipe(
if (crisis) { take(1),
return crisis; map(crisis => {
} else { // id not found if (crisis) {
this.router.navigate(['/crisis-center']); return crisis;
return null; } else { // id not found
} this.router.navigate(['/crisis-center']);
}); return null;
}
})
);
} }
} }

View File

@ -1,9 +1,9 @@
// #docplaster // #docplaster
// #docregion // #docregion
import 'rxjs/add/operator/switchMap';
import { Component, OnInit, HostBinding } from '@angular/core'; import { Component, OnInit, HostBinding } from '@angular/core';
import { ActivatedRoute, Router, ParamMap } from '@angular/router'; import { ActivatedRoute, Router, ParamMap } from '@angular/router';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { slideInDownAnimation } from '../animations'; import { slideInDownAnimation } from '../animations';
import { Crisis, CrisisService } from './crisis.service'; import { Crisis, CrisisService } from './crisis.service';
@ -46,8 +46,9 @@ export class CrisisDetailComponent implements OnInit {
// #docregion ngOnInit // #docregion ngOnInit
ngOnInit() { ngOnInit() {
this.route.paramMap this.route.paramMap
.switchMap((params: ParamMap) => .pipe(
this.service.getCrisis(params.get('id'))) switchMap((params: ParamMap) =>
this.service.getCrisis(params.get('id'))))
.subscribe((crisis: Crisis) => { .subscribe((crisis: Crisis) => {
if (crisis) { if (crisis) {
this.editName = crisis.name; this.editName = crisis.name;

View File

@ -2,7 +2,7 @@
// #docregion // #docregion
import { Component, OnInit, HostBinding } from '@angular/core'; import { Component, OnInit, HostBinding } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { slideInDownAnimation } from '../animations'; import { slideInDownAnimation } from '../animations';
import { Crisis } from './crisis.service'; import { Crisis } from './crisis.service';

View File

@ -1,10 +1,10 @@
import 'rxjs/add/operator/switchMap';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router'; import { ActivatedRoute, ParamMap } from '@angular/router';
import { Crisis, CrisisService } from './crisis.service'; import { Crisis, CrisisService } from './crisis.service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
@Component({ @Component({
// #docregion relative-navigation-router-link // #docregion relative-navigation-router-link
@ -34,10 +34,11 @@ export class CrisisListComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.crises$ = this.route.paramMap this.crises$ = this.route.paramMap.pipe(
.switchMap((params: ParamMap) => { switchMap((params: ParamMap) => {
this.selectedId = +params.get('id'); this.selectedId = +params.get('id');
return this.service.getCrises(); return this.service.getCrises();
}); })
);
} }
} }

View File

@ -1,10 +1,10 @@
// #docregion // #docregion
import 'rxjs/add/operator/switchMap';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router'; import { ActivatedRoute, ParamMap } from '@angular/router';
import { Crisis, CrisisService } from './crisis.service'; import { Crisis, CrisisService } from './crisis.service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
@Component({ @Component({
template: ` template: `
@ -32,10 +32,11 @@ export class CrisisListComponent implements OnInit {
// #enddocregion ctor // #enddocregion ctor
ngOnInit() { ngOnInit() {
this.crises$ = this.route.paramMap this.crises$ = this.route.paramMap.pipe(
.switchMap((params: ParamMap) => { switchMap((params: ParamMap) => {
this.selectedId = +params.get('id'); this.selectedId = +params.get('id');
return this.service.getCrises(); return this.service.getCrises();
}); })
);
} }
} }

View File

@ -1,8 +1,7 @@
// #docplaster // #docplaster
// #docregion , mock-crises // #docregion , mock-crises
import 'rxjs/add/observable/of'; import { BehaviorSubject } from 'rxjs';
import 'rxjs/add/operator/map'; import { map } from 'rxjs/operators';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
export class Crisis { export class Crisis {
constructor(public id: number, public name: string) { } constructor(public id: number, public name: string) { }
@ -26,8 +25,9 @@ export class CrisisService {
getCrises() { return this.crises$; } getCrises() { return this.crises$; }
getCrisis(id: number | string) { getCrisis(id: number | string) {
return this.getCrises() return this.getCrises().pipe(
.map(crises => crises.find(crisis => crisis.id === +id)); map(crises => crises.find(crisis => crisis.id === +id))
);
} }
// #enddocregion // #enddocregion

View File

@ -1,7 +1,6 @@
// #docregion // #docregion
import 'rxjs/add/observable/of';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
/** /**
* Async modal dialog service * Async modal dialog service
@ -17,6 +16,6 @@ export class DialogService {
confirm(message?: string): Observable<boolean> { confirm(message?: string): Observable<boolean> {
const confirmation = window.confirm(message || 'Is it OK?'); const confirmation = window.confirm(message || 'Is it OK?');
return Observable.of(confirmation); return of(confirmation);
}; };
} }

View File

@ -1,10 +1,10 @@
// #docplaster // #docplaster
// #docregion // #docregion
// #docregion rxjs-operator-import // #docregion rxjs-operator-import
import 'rxjs/add/operator/switchMap'; import { switchMap } from 'rxjs/operators';
// #enddocregion rxjs-operator-import // #enddocregion rxjs-operator-import
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
// #docregion imports // #docregion imports
import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { Router, ActivatedRoute, ParamMap } from '@angular/router';
// #enddocregion imports // #enddocregion imports
@ -41,9 +41,10 @@ export class HeroDetailComponent implements OnInit {
// #docregion ngOnInit // #docregion ngOnInit
ngOnInit() { ngOnInit() {
this.hero$ = this.route.paramMap this.hero$ = this.route.paramMap.pipe(
.switchMap((params: ParamMap) => switchMap((params: ParamMap) =>
this.service.getHero(params.get('id'))); this.service.getHero(params.get('id')))
);
} }
// #enddocregion ngOnInit // #enddocregion ngOnInit

View File

@ -2,7 +2,7 @@
// #docregion // #docregion
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { Hero, HeroService } from './hero.service'; import { Hero, HeroService } from './hero.service';

View File

@ -1,11 +1,11 @@
// #docplaster // #docplaster
// #docregion // #docregion
// #docregion rxjs-operator-import // #docregion rxjs-operator-import
import 'rxjs/add/operator/switchMap'; import { switchMap } from 'rxjs/operators';
// #enddocregion rxjs-operator-import // #enddocregion rxjs-operator-import
import { Component, OnInit, HostBinding } from '@angular/core'; import { Component, OnInit, HostBinding } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { Observable } from 'rxjs';
import { slideInDownAnimation } from '../animations'; import { slideInDownAnimation } from '../animations';
@ -48,9 +48,10 @@ export class HeroDetailComponent implements OnInit {
// #docregion ngOnInit // #docregion ngOnInit
ngOnInit() { ngOnInit() {
this.hero$ = this.route.paramMap this.hero$ = this.route.paramMap.pipe(
.switchMap((params: ParamMap) => switchMap((params: ParamMap) =>
this.service.getHero(params.get('id'))); this.service.getHero(params.get('id')))
);
} }
// #enddocregion ngOnInit // #enddocregion ngOnInit

View File

@ -3,7 +3,7 @@
// TODO SOMEDAY: Feature Componetized like HeroCenter // TODO SOMEDAY: Feature Componetized like HeroCenter
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { Hero, HeroService } from './hero.service'; import { Hero, HeroService } from './hero.service';

View File

@ -2,8 +2,8 @@
// #docregion // #docregion
// TODO SOMEDAY: Feature Componetized like CrisisCenter // TODO SOMEDAY: Feature Componetized like CrisisCenter
// #docregion rxjs-imports // #docregion rxjs-imports
import 'rxjs/add/operator/switchMap'; import { Observable } from 'rxjs';
import { Observable } from 'rxjs/Observable'; import { switchMap } from 'rxjs/operators';
// #enddocregion rxjs-imports // #enddocregion rxjs-imports
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
// #docregion import-router // #docregion import-router
@ -41,12 +41,13 @@ export class HeroListComponent implements OnInit {
) {} ) {}
ngOnInit() { ngOnInit() {
this.heroes$ = this.route.paramMap this.heroes$ = this.route.paramMap.pipe(
.switchMap((params: ParamMap) => { switchMap((params: ParamMap) => {
// (+) before `params.get()` turns the string into a number // (+) before `params.get()` turns the string into a number
this.selectedId = +params.get('id'); this.selectedId = +params.get('id');
return this.service.getHeroes(); return this.service.getHeroes();
}); })
);
} }
// #enddocregion ctor // #enddocregion ctor
// #docregion ctor // #docregion ctor

View File

@ -1,8 +1,7 @@
// #docregion // #docregion
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs';
import { map } from 'rxjs/operators';
export class Hero { export class Hero {
constructor(public id: number, public name: string) { } constructor(public id: number, public name: string) { }
@ -19,11 +18,12 @@ const HEROES = [
@Injectable() @Injectable()
export class HeroService { export class HeroService {
getHeroes() { return Observable.of(HEROES); } getHeroes() { return of(HEROES); }
getHero(id: number | string) { getHero(id: number | string) {
return this.getHeroes() return this.getHeroes().pipe(
// (+) before `id` turns the string into a number // (+) before `id` turns the string into a number
.map(heroes => heroes.find(hero => hero.id === +id)); map(heroes => heroes.find(hero => hero.id === +id))
);
} }
} }

View File

@ -1,8 +1,7 @@
// #docregion // #docregion
import 'rxjs/add/observable/of';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router'; import { PreloadingStrategy, Route } from '@angular/router';
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
@Injectable() @Injectable()
export class SelectivePreloadingStrategy implements PreloadingStrategy { export class SelectivePreloadingStrategy implements PreloadingStrategy {
@ -18,7 +17,7 @@ export class SelectivePreloadingStrategy implements PreloadingStrategy {
return load(); return load();
} else { } else {
return Observable.of(null); return of(null);
} }
} }
} }

View File

@ -1,10 +1,9 @@
import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs';
import 'rxjs/add/observable/of';
// #docregion // #docregion
import { ajax } from 'rxjs/observable/dom/ajax'; import { ajax } from 'rxjs/ajax';
import { map, catchError } from 'rxjs/operators'; import { map, catchError } from 'rxjs/operators';
// Return "response" from the API. If an error happens, // Return "response" from the API. If an error happens,
// return an empty array. // return an empty array.
@ -15,7 +14,7 @@ const apiData = ajax('/api/data').pipe(
} }
return res.response; return res.response;
}), }),
catchError(err => Observable.of([])) catchError(err => of([]))
); );
apiData.subscribe({ apiData.subscribe({

View File

@ -1,7 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
@Component({ @Component({
selector: 'app-stopwatch', selector: 'app-stopwatch',

View File

@ -1,12 +1,10 @@
import { Observable } from 'rxjs/Observable'; import { of, pipe } from 'rxjs';
import 'rxjs/add/observable/of';
// #docregion // #docregion
import { pipe } from 'rxjs/util/pipe';
import { filter, map } from 'rxjs/operators'; import { filter, map } from 'rxjs/operators';
const nums = Observable.of(1, 2, 3, 4, 5); const nums = of(1, 2, 3, 4, 5);
// Create a function that accepts an Observable. // Create a function that accepts an Observable.
const squareOddVals = pipe( const squareOddVals = pipe(

View File

@ -1,12 +1,10 @@
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import 'rxjs/add/observable/of';
// #docregion // #docregion
import { filter } from 'rxjs/operators/filter'; import { filter, map } from 'rxjs/operators';
import { map } from 'rxjs/operators/map';
const squareOdd = Observable.of(1, 2, 3, 4, 5) const squareOdd = of(1, 2, 3, 4, 5)
.pipe( .pipe(
filter(n => n % 2), filter(n => n % 2),
map(n => n * n) map(n => n * n)

View File

@ -1,12 +1,11 @@
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import 'rxjs/add/observable/of';
// #docregion // #docregion
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
const nums = Observable.of(1, 2, 3); const nums = of(1, 2, 3);
const squareValues = map((val: number) => val * val); const squareValues = map((val: number) => val * val);
const squaredNums = squareValues(nums); const squaredNums = squareValues(nums);

View File

@ -1,11 +1,10 @@
import { Observable } from 'rxjs/Observable'; import { Observable, of } from 'rxjs';
import 'rxjs/add/observable/of';
// #docregion // #docregion
import { ajax } from 'rxjs/observable/dom/ajax'; import { ajax } from 'rxjs/ajax';
import { map, retry, catchError } from 'rxjs/operators'; import { map, retry, catchError } from 'rxjs/operators';
const apiData = ajax('/api/data').pipe( const apiData = ajax('/api/data').pipe(
@ -16,7 +15,7 @@ const apiData = ajax('/api/data').pipe(
} }
return res.response; return res.response;
}), }),
catchError(err => Observable.of([])) catchError(err => of([]))
); );
apiData.subscribe({ apiData.subscribe({

View File

@ -1,7 +1,7 @@
// #docregion promise // #docregion promise
import { fromPromise } from 'rxjs/observable/fromPromise'; import { fromPromise } from 'rxjs';
// Create an Observable out of a promise // Create an Observable out of a promise
const data = fromPromise(fetch('/api/endpoint')); const data = fromPromise(fetch('/api/endpoint'));
@ -16,7 +16,7 @@ data.subscribe({
// #docregion interval // #docregion interval
import { interval } from 'rxjs/observable/interval'; import { interval } from 'rxjs';
// Create an Observable that will publish a value on an interval // Create an Observable that will publish a value on an interval
const secondsCounter = interval(1000); const secondsCounter = interval(1000);
@ -29,7 +29,7 @@ secondsCounter.subscribe(n =>
// #docregion event // #docregion event
import { fromEvent } from 'rxjs/observable/fromEvent'; import { fromEvent } from 'rxjs';
const el = document.getElementById('my-element'); const el = document.getElementById('my-element');
@ -53,7 +53,7 @@ const subscription = mouseMoves.subscribe((evt: MouseEvent) => {
// #docregion ajax // #docregion ajax
import { ajax } from 'rxjs/observable/dom/ajax'; import { ajax } from 'rxjs/ajax';
// Create an Observable that will create an AJAX request // Create an Observable that will create an AJAX request
const apiData = ajax('/api/data'); const apiData = ajax('/api/data');

View File

@ -3,7 +3,7 @@ import { SwUpdate } from '@angular/service-worker';
// #docregion sw-check-update // #docregion sw-check-update
import { interval } from 'rxjs/observable/interval'; import { interval } from 'rxjs';
@Injectable() @Injectable()
export class CheckForUpdateService { export class CheckForUpdateService {

View File

@ -5,6 +5,7 @@
import { ExceptionService, SpinnerService, ToastService } from '../../core'; import { ExceptionService, SpinnerService, ToastService } from '../../core';
import { Http } from '@angular/http'; import { Http } from '@angular/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators';
import { Hero } from './hero.model'; import { Hero } from './hero.model';
// #enddocregion example // #enddocregion example
@ -19,13 +20,13 @@ export class HeroService {
) { } ) { }
getHero(id: number) { getHero(id: number) {
return this.http.get(`api/heroes/${id}`) return this.http.get(`api/heroes/${id}`).pipe(
.map(response => response.json().data as Hero); map(response => response.json().data as Hero));
} }
getHeroes() { getHeroes() {
return this.http.get(`api/heroes`) return this.http.get(`api/heroes`).pipe(
.map(response => response.json().data as Hero[]); map(response => response.json().data as Hero[]));
} }
} }

View File

@ -2,6 +2,7 @@
// #docregion example // #docregion example
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Http } from '@angular/http'; import { Http } from '@angular/http';
import { map } from 'rxjs/operators';
import { Hero } from './hero.model'; import { Hero } from './hero.model';
import { ExceptionService, SpinnerService, ToastService } from '../../core'; import { ExceptionService, SpinnerService, ToastService } from '../../core';
@ -19,13 +20,13 @@ export class HeroService {
) { } ) { }
getHero(id: number) { getHero(id: number) {
return this.http.get(`api/heroes/${id}`) return this.http.get(`api/heroes/${id}`).pipe(
.map(response => response.json() as Hero); map(response => response.json() as Hero));
} }
getHeroes() { getHeroes() {
return this.http.get(`api/heroes`) return this.http.get(`api/heroes`).pipe(
.map(response => response.json() as Hero[]); map(response => response.json() as Hero[]));
} }
} }

View File

@ -1,6 +1,6 @@
// #docregion // #docregion
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs/Subscription'; import { Subscription } from 'rxjs';
import { LoggerService } from '../logger.service'; import { LoggerService } from '../logger.service';
import { SpinnerState, SpinnerService } from './spinner.service'; import { SpinnerState, SpinnerService } from './spinner.service';

View File

@ -1,6 +1,6 @@
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject'; import { Subject } from 'rxjs';
export interface SpinnerState { export interface SpinnerState {
show: boolean; show: boolean;

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { Hero, HeroService } from './shared'; import { Hero, HeroService } from './shared';

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import { Hero, HeroService } from './shared'; import { Hero, HeroService } from './shared';

View File

@ -1,8 +1,8 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Http } from '@angular/http'; import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import 'rxjs/add/operator/map'; import { map } from 'rxjs/operators';
import { Hero } from './hero.model'; import { Hero } from './hero.model';
@ -12,7 +12,8 @@ export class HeroService {
constructor(private http: Http) {} constructor(private http: Http) {}
getHeroes(): Observable<Hero[]> { getHeroes(): Observable<Hero[]> {
return this.http.get('api/heroes') return this.http.get('api/heroes').pipe(
.map(resp => resp.json().data as Hero[]); map(resp => resp.json().data as Hero[])
);
} }
} }

View File

@ -4,10 +4,8 @@
import { OnInit } from '@angular/core'; import { OnInit } from '@angular/core';
import { Http, Response } from '@angular/http'; import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs';
import 'rxjs/add/operator/catch'; import { catchError, finalize, map } from 'rxjs/operators';
import 'rxjs/add/operator/finally';
import 'rxjs/add/operator/map';
import { Hero } from '../shared/hero.model'; import { Hero } from '../shared/hero.model';
@ -18,11 +16,11 @@ export class HeroListComponent implements OnInit {
constructor(private http: Http) {} constructor(private http: Http) {}
getHeroes() { getHeroes() {
this.heroes = []; this.heroes = [];
this.http.get(heroesUrl) this.http.get(heroesUrl).pipe(
.map((response: Response) => <Hero[]>response.json().data) map((response: Response) => <Hero[]>response.json().data),
.catch(this.catchBadResponse) catchError(this.catchBadResponse),
.finally(() => this.hideSpinner()) finalize(() => this.hideSpinner())
.subscribe((heroes: Hero[]) => this.heroes = heroes); ).subscribe((heroes: Hero[]) => this.heroes = heroes);
} }
ngOnInit() { ngOnInit() {
this.getHeroes(); this.getHeroes();

Some files were not shown because too many files have changed in this diff Show More