Compare commits

..

35 Commits
8.2.2 ... 8.1.0

Author SHA1 Message Date
1033a0285b release: cut the v8.1.0 release 2019-07-02 13:44:35 -07:00
376c5fceb5 docs: add accessibility guide (#30851)
PR Close #30851
2019-07-02 11:39:24 -07:00
48a7581e1e docs: fix typo in Template Syntax (#31298)
Fixes #31282

PR Close #31298
2019-07-02 11:30:50 -07:00
d0c32e03b9 docs(core): in template syntax guide, make SVG example more clear (#31356)
add e2e test for SVG template example
fix template syntax example app
- linting errors
- runtime exceptions
- template type errors
- deprecated type casting
- deprecated currency pipe example

Relates to #30559

PR Close #31356
2019-07-02 11:30:15 -07:00
a57ea2640a test(zone.js): fix typos (#31358)
Relates to #31144

PR Close #31358
2019-07-02 11:29:33 -07:00
4ea54a777f docs: fix typo in deployment page (#31369)
PR Close #31369
2019-07-02 11:29:11 -07:00
0fe6110b97 build(docs-infra): update dgeni-packages dependency (#31368)
The new version 0.27.5 now has the `post-process-html` package, so we
don't need it in angular/angular any more.

PR Close #31368
2019-07-02 11:28:24 -07:00
ffe705066f build(bazel): fix build failures with worker mode cache and @types/events (#31326)
Errors observed only in tests on CircleCI — was not reproducible locally:

```
ERROR: /home/circleci/ng/packages/http/test/BUILD.bazel:3:1: Compiling TypeScript (devmode) //packages/http/test:test_lib failed (Exit 1): tsc_wrapped failed: error executing command
(cd /home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular && \
exec env - \
BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 \
PATH=/bin:/usr/bin:/usr/local/bin \
bazel-out/host/bin/external/npm/@bazel/typescript/bin/tsc_wrapped @@bazel-out/k8-fastbuild/bin/packages/http/test/test_lib_es5_tsconfig.json)
Execution platform: //tools:rbe_ubuntu1604-angular
Compilation failed Error: missing input digest for /home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/external/npm/node_modules/@types/events/index.d.ts.

ERROR: /home/circleci/ng/packages/benchpress/test/BUILD.bazel:3:1: Compiling TypeScript (devmode) //packages/benchpress/test:test_lib failed (Exit 1): tsc_wrapped failed: error executing command
(cd /home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular && \
exec env - \
BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 \
PATH=/bin:/usr/bin:/usr/local/bin \
bazel-out/host/bin/external/npm/@bazel/typescript/bin/tsc_wrapped @@bazel-out/k8-fastbuild/bin/packages/benchpress/test/test_lib_es5_tsconfig.json)
Execution platform: //tools:rbe_ubuntu1604-angular
Compilation failed Error: missing input digest for /home/circleci/.cache/bazel/_bazel_circleci/9ce5c2144ecf75d11717c0aa41e45a8d/execroot/angular/external/npm/node_modules/@types/events/index.d.ts

ERROR: C:/codefresh/volume/angular/packages/compiler/test/css_parser/BUILD.bazel:3:1: Compiling TypeScript (devmode) //packages/compiler/test/css_parser:css_parser_lib failed (Exit 1):
tsc_wrapped.exe failed: error executing command
cd C:/users/containeradministrator/_bazel_containeradministrator/zquin2l6/execroot/angular
SET PATH=C:\msys64\usr\bin;C:\msys64\bin;C:\Windows;C:\Windows\System32;C:\Windows\System32\WindowsPowerShell\v1.0
SET RUNFILES_MANIFEST_ONLY=1
bazel-out/host/bin/external/npm/@bazel/typescript/bin/tsc_wrapped.exe @@bazel-out/x64_windows-fastbuild/bin/packages/compiler/test/css_parser/css_parser_lib_es5_tsconfig.json
Execution platform: @bazel_tools//platforms:host_platform
Compilation failed Error: missing input digest for C:/users/containeradministrator/_bazel_containeradministrator/zquin2l6/execroot/angular/external/npm/node_modules/@types/events/index.
d.ts
```

PR Close #31326
2019-07-01 14:17:09 -07:00
0ac6406f00 build(bazel): update //packages/bazel/test/ngc-wrapped:ngc_test test (#31326)
after update to rules_nodejs 0.32.1, @types are no longer automatically discovered by ngc-wrapped (which uses parts of ts_library) so this test needed updating so that the types files it generates is added as an explicit dep

PR Close #31326
2019-07-01 14:17:09 -07:00
57ffb41a70 build(bazel): add build --incompatible_list_based_execution_strategy_selection=false flag (#31326)
This option is changed to true in Bazel 0.27 and exposes a possible
regression in Bazel 0.27.0.
Error observed is in npm_package target `//packages/common/locales:package`:
```
ERROR: /home/circleci/ng/packages/common/locales/BUILD.bazel:13:1: Assembling
npm package packages/common/locales/package failed: No usable spawn strategy found
for spawn with mnemonic SkylarkAction.  Your --spawn_strategyor --strategy flags
are probably too strict. Visit https://github.com/bazelbuild/bazel/issues/7480 for
migration advises
```
Suspect is https://github.com/bazelbuild/rules_nodejs/blob/master/internal/npm_package/npm_package.bzl#L75-L82:
```
 execution_requirements = {
    # Never schedule this action remotely because it's not computationally expensive.
    # It just copies files into a directory; it's not worth copying inputs and outputs to a remote worker.
    # Also don't run it in a sandbox, because it resolves an absolute path to the bazel-out directory
    # allowing the .pack and .publish runnables to work with no symlink_prefix
    # See https://github.com/bazelbuild/rules_nodejs/issues/187
    "local": "1",
},
```

PR Close #31326
2019-07-01 14:17:09 -07:00
c60edabd70 build(bazel): remove deprecated rules_nodejs NodeModuleInfo provider (#31326)
PR Close #31326
2019-07-01 14:17:09 -07:00
b3b8e102c0 build: disable must-use-promises check in ts_library tsetse for tests as... (#31326)
...tsetse now falsely asserting on  some lines in a few tests such as packages/core/schematics/test/injectable_pipe_migration_spec.ts.

```
    await runMigration();
    expect(tree.readContent('/index.ts'))
```

it asserts that "await is required on promise" on the 2nd line when there is no promise there

PR Close #31326
2019-07-01 14:17:09 -07:00
87c449a085 build(bazel): cleanup entry_point target (#31326)
PR Close #31326
2019-07-01 14:17:09 -07:00
52d47b4696 build(bazel): update to rules_nodejs 0.32.2 (#31326)
Brings in ts_library fixes required to get angular/angular building after 0.32.0:
typescript: exclude typescript lib declarations in node_module_library transitive_declarations
typescript: remove override of @bazel/tsetse (+1 squashed commit)

@npm//node_modules/foobar:foobar.js labels changed to @npm//:node_modules/foobar/foobar.js with fix for bazelbuild/rules_nodejs#802

also updates to rules_rass commit compatible with rules_nodejs 0.32.0

PR Close #31326
2019-07-01 14:17:08 -07:00
917933bb9e build(bazel): update to bazel 0.27.0 and fix compat in @angular/bazel package (#31326)
ctx.actions.declare_file now used in @angular/bazel ng_module rule as ctx.new_file is now deprecated. Fixes error:

```
        File "ng_module.bzl", line 272, in _expected_outs
                ctx.new_file(ctx.genfiles_dir, (ctx.label.name ..."))
Use ctx.actions.declare_file instead of ctx.new_file.
Use --incompatible_new_actions_api=false to temporarily disable this check.
```

This can be worked around with incompatible_new_actions_api flag but may as well fix it proper so downstream doesn't require this flag due to this code.

Also, depset() is no longer iterable by default without a flag. This required fixing in a few spots in @angular/bazel.

fix: foo

PR Close #31326
2019-07-01 14:17:08 -07:00
80ccd6c19b fix(core): handle undefined meta in injectArgs (#31333)
In the recent versions of the CLI we introduced a ctor downleveler tranformer for VE JIT builds based on the one found in tsickle, to fix the TDZ issue of `forwardRef`.

However this caused a regression as the injector is not handling that a position `paramType` can be undefined. Which is bubbled down to c6b29f4c6d/packages/core/src/di/injector_compatibility.ts (L162) and will crash c6b29f4c6d/packages/core/src/di/injector_compatibility.ts (L174-L186)

Fixes https://github.com/angular/angular-cli/issues/14888

PR Close #31333
2019-07-01 10:11:08 -07:00
b7e3d80879 feat(ivy): ngcc - handle top-level helper calls in CommonJS (#31335)
Some formats of CommonJS put the decorator helper calls
outside the class IIFE as statements on the top level of the
source file.

This commit adds support to the `CommonJSReflectionHost`
for this format.

PR Close #31335
2019-07-01 10:09:41 -07:00
4bbf60ed01 docs: change order of Angular Elements article (#30521)
PR Close #30268

PR Close #30521
2019-06-28 12:22:45 -07:00
299a43c7f8 Revert "fix(router): adjust UrlTree redirect to replace URL if in eager update (#31168)" (#31344)
This reverts commit 15e397816f.

Reason: it broke an internal g3 app.

PR Close #31344
2019-06-28 11:40:27 -07:00
c92fe6f6fb docs: add tests for lazy loading angularjs example (#30622)
PR Close #30622
2019-06-28 09:26:50 -07:00
c8875f2dbb docs: added svg example (#30559)
Fixes #30441

PR Close #30559
2019-06-27 15:56:26 -07:00
3ca56a6d5c fix(docs-infra): detect docregions on more file types (pug, svg, yml) (#30559)
PR Close #30559
2019-06-27 15:56:26 -07:00
7223f60060 fix(router): adjust UrlTree redirect to replace URL if in eager update (#31168)
Without this change when using UrlTree redirects in `urlUpdateStrategy="eager"`, the URL would get updated to the target location, then redirected. This resulted in having an additional entry in the `history` and thus the `back` button would be broken (going back would land on the URL causing a new redirect).

Additionally, there was a bug where the redirect, even without `urlUpdateStrategy="eager"`, could create a history with too many entries. This was due to kicking off a new navigation within the navigation cancelling logic. With this PR the new navigation is pushed to the next tick with a `setTimeout`, allowing the page being redirected from to be cancelled before starting a new navigation.

Related to #27148

PR Close #31168
2019-06-27 15:54:20 -07:00
8b034188bd docs: fix example misprints (#31284)
PR Close #31284
2019-06-27 15:53:26 -07:00
579f1295ac ci: add branch info to CircleCI failure notifications (#31319)
PR Close #31319
2019-06-27 15:52:14 -07:00
2914b10eba build(docs-infra): upgrade cli command docs sources to 823731f6e (#31308)
Updating [angular#8.1.x](https://github.com/angular/angular/tree/8.1.x) from [cli-builds#8.0.x](https://github.com/angular/cli-builds/tree/8.0.x).

##
Relevant changes in [commit range](01a7186bb...823731f6e):

**Modified**
- help/analytics.json
- help/build.json
- help/e2e.json
- help/generate.json
- help/new.json
- help/serve.json
- help/test.json
- help/update.json
- help/xi18n.json

##

PR Close #31308
2019-06-27 11:51:56 -07:00
c00544ac51 refactor(service-worker): remove redundant cache operation (#30977)
At this point, the response will have been cached (or scheduled to be
cached) in other code paths, so caching it again is redundant.

PR Close #30977
2019-06-27 09:52:29 -07:00
a9038ef13c fix(service-worker): cache opaque responses when requests exceeds timeout threshold (#30977)
PR Close #30977
2019-06-27 09:52:29 -07:00
b0c345324a fix(service-worker): cache opaque responses in data groups with freshness strategy (#30977)
Previously, (presummably due to a typo) the `okToCacheOpaque` argument
of `DataGroup#cacheResponse()` was essentially never taken into account
(since opaque responses have a non-200 status code and thus `res.ok` is
always false).

This commit fixes the typo, which allows opaque responses to be cached
when `okToCacheOpaque` is true (i.e. in data groups using the
`freshness` strategy).

Fixes #30968

PR Close #30977
2019-06-27 09:52:29 -07:00
26efc682d5 refactor(service-worker): make the caching behavior more explicit (#30977)
This commit doesn't change the behavior wrt caching, but it makes it
more explicit that only non-timed-out responses are cached. In case of a
timeout, `res` would be set to a programmatically created 504
`Response`, so `cacheResponse()` (which checks for `res.ok`) would not
have cached it anyway, but this makes change makes it more explicit (and
more similar to the equivalent part in [handleFetchWithFreshness()][1]).

[1]: https://github.com/angular/angular/blob/2b4d5c754/packages/service-worker/worker/src/data.ts#L379-L388

PR Close #30977
2019-06-27 09:52:29 -07:00
8b6128759c test(service-worker): better simulate opaque requests (#30977)
Previously, opaque responses where handled a little differently than
other responses from the mock server. More specifically, they were not
tracked (so no assertions could be made for them) and their
[`Body` mixin][1] methods (such as `arrayBuffer()`, `json()`, `text()`)
would throw an error due to `body` being `null`.

This commit ensures opaque responses are also captured on the mock
server and also changes `Body` mixin methods to better simulate the
[spec'd behavior][2].

(These improvements will be necessary to test caching of opaque
responses in a subsequent commit.)

[1]: https://developer.mozilla.org/en-US/docs/Web/API/Body
[2]: https://fetch.spec.whatwg.org/#concept-body-consume-body

PR Close #30977
2019-06-27 09:52:29 -07:00
54c171cded test(service-worker): properly reset mock server state for each test (#30977)
PR Close #30977
2019-06-27 09:52:29 -07:00
642f6046af test(service-worker): simplify test helpers (#30977)
PR Close #30977
2019-06-27 09:52:29 -07:00
d0e213d137 test(service-worker): remove obsolete async test helpers (#30977)
Jasmine natively supports returning promises from spec functions for
quite some time now. We don't need special async helpers.

PR Close #30977
2019-06-27 09:52:29 -07:00
7418c901c2 docs: couple of small UI fixes throughout some documents (#31155)
PR Close #31155
2019-06-27 09:51:28 -07:00
737 changed files with 20172 additions and 26717 deletions

View File

@ -1,3 +1,12 @@
###############################
# Typescript / Angular / Sass #
###############################
# Make compilation fast, by keeping a few copies of the compilers
# running as daemons, and cache SourceFile AST's to reduce parse time.
build --strategy=AngularTemplateCompile=worker
build --strategy=TypeScriptCompile=worker
# Enable debugging tests with --config=debug
test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test_strategy=exclusive --test_timeout=9999 --nocache_test_results
@ -74,6 +83,12 @@ query --output=label_kind
# By default, failing tests don't print any output, it goes to the log file
test --test_output=errors
# Show which actions are run under workers,
# and print all the actions running in parallel.
# Helps to demonstrate that bazel uses all the cores on the machine.
build --experimental_ui
test --experimental_ui
################################
# Settings for CircleCI #
################################

View File

@ -28,14 +28,3 @@ test --flaky_test_attempts=2
# More details on failures
build --verbose_failures=true
# We have seen some flakiness in using TS workers on CircleCI
# https://angular-team.slack.com/archives/C07DT5M6V/p1562693245183400
# > failures like `ERROR: /home/circleci/ng/packages/core/test/BUILD.bazel:5:1:
# > Compiling TypeScript (devmode) //packages/core/test:test_lib failed: Worker process did not return a WorkResponse:`
# > I saw that issue a couple times today.
# > Example job: https://circleci.com/gh/angular/angular/385517
# We expect that TypeScript compilations will parallelize wider than the number of local cores anyway
# so we should saturate remote workers with TS compilations
build --strategy=TypeScriptCompile=standalone
build --strategy=AngularTemplateCompile=standalone

View File

@ -58,7 +58,17 @@ var_5: &setup_bazel_remote_execution
# cause decryption failures based on the openssl version. https://stackoverflow.com/a/39641378/4317734
openssl aes-256-cbc -d -in .circleci/gcp_token -md md5 -k "$CI_REPO_NAME" -out /home/circleci/.gcp_credentials
echo "export GOOGLE_APPLICATION_CREDENTIALS=/home/circleci/.gcp_credentials" >> $BASH_ENV
./.circleci/setup-rbe.sh .bazelrc.user
touch .bazelrc.user
sudo bash -c "echo -e 'build --config=remote\n' >> .bazelrc.user"
sudo bash -c "echo -e 'build:remote --remote_accept_cached=true\n' >> .bazelrc.user"
echo "Reading from remote cache for bazel remote jobs."
if [[ "$CI_PULL_REQUEST" == "false" ]]; then
sudo bash -c "echo -e 'build:remote --remote_upload_local_results=true\n' >> .bazelrc.user"
echo "Uploading local build results to remote cache."
else
sudo bash -c "echo -e 'build:remote --remote_upload_local_results=false\n' >> .bazelrc.user"
echo "Not uploading local build results to remote cache."
fi
# Settings common to each job
var_6: &job_defaults
@ -141,11 +151,6 @@ var_14: &notify_dev_infra_on_fail
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job for $CIRCLE_BRANCH branch failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
curl --request POST --header "Content-Type: application/json" --data "$notificationJson" $SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
# Cache key for the Material unit tests job. **Note** when updating the SHA in the cache keys,
# also update the SHA for the "MATERIAL_REPO_COMMIT" environment variable.
var_15: &material_unit_tests_cache_key v4-angular-material-701302dc482d7e4b77990b24e3b5ab330bbf1aa5
var_16: &material_unit_tests_cache_key_short v4-angular-material
version: 2
jobs:
setup:
@ -240,11 +245,6 @@ jobs:
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.br
destination: core/todo/bundle.br
# This job is currently a PoC for running tests on SauceLabs via bazel. It runs a subset of the
# tests in `legacy-unit-tests-saucelabs` (see
# [BUILD.bazel](https://github.com/angular/angular/blob/ef44f51d5/BUILD.bazel#L66-L92)).
#
# NOTE: This is currently limited to master builds only. See the `default_workflow` configuration.
test_saucelabs_bazel:
<<: *job_defaults
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
@ -293,8 +293,6 @@ jobs:
- run: yarn --cwd aio e2e --configuration=ci
# Run PWA-score tests
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
# Run accessibility tests
- run: yarn --cwd aio test-a11y-score-localhost
# Check the bundle sizes.
- run: yarn --cwd aio payload-size
# Run unit tests for Firebase redirects
@ -321,7 +319,7 @@ jobs:
- *attach_workspace
- *init_environment
# Build aio (with local Angular packages)
- run: yarn --cwd aio build-local-ci
- run: yarn --cwd aio build-local --progress=false
# Run unit tests
- run: yarn --cwd aio test --progress=false --watch=false
# Run e2e tests
@ -340,7 +338,7 @@ jobs:
- *attach_workspace
- *init_environment
# Build aio with Ivy (using local Angular packages)
- run: yarn --cwd aio build-with-ivy-ci
- run: yarn --cwd aio build-with-ivy --progress=false
# Run unit tests
- run: yarn --cwd aio test --progress=false --watch=false
# Run e2e tests
@ -620,36 +618,40 @@ jobs:
resource_class: xlarge
docker:
- image: *browsers_docker_image
# The Material unit tests support splitting the browsers across multiple CircleCI
# instances. Since by default this job launches two browsers, we run each browser
# in its own container instance.
# https://github.com/angular/material2/blob/7baeaa797b19da2d2998f0d26f6fede3c8a13714/test/karma.conf.js#L107-L110
parallelism: 2
environment:
# The Material unit tests also support launching the same browser multiple times by
# sharding individual specs across the defined multiple instances.
# See: https://github.com/angular/material2/blob/7baeaa797b19da2d2998f0d26f6fede3c8a13714/test/karma.conf.js#L113-L116
KARMA_PARALLEL_BROWSERS: 3
steps:
- *attach_workspace
- *init_environment
# Although RBE is configured below for the Material repo, also setup RBE in the Angular repo
# to provision Angular's GCP token into the environment variables.
- *setup_bazel_remote_execution
# Restore the cache before cloning the repository because the clone script re-uses
# the restored repository if present. This reduces the amount of times the components
# repository needs to be cloned (this is slow and increases based on commits in the repo).
- restore_cache:
keys:
- *material_unit_tests_cache_key
- *material_unit_tests_cache_key_short
- run:
name: "Fetching Material repository"
name: "Cloning Material repository"
command: ./scripts/ci/clone_angular_material_repo.sh
- restore_cache:
# Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable.
# It needs to be hardcoded here, because env variables interpolation is not supported.
keys:
- v2-angular-material-{{ checksum "/tmp/material2/yarn.lock" }}
- v2-angular-material-
- run:
# Run yarn install to fetch the Bazel binaries as used in the Material repo.
name: Installing Material dependencies.
command: yarn --cwd ${MATERIAL_REPO_TMP_DIR} install --frozen-lockfile --non-interactive
# Save the cache before we run the Material unit tests script. This is necessary
# because we don't want to cache the node modules which have been modified to contain
# the attached Ivy package output.
- save_cache:
key: *material_unit_tests_cache_key
# Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable.
# It needs to be hardcoded here, because env variables interpolation is not supported.
key: v2-angular-material-{{ checksum "/tmp/material2/yarn.lock" }}
paths:
# Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable.
# It needs to be hardcoded here, because env variables interpolation is not supported.
- "/tmp/material2"
- run:
name: "Setup Bazel RBE remote execution in Material repo"
command: |
./.circleci/setup-rbe.sh "${MATERIAL_REPO_TMP_DIR}/.bazelrc.user"
- "/tmp/material2/node_modules"
- run:
name: "Running Material unit tests"
command: ./scripts/ci/run_angular_material_unit_tests.sh
@ -664,10 +666,6 @@ jobs:
# Run zone.js tools tests
- run: yarn --cwd packages/zone.js promisetest
- run: yarn --cwd packages/zone.js promisefinallytest
- run: yarn bazel build //packages/zone.js:npm_package &&
cp dist/bin/packages/zone.js/npm_package/dist/zone-mix.js ./packages/zone.js/test/extra/ &&
cp dist/bin/packages/zone.js/npm_package/dist/zone-patch-electron.js ./packages/zone.js/test/extra/ &&
yarn --cwd packages/zone.js electrontest
workflows:
version: 2
@ -676,41 +674,31 @@ workflows:
- setup
- lint:
requires:
- setup
- setup
- test:
requires:
- setup
- setup
- test_ivy_aot:
requires:
- setup
- setup
- build-npm-packages:
requires:
- setup
- setup
- build-ivy-npm-packages:
requires:
- setup
- legacy-misc-tests:
requires:
- build-npm-packages
- legacy-unit-tests-saucelabs:
requires:
- setup
- test_saucelabs_bazel:
requires:
- setup
# This job is currently a PoC and a subset of `legacy-unit-tests-saucelabs`. Running on
# master only to avoid wasting resources.
#
# TODO: Run this job on all branches (including PRs) as soon as it is not a PoC.
filters:
branches:
only: master
- setup
- test_aio:
requires:
- setup
- setup
- legacy-unit-tests-saucelabs:
requires:
- setup
- deploy_aio:
requires:
- test_aio
- legacy-misc-tests:
requires:
- build-npm-packages
- test_aio_local:
requires:
- build-npm-packages
@ -767,6 +755,22 @@ workflows:
requires:
- setup
saucelabs_tests:
jobs:
- setup
- test_saucelabs_bazel:
requires:
- setup
triggers:
- schedule:
# Runs the Saucelabs legacy tests every hour. We still want to run Saucelabs
# frequently as the caretaker needs up-to-date results when merging PRs or creating
# a new release. Also we primarily moved the Saucelabs job into a cronjob that doesn't
# run for PRs, in order to ensure that PRs are not affected by Saucelabs flakiness or
# incidents. This is still guaranteed (even if we run the job every hour).
cron: "0 * * * *"
filters: *publish_branches_filter
aio_monitoring:
jobs:
- setup

View File

@ -77,9 +77,7 @@ setPublicVar SAUCE_READY_FILE_TIMEOUT 120
# their separate build setups.
setPublicVar MATERIAL_REPO_TMP_DIR "/tmp/material2"
setPublicVar MATERIAL_REPO_URL "https://github.com/angular/material2.git"
setPublicVar MATERIAL_REPO_BRANCH "master"
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI "config.yml".
setPublicVar MATERIAL_REPO_COMMIT "701302dc482d7e4b77990b24e3b5ab330bbf1aa5"
setPublicVar MATERIAL_REPO_BRANCH "ivy-2019"
# Source `$BASH_ENV` to make the variables available immediately.
source $BASH_ENV;

Binary file not shown.

View File

@ -1,20 +0,0 @@
#!/usr/bin/env bash
set -u -e -o pipefail
# The path of the .bazelrc.user file to update should be passed as first parameter to this script.
# This allows to setup RBE for both the Angular repo and the Material repo.
bazelrc_user="$1"
echo "Writing RBE configuration to ${bazelrc_user}"
touch ${bazelrc_user}
echo -e 'build --config=remote\n' >> ${bazelrc_user}
echo -e 'build:remote --remote_accept_cached=true\n' >> ${bazelrc_user}
echo "Reading from remote cache for bazel remote jobs."
if [[ "$CI_PULL_REQUEST" == "false" ]]; then
echo -e 'build:remote --remote_upload_local_results=true\n' >> ${bazelrc_user}
echo "Uploading local build results to remote cache."
else
echo -e 'build:remote --remote_upload_local_results=false\n' >> ${bazelrc_user}
echo "Not uploading local build results to remote cache."
fi

View File

@ -0,0 +1,120 @@
# escape=`
ARG core=mcr.microsoft.com/windows/servercore:1809
ARG target=mcr.microsoft.com/powershell:windowsservercore-1809
FROM $core as download
ARG node_version=10.13.0
ARG yarn_version=1.13.0
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
ENV GPG_VERSION 2.3.4
RUN Invoke-WebRequest $('https://files.gpg4win.org/gpg4win-vanilla-{0}.exe' -f $env:GPG_VERSION) -OutFile 'gpg4win.exe' -UseBasicParsing ; `
Start-Process .\gpg4win.exe -ArgumentList '/S' -NoNewWindow -Wait
RUN @( `
'94AE36675C464D64BAFA68DD7434390BDBE9B9C5', `
'FD3A5288F042B6850C66B31F09FE44734EB7990E', `
'71DCFD284A79C3B38668286BC97EC7A07EDE3FC1', `
'DD8F2338BAE7501E3DD5AC78C273792F7D83545D', `
'C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8', `
'B9AE9905FFD7803F25714661B63B535A4C206CA9', `
'77984A986EBC2AA786BC0F66B01FBB92821C587A', `
'8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600', `
'4ED778F539E3634C779C87C6D7062848A1AB005C', `
'A48C2BEE680E841632CD4E44F07496B3EB3C1762', `
'B9E2F5981AA6E0CD28160D9FF13993A75599653C' `
) | foreach { `
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys $_ ; `
}
ENV NODE_VERSION=$node_version
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/SHASUMS256.txt.asc' -f $env:NODE_VERSION) -OutFile 'SHASUMS256.txt.asc' -UseBasicParsing ; `
gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/node-v{0}-win-x64.zip' -f $env:NODE_VERSION) -OutFile 'node.zip' -UseBasicParsing ; `
$sum = $(cat SHASUMS256.txt.asc | sls $(' node-v{0}-win-x64.zip' -f $env:NODE_VERSION)) -Split ' ' ; `
if ((Get-FileHash node.zip -Algorithm sha256).Hash -ne $sum[0]) { Write-Error 'SHA256 mismatch' } ; `
Expand-Archive node.zip -DestinationPath C:\ ; `
Rename-Item -Path $('C:\node-v{0}-win-x64' -f $env:NODE_VERSION) -NewName 'C:\nodejs'
ENV YARN_VERSION=$yarn_version
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; `
Invoke-WebRequest $('https://yarnpkg.com/downloads/{0}/yarn-{0}.msi' -f $env:YARN_VERSION) -OutFile yarn.msi -UseBasicParsing ; `
$sig = Get-AuthenticodeSignature yarn.msi ; `
if ($sig.Status -ne 'Valid') { Write-Error 'Authenticode signature is not valid' } ; `
Write-Output $sig.SignerCertificate.Thumbprint ; `
if (@( `
'7E253367F8A102A91D04829E37F3410F14B68A5F', `
'AF764E1EA56C762617BDC757C8B0F3780A0CF5F9' `
) -notcontains $sig.SignerCertificate.Thumbprint) { Write-Error 'Unknown signer certificate' } ; `
Start-Process msiexec.exe -ArgumentList '/i', 'yarn.msi', '/quiet', '/norestart' -NoNewWindow -Wait
ENV GIT_VERSION 2.20.1
ENV GIT_DOWNLOAD_URL https://github.com/git-for-windows/git/releases/download/v${GIT_VERSION}.windows.1/MinGit-${GIT_VERSION}-busybox-64-bit.zip
ENV GIT_SHA256 9817ab455d9cbd0b09d8664b4afbe4bbf78d18b556b3541d09238501a749486c
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; `
Invoke-WebRequest -UseBasicParsing $env:GIT_DOWNLOAD_URL -OutFile git.zip; `
if ((Get-FileHash git.zip -Algorithm sha256).Hash -ne $env:GIT_SHA256) {exit 1} ; `
Expand-Archive git.zip -DestinationPath C:\git; `
Remove-Item git.zip
FROM $target as baseimage
ENV NPM_CONFIG_LOGLEVEL info
COPY --from=download /nodejs /nodejs
COPY --from=download [ "/Program Files (x86)/yarn", "/yarn" ]
COPY --from=download /git /git
ARG SETX=/M
RUN setx %SETX% PATH "%PATH%;C:\nodejs;C:\yarn\bin;C:\git\cmd;C:\git\mingw64\bin;C:\git\usr\bin"
CMD [ "node.exe" ]
FROM baseimage
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe; `
Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait; `
Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz; `
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait; `
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait; `
Remove-Item msys2.tar.xz; `
Remove-Item msys2.tar; `
Remove-Item 7z.exe; `
Remove-Item -Recurse 7zip; `
[Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine); `
[Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine)
# Install VS Build Tools
RUN Invoke-WebRequest -UseBasicParsing https://download.visualstudio.microsoft.com/download/pr/df649173-11e9-4af2-8eb7-0eb02ba8958a/cadb5bdac41e55bb8f6a6b7c45273370/vs_buildtools.exe -OutFile vs_BuildTools.exe; `
# Installer won't detect DOTNET_SKIP_FIRST_TIME_EXPERIENCE if ENV is used, must use setx /M
setx /M DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1; `
Start-Process vs_BuildTools.exe `
-ArgumentList `
'--add', 'Microsoft.VisualStudio.Workload.VCTools', `
'--add', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', `
'--add', 'Microsoft.Component.VC.Runtime.UCRTSDK', `
'--add', 'Microsoft.VisualStudio.Component.Windows10SDK.17763', `
'--quiet', '--norestart', '--nocache' `
-NoNewWindow -Wait; `
Remove-Item -Force vs_buildtools.exe; `
Remove-Item -Force -Recurse \"${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\"; `
Remove-Item -Force -Recurse ${Env:TEMP}\*; `
Remove-Item -Force -Recurse \"${Env:ProgramData}\Package Cache\"; `
[Environment]::SetEnvironmentVariable('BAZEL_VC', \"${Env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\BuildTools\VC\", [System.EnvironmentVariableTarget]::Machine)
# Install Python
RUN Invoke-WebRequest -UseBasicParsing https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile python-3.5.1.exe; `
Start-Process python-3.5.1.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait; `
Remove-Item -Force python-3.5.1.exe
CMD ["cmd.exe"]

33
.codefresh/README.md Normal file
View File

@ -0,0 +1,33 @@
# CodeFresh configuration
[![Codefresh build status](https://g.codefresh.io/api/badges/pipeline/angular/angular%2Fangular%2Fangular?type=cf-1)](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular)
This folder contains configuration for the [CodeFresh](<https://codefresh.io/>) based CI checks for this repository.
## The build pipeline
CodeFresh uses a several pipeline for each repository. The `codefresh.yml` file defines pipeline [build steps](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines/) for this repository.
Run results can be seen in the GitHub checks interface and in the [public pipeline](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular)
Although most configuration is done via `pipeline.yml`, some options are only available in the online [pipeline settings](https://g.codefresh.io/pipelines/angular/services?repoOwner=angular&repoName=angular&project=angular%2Fangular&context=github&serviceName=angular%2Fangular), which needs a login to access.
## Caretaker
CodeFresh status can be found at <http://status.codefresh.io/>.
Issues related to the CodeFresh setup should be escalated to the Tools Team via the current caretaker, followed by Alex Eagle and Filipe Silva.
## Rollout strategy
Currently it is only used for tests on Windows platforms, on the master branch, and without pushing user-facing reports. It's only possible to see current builds in the [public pipeline dashboard](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular).
After a week or two of running like this, we should reassess how stable and reliable it is.
Next steps include:
- building PRs
- showing build status publicly
- blocking PRs that break the build
- expanding the test suite

38
.codefresh/bazel.rc Normal file
View File

@ -0,0 +1,38 @@
# These options are enabled when running on CI
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
# See documentation in /docs/BAZEL.md
# Save built files and downloaded repositories in a location that can be cached by CodeFresh and
# shared between builds. This helps speed up the analysis time significantly with Bazel managed node
# dependencies on the CI.
# https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines/#caching-the-artifacts-of-your-build-system
build --repository_cache=C:/codefresh/volume/bazel_repository_cache
# Setting the output_base to a Docker volume is currently broken because of a Docker bug on Windows:
# https://github.com/moby/moby/issues/37024
# This affects Bazel because bazel_output_base\external\bazel_tools is an absolute path junction.
# When its fixed we can uncomment this line, and use a different output_base for Ivy tests (they
# use a separate compiler and destructively replace the cache).
# startup --output_base=C:/codefresh/volume/bazel_output_base
# Don't be spammy in the logs
# TODO(gmagolan): Hide progress again once build performance improves
# Presently, CircleCI can timeout during bazel test ... with the following
# error: Too long with no output (exceeded 10m0s)
build --noshow_progress
# Print all the options that apply to the build.
# This helps us diagnose which options override others
# (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc)
build --announce_rc
# Workaround https://github.com/bazelbuild/bazel/issues/3645
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
# Limit Bazel to consuming resources that fit in CodeFresh VMs
# TODO(filipesilva): determine the correct memory limit
build --local_resources=10240,8.0,1.0
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
test --flaky_test_attempts=2
# More details on failures
build --verbose_failures=true

28
.codefresh/codefresh.yml Normal file
View File

@ -0,0 +1,28 @@
version: '1.0'
steps:
BuildImage:
title: Build Docker image
type: build
image_name: node-bazel-windows
working_directory: ./.codefresh
no_cf_cache: true
build_arguments:
- node_version=10.13.0
- yarn_version=1.13.0
dockerfile: ./Dockerfile.win-1809
RunTests:
title: Run Bazel tests
image: ${{BuildImage}}
commands:
# Install dependencies
- yarn install --frozen-lockfile --non-interactive --network-timeout 100000 --no-progress
# Add Bazel CI config
- copy .codefresh\bazel.rc %ProgramData%\bazel.bazelrc
# Run tests
# At the moment 'browser:chromium-local' are broken in CI while locally they work
# VE
- yarn bazel test --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only,-browser:chromium-local //...
# Ivy
- yarn bazel test --define=compile=aot --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot --test_tag_filters=-no-ivy-aot,-fixme-ivy-aot,-browser:chromium-local //...

1
.github/CODEOWNERS vendored
View File

@ -884,6 +884,7 @@ testing/** @angular/fw-test
/* @angular/fw-dev-infra
/.buildkite/** @angular/fw-dev-infra
/.circleci/** @angular/fw-dev-infra
/.codefresh/** @angular/fw-dev-infra
/.devcontainer/** @angular/fw-dev-infra
/.github/** @angular/fw-dev-infra
/.vscode/** @angular/fw-dev-infra

View File

@ -1,138 +1,3 @@
<a name="8.2.2"></a>
## [8.2.2](https://github.com/angular/angular/compare/8.2.1...8.2.2) (2019-08-12)
### Bug Fixes
* **bazel:** disable treeshaking when generating FESM and UMD bundles ([#32069](https://github.com/angular/angular/issues/32069)) ([3420d29](https://github.com/angular/angular/commit/3420d29))
<a name="8.2.1"></a>
## [8.2.1](https://github.com/angular/angular/compare/8.2.0...8.2.1) (2019-08-08)
### Bug Fixes
* **upgrade:** compile downgraded components synchronously (if possible) ([#31840](https://github.com/angular/angular/issues/31840)) ([04ebd59](https://github.com/angular/angular/commit/04ebd59)), closes [#27217](https://github.com/angular/angular/issues/27217) [#30330](https://github.com/angular/angular/issues/30330)
<a name="8.2.0"></a>
# [8.2.0](https://github.com/angular/angular/compare/8.2.0-rc.0...8.2.0) (2019-07-31)
### Bug Fixes
* **core:** DebugElement.listeners not cleared on destroy ([#31820](https://github.com/angular/angular/issues/31820)) ([46b160e](https://github.com/angular/angular/commit/46b160e))
<a name="8.2.0-rc.0"></a>
# [8.2.0-rc.0](https://github.com/angular/angular/compare/8.2.0-next.2...8.2.0-rc.0) (2019-07-26)
### Bug Fixes
* **bazel:** increase memory limit of ngc under bazel from 2 to 4 GB ([#31784](https://github.com/angular/angular/issues/31784)) ([5a8eb92](https://github.com/angular/angular/commit/5a8eb92))
* **core:** allow Z variations of CSS transforms in sanitizer ([#29264](https://github.com/angular/angular/issues/29264)) ([78e7fdd](https://github.com/angular/angular/commit/78e7fdd))
* **elements:** handle falsy initial value ([#31604](https://github.com/angular/angular/issues/31604)) ([7151eae](https://github.com/angular/angular/commit/7151eae)), closes [angular/angular#30834](https://github.com/angular/angular/issues/30834)
* **platform-browser:** debug element query predicates not compatible with strictFunctionTypes ([#30993](https://github.com/angular/angular/issues/30993)) ([10a1e19](https://github.com/angular/angular/commit/10a1e19))
### Features
* **bazel:** compile targets used for indexing by Kythe with Ivy ([#31786](https://github.com/angular/angular/issues/31786)) ([82055b2](https://github.com/angular/angular/commit/82055b2))
* **upgrade:** support $element in upgraded component template/templateUrl functions ([#31637](https://github.com/angular/angular/issues/31637)) ([29e1c53](https://github.com/angular/angular/commit/29e1c53))
### Performance Improvements
* **compiler:** avoid copying from prototype while cloning an object ([#31638](https://github.com/angular/angular/issues/31638)) ([24ca582](https://github.com/angular/angular/commit/24ca582)), closes [#31627](https://github.com/angular/angular/issues/31627)
<a name="8.1.3"></a>
## [8.1.3](https://github.com/angular/angular/compare/8.1.2...8.1.3) (2019-07-26)
### Bug Fixes
* **elements:** handle falsy initial value ([#31604](https://github.com/angular/angular/issues/31604)) ([434b796](https://github.com/angular/angular/commit/434b796)), closes [angular/angular#30834](https://github.com/angular/angular/issues/30834)
### Performance Improvements
* **compiler:** avoid copying from prototype while cloning an object ([#31638](https://github.com/angular/angular/issues/31638)) ([1f3daa0](https://github.com/angular/angular/commit/1f3daa0)), closes [#31627](https://github.com/angular/angular/issues/31627)
<a name="8.2.0-next.2"></a>
# [8.2.0-next.2](https://github.com/angular/angular/compare/8.2.0-next.1...8.2.0-next.2) (2019-07-17)
### Bug Fixes
* use the correct WTF array to iterate over ([#31208](https://github.com/angular/angular/issues/31208)) ([9204de9](https://github.com/angular/angular/commit/9204de9))
* **bazel:** pass custom bazel compiler host rather than rewriting one ([#31496](https://github.com/angular/angular/issues/31496)) ([0c61a35](https://github.com/angular/angular/commit/0c61a35))
* **compiler-cli:** Return original sourceFile instead of redirected sourceFile from getSourceFile ([#26036](https://github.com/angular/angular/issues/26036)) ([3166cff](https://github.com/angular/angular/commit/3166cff)), closes [#22524](https://github.com/angular/angular/issues/22524)
* **language-service:** Eagarly initialize data members ([#31577](https://github.com/angular/angular/issues/31577)) ([0110de2](https://github.com/angular/angular/commit/0110de2))
<a name="8.1.2"></a>
## [8.1.2](https://github.com/angular/angular/compare/8.1.0...8.1.2) (2019-07-17)
### Bug Fixes
* use the correct WTF array to iterate over ([#31208](https://github.com/angular/angular/issues/31208)) ([4aed480](https://github.com/angular/angular/commit/4aed480))
* **compiler-cli:** Return original sourceFile instead of redirected sourceFile from getSourceFile ([#26036](https://github.com/angular/angular/issues/26036)) ([13dbb98](https://github.com/angular/angular/commit/13dbb98)), closes [#22524](https://github.com/angular/angular/issues/22524)
* **core:** export provider interfaces that are part of the public API types ([#31377](https://github.com/angular/angular/issues/31377)) ([bebf089](https://github.com/angular/angular/commit/bebf089)), closes [/github.com/angular/angular/pull/31377#discussion_r299254408](https://github.com//github.com/angular/angular/pull/31377/issues/discussion_r299254408) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L365-L366](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L365-L366) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L283-L284](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L283-L284) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts#L23](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts/issues/L23)
<a name="8.2.0-next.1"></a>
# [8.2.0-next.1](https://github.com/angular/angular/compare/8.2.0-next.0...8.2.0-next.1) (2019-07-10)
### Bug Fixes
* **bazel:** revert location of xi18n outputs to bazel-genfiles ([#31410](https://github.com/angular/angular/issues/31410)) ([1d3e227](https://github.com/angular/angular/commit/1d3e227))
* **compiler:** give ASTWithSource its own visit method ([#31347](https://github.com/angular/angular/issues/31347)) ([6aaca21](https://github.com/angular/angular/commit/6aaca21))
### Features
* **core:** add automatic migration from Renderer to Renderer2 ([#30936](https://github.com/angular/angular/issues/30936)) ([c095597](https://github.com/angular/angular/commit/c095597))
<a name="8.1.1"></a>
## [8.1.1](https://github.com/angular/angular/compare/8.1.0...8.1.1) (2019-07-10)
### Bug Fixes
* **core:** export provider interfaces that are part of the public API types ([#31377](https://github.com/angular/angular/issues/31377)) ([bebf089](https://github.com/angular/angular/commit/bebf089)), closes [/github.com/angular/angular/pull/31377#discussion_r299254408](https://github.com//github.com/angular/angular/pull/31377/issues/discussion_r299254408) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L365-L366](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L365-L366) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L283-L284](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L283-L284) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts#L23](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts/issues/L23)
<a name="8.2.0-next.0"></a>
# [8.2.0-next.0](https://github.com/angular/angular/compare/8.1.0-rc.0...8.2.0-next.0) (2019-07-02)
### Bug Fixes
* **core:** handle `undefined` meta in `injectArgs` ([#31333](https://github.com/angular/angular/issues/31333)) ([80ccd6c](https://github.com/angular/angular/commit/80ccd6c)), closes [CLI #14888](https://github.com/angular/angular-cli/issues/14888)
* **service-worker:** cache opaque responses in data groups with `freshness` strategy ([#30977](https://github.com/angular/angular/issues/30977)) ([d7be38f](https://github.com/angular/angular/commit/d7be38f)), closes [#30968](https://github.com/angular/angular/issues/30968)
* **service-worker:** cache opaque responses when requests exceeds timeout threshold ([#30977](https://github.com/angular/angular/issues/30977)) ([93abc35](https://github.com/angular/angular/commit/93abc35))
### Features
* **bazel:** allow passing a custom bazel compiler host to ngc compile ([#31341](https://github.com/angular/angular/issues/31341)) ([a29dc96](https://github.com/angular/angular/commit/a29dc96))
* **bazel:** allow passing and rewriting an old bazel host ([#31381](https://github.com/angular/angular/issues/31381)) ([11a208f](https://github.com/angular/angular/commit/11a208f)), closes [#31341](https://github.com/angular/angular/issues/31341)
<a name="8.1.0"></a>
# [8.1.0](https://github.com/angular/angular/compare/8.1.0-rc.0...8.1.0) (2019-07-02)

View File

@ -18,11 +18,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Fetch rules_nodejs so we can install our npm dependencies
http_archive(
name = "build_bazel_rules_nodejs",
patch_args = ["-p1"],
# Patch https://github.com/bazelbuild/rules_nodejs/pull/903
patches = ["//tools:rollup_bundle_commonjs_ignoreGlobal.patch"],
sha256 = "7c4a690268be97c96f04d505224ec4cb1ae53c2c2b68be495c9bd2634296a5cd",
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.34.0/rules_nodejs-0.34.0.tar.gz"],
sha256 = "6d4edbf28ff6720aedf5f97f9b9a7679401bf7fca9d14a0fff80f644a99992b4",
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.32.2/rules_nodejs-0.32.2.tar.gz"],
)
# Check the bazel version and download npm dependencies
@ -53,8 +50,7 @@ Try running `yarn bazel` instead.
# - 0.32.0 yarn_install and npm_install no longer puts build files under symlinked node_modules
# - 0.32.1 remove override of @bazel/tsetse & exclude typescript lib declarations in node_module_library transitive_declarations
# - 0.32.2 resolves bug in @bazel/hide-bazel-files postinstall step
# - 0.34.0 introduces protractor rule
check_rules_nodejs_version(minimum_version_string = "0.34.0")
check_rules_nodejs_version(minimum_version_string = "0.32.2")
# Setup the Node.js toolchain
node_repositories(
@ -92,11 +88,6 @@ load("//packages/bazel:package.bzl", "rules_angular_dev_dependencies")
rules_angular_dev_dependencies()
# Load protractor dependencies
load("@npm_bazel_protractor//:package.bzl", "npm_bazel_protractor_dependencies")
npm_bazel_protractor_dependencies()
# Load karma dependencies
load("@npm_bazel_karma//:package.bzl", "rules_karma_dependencies")

View File

@ -14,12 +14,10 @@ Here are the most important tasks you might need to use:
* `yarn` - install all the dependencies.
* `yarn setup` - install all the dependencies, boilerplate, stackblitz, zips and run dgeni on the docs.
* `yarn setup-local` - same as `setup`, but build the Angular packages from the source code and use these locally built versions (instead of the ones fetched from npm) for aio and docs examples boilerplate.
* `yarn setup-local` - same as `setup`, but use the locally built Angular packages for aio and docs examples boilerplate.
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
* `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
* `yarn build-with-ivy` - same as `build-local`, but in addition also turns on `ivy` mode in aio.
(Note: To turn on `ivy` mode in examples, see `yarn boilerplate:add` below.)
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
* `yarn serve-and-sync` - run both the `docs-watch` and `start` in the same console.
@ -33,10 +31,7 @@ Here are the most important tasks you might need to use:
* `yarn docs-lint` - check that the doc gen code follows our style rules.
* `yarn docs-test` - run the unit tests for the doc generation code.
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally.
- Add the option `--local` to use your local version of Angular contained in the "dist" folder.
- Add the option `--ivy` to turn on `ivy` mode.
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally. Add the option `--local` to use your local version of Angular contained in the "dist" folder.
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
* `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.

View File

@ -7,7 +7,7 @@ The Angular CLI is a command-line interface tool that you use to initialize, dev
Major versions of Angular CLI follow the supported major version of Angular, but minor versions can be released separately.
Install the CLI using the `npm` package manager:
<code-example language="bash">
<code-example format="." language="bash">
npm install -g @angular/cli
</code-example>
@ -20,14 +20,14 @@ Invoke the tool on the command line through the `ng` executable.
Online help is available on the command line.
Enter the following to list commands or options for a given command (such as [generate](cli/generate)) with a short description.
<code-example language="bash">
<code-example format="." language="bash">
ng help
ng generate --help
</code-example>
To create, build, and serve a new, basic Angular project on a development server, go to the parent directory of your new workspace use the following commands:
<code-example language="bash">
<code-example format="." language="bash">
ng new my-first-project
cd my-first-project
ng serve
@ -36,14 +36,6 @@ ng serve
In your browser, open http://localhost:4200/ to see the new app run.
When you use the [ng serve](cli/serve) command to build an app and serve it locally, the server automatically rebuilds the app and reloads the page when you change any of the source files.
<div class="alert is-helpful">
When you run `ng new my-first-project` a new folder, named `my-first-project`, will be created in the current working directory. Since you want to be able to create files inside that folder, make sure you have sufficient rights in the current working directory before running the command.
If the current working directory is not the right place for your project, you can change to a more appropriate directory by running `cd <path-to-other-directory>` first.
</div>
## Workspaces and project files
The [ng new](cli/new) command creates an *Angular workspace* folder and generates a new app skeleton.
@ -82,8 +74,8 @@ Command syntax is shown as follows:
* Option names are prefixed with a double dash (--).
Option aliases are prefixed with a single dash (-).
Arguments are not prefixed.
For example:
<code-example language="bash">
For example:
<code-example format="." language="bash">
ng build my-app -c production
</code-example>
@ -113,5 +105,5 @@ Schematic options are supplied to the command in the same format as immediate co
### Building with Bazel
Optionally, you can configure the Angular CLI to use [Bazel](https://docs.bazel.build) as the build tool. For more information, see [Building with Bazel](guide/bazel).
Optionally, you can configure the Angular CLI to use [Bazel](https://docs.bazel.build) as the build tool. For more information, see [Building with Bazel](guide/bazel).

View File

@ -25,8 +25,8 @@ describe('Attribute directives', () => {
greenRb.click();
browser.actions().mouseMove(highlightedEle).perform();
// Wait for up to 4s for the background color to be updated,
// Wait for up to 2s for the background color to be updated,
// to account for slow environments (e.g. CI).
browser.wait(() => highlightedEle.getCssValue('background-color').then(c => c === lightGreen), 4000);
browser.wait(() => highlightedEle.getCssValue('background-color').then(c => c === lightGreen), 2000);
});
});

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -18,7 +18,7 @@ export class AppComponent {
}
deleteItem(item: Item) {
alert(`Delete the ${item.name}.`);
alert(`Delete the ${item}.`);
}
onClickMe(event?: KeyboardEvent) {

View File

@ -16,20 +16,16 @@
<!-- #enddocregion checkout-form-1 -->
<div>
<label for="name">
Name
</label>
<input id="name" type="text" formControlName="name">
<label>Name</label>
<input type="text" formControlName="name">
</div>
<div>
<label for="address">
Address
</label>
<input id="address" type="text" formControlName="address">
<label>Address</label>
<input type="text" formControlName="address">
</div>
<button class="button" type="submit">Purchase</button>
<!-- #docregion checkout-form-1 -->
</form>

View File

@ -10,6 +10,7 @@ export class AppComponent {
gender = 'female';
fly = true;
logo = 'https://angular.io/assets/images/logos/angular/angular.png';
heroes: string[] = ['Magneta', 'Celeritas', 'Dynama'];
inc(i: number) {
this.minutes = Math.min(5, Math.max(0, this.minutes + i));
}

View File

@ -1,14 +0,0 @@
// imports
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
// @NgModule decorator with its metadata
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}

View File

@ -7,9 +7,9 @@ import { from } from 'rxjs';
const data = from(fetch('/api/endpoint'));
// Subscribe to begin listening for async result
data.subscribe({
next(response) { console.log(response); },
error(err) { console.error('Error: ' + err); },
complete() { console.log('Completed'); }
next(response) { console.log(response); },
error(err) { console.error('Error: ' + err); },
complete() { console.log('Completed'); }
});
// #enddocregion promise

View File

@ -0,0 +1,12 @@
{
"description": "Quickstart AppComponent Testing",
"files":[
"src/browser-test-shim.js",
"src/app/app.component.ts",
"src/app/app.component.spec.ts",
"src/quickstart-specs.html"
],
"main": "src/quickstart-specs.html",
"file": "src/app/app.component.spec.ts",
"tags": ["quickstart", "setup", "testing"]
}

View File

@ -0,0 +1,36 @@
<!-- Run application specs in a browser -->
<!-- #docregion -->
<!DOCTYPE html>
<html>
<head>
<base href="/">
<title>1st Specs</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
</head>
<body>
<!-- Polyfills -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script src="node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
<script src="node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/zone.js/dist/zone-testing.js"></script>
<!-- #docregion files -->
<script>
var __spec_files__ = [
'app/app.component.spec'
];
</script>
<!-- #enddocregion files-->
<script src="browser-test-shim.js"></script>
</body>
</html>

View File

@ -0,0 +1,12 @@
{
"description": "QuickStart Setup",
"files": [
"src/app/app.component.ts",
"src/app/app.module.ts",
"src/index.html",
"src/main.ts",
"src/styles.css"
],
"file": "src/app/app.component.ts",
"tags": ["quickstart", "setup", "seed"]
}

View File

@ -1,22 +1,24 @@
{
"description": "Testing - specs",
"files":[
"src/expected.ts",
"src/index-specs.html",
"src/main-specs.ts",
"src/styles.css",
"src/test.css",
"src/tests.sb.ts",
"e2e/src/**/*.ts",
"src/app/**/*.css",
"src/app/**/*.html",
"src/app/**/*.ts",
"src/app/**/*.spec.ts",
"src/testing/**/*",
"src/testing/*.ts",
"src/**/*.spec.ts"
"!src/main.ts",
"!src/app/bag/*.*",
"!src/app/1st.spec.ts",
"src/expected.ts",
"src/test.css",
"src/tests.sb.ts",
"src/main-specs.ts",
"src/index-specs.html"
],
"main": "src/index-specs.html",
"tags": ["testing"]

View File

@ -1,3 +1,3 @@
import jasmineRequire from 'jasmine-core/lib/jasmine-core/jasmine.js';
const jasmineRequire = require('jasmine-core/lib/jasmine-core/jasmine.js');
window['jasmineRequire'] = jasmineRequire;

View File

@ -1,18 +1,19 @@
{
"description": "Heroes Test App",
"files":[
"src/index.html",
"src/main.ts",
"src/styles.css",
"src/test.css",
"e2e/src/**/*.ts",
"src/app/**/*.css",
"src/app/**/*.html",
"src/app/**/*.ts",
"!src/**/*.spec.ts"
"!src/app/bag/*.*",
"!src/test.ts",
"src/test.css",
"src/main.ts",
"src/index.html"
],
"tags": ["testing"]
}

View File

@ -1,4 +1,8 @@
/* HeroesComponent's private CSS styles */
.selected {
background-color: #CFD8DC !important;
color: white;
}
.heroes {
margin: 0 0 2em 0;
list-style-type: none;
@ -15,18 +19,18 @@
height: 1.6em;
border-radius: 4px;
}
.heroes li.selected:hover {
background-color: #BBD8DC !important;
color: white;
}
.heroes li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.heroes li.selected {
background-color: #CFD8DC;
color: white;
}
.heroes li.selected:hover {
background-color: #BBD8DC;
color: white;
.heroes .text {
position: relative;
top: -3px;
}
.heroes .badge {
display: inline-block;

View File

@ -34,7 +34,4 @@ export class HeroesComponent implements OnInit {
this.selectedHero = hero;
}
// #enddocregion on-select
// #docregion component
}
// #enddocregion component

View File

@ -1,4 +1,8 @@
/* HeroesComponent's private CSS styles */
.selected {
background-color: #CFD8DC !important;
color: white;
}
.heroes {
margin: 0 0 2em 0;
list-style-type: none;
@ -15,18 +19,18 @@
height: 1.6em;
border-radius: 4px;
}
.heroes li.selected:hover {
background-color: #BBD8DC !important;
color: white;
}
.heroes li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.heroes li.selected {
background-color: #CFD8DC;
color: white;
}
.heroes li.selected:hover {
background-color: #BBD8DC;
color: white;
.heroes .text {
position: relative;
top: -3px;
}
.heroes .badge {
display: inline-block;

View File

@ -1,4 +1,8 @@
/* HeroesComponent's private CSS styles */
.selected {
background-color: #CFD8DC !important;
color: white;
}
.heroes {
margin: 0 0 2em 0;
list-style-type: none;
@ -15,18 +19,18 @@
height: 1.6em;
border-radius: 4px;
}
.heroes li.selected:hover {
background-color: #BBD8DC !important;
color: white;
}
.heroes li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.heroes li.selected {
background-color: #CFD8DC;
color: white;
}
.heroes li.selected:hover {
background-color: #BBD8DC;
color: white;
.heroes .text {
position: relative;
top: -3px;
}
.heroes .badge {
display: inline-block;
@ -39,6 +43,8 @@
left: -1px;
top: -4px;
height: 1.8em;
min-width: 16px;
text-align: right;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}

View File

@ -1,13 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroesComponent } from './heroes/heroes.component';
const routes: Routes = [
{ path: 'heroes', component: HeroesComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -7,7 +7,9 @@ import { RouterModule, Routes } from '@angular/router';
// #docregion import-dashboard
import { DashboardComponent } from './dashboard/dashboard.component';
// #enddocregion import-dashboard
// #docregion heroes-route
import { HeroesComponent } from './heroes/heroes.component';
// #enddocregion heroes-route
// #docregion import-herodetail
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
// #enddocregion import-herodetail
@ -37,9 +39,7 @@ const routes: Routes = [
imports: [ RouterModule.forRoot(routes) ],
// #enddocregion ngmodule-imports
// #docregion v1
// #docregion export-routermodule
exports: [ RouterModule ]
// #enddocregion export-routermodule
})
export class AppRoutingModule {}
// #enddocregion , v1

View File

@ -23,17 +23,13 @@ import { HeroSearchComponent } from './hero-search/hero-search.component';
// #docregion v1
import { MessagesComponent } from './messages/messages.component';
// #docregion import-httpclientmodule
@NgModule({
imports: [
// #enddocregion import-httpclientmodule
BrowserModule,
FormsModule,
AppRoutingModule,
// #docregion in-mem-web-api-imports
// #docregion import-httpclientmodule
HttpClientModule,
// #enddocregion import-httpclientmodule
// The HttpClientInMemoryWebApiModule module intercepts HTTP requests
// and returns simulated server responses.
@ -42,9 +38,7 @@ import { MessagesComponent } from './messages/messages.component';
InMemoryDataService, { dataEncapsulation: false }
)
// #enddocregion in-mem-web-api-imports
// #docregion import-httpclientmodule
],
// #enddocregion import-httpclientmodule
declarations: [
AppComponent,
DashboardComponent,
@ -56,9 +50,6 @@ import { MessagesComponent } from './messages/messages.component';
// #docregion v1
],
bootstrap: [ AppComponent ]
// #docregion import-httpclientmodule
})
// #enddocregion import-httpclientmodule
export class AppModule { }
// #enddocregion , v1

View File

@ -13,6 +13,11 @@ import { catchError, map, tap } from 'rxjs/operators';
import { Hero } from './hero';
import { MessageService } from './message.service';
// #docregion http-options
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
// #enddocregion http-options
@Injectable({ providedIn: 'root' })
export class HeroService {
@ -21,12 +26,6 @@ export class HeroService {
private heroesUrl = 'api/heroes'; // URL to web api
// #enddocregion heroesUrl
// #docregion http-options
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
// #enddocregion http-options
// #docregion ctor
constructor(
private http: HttpClient,
@ -97,7 +96,7 @@ export class HeroService {
// #docregion addHero
/** POST: add a new hero to the server */
addHero (hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe(
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),
catchError(this.handleError<Hero>('addHero'))
);
@ -110,7 +109,7 @@ export class HeroService {
const id = typeof hero === 'number' ? hero : hero.id;
const url = `${this.heroesUrl}/${id}`;
return this.http.delete<Hero>(url, this.httpOptions).pipe(
return this.http.delete<Hero>(url, httpOptions).pipe(
tap(_ => this.log(`deleted hero id=${id}`)),
catchError(this.handleError<Hero>('deleteHero'))
);
@ -120,7 +119,7 @@ export class HeroService {
// #docregion updateHero
/** PUT: update the hero on the server */
updateHero (hero: Hero): Observable<any> {
return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(
return this.http.put(this.heroesUrl, hero, httpOptions).pipe(
tap(_ => this.log(`updated hero id=${hero.id}`)),
catchError(this.handleError<any>('updateHero'))
);

View File

@ -179,8 +179,3 @@ Books
* "A Web for Everyone: Designing Accessible User Experiences", Sarah Horton and Whitney Quesenbery
* "Inclusive Design Patterns", Heydon Pickering
## More on accessibility
You may also be interested in the following:
* [Audit your Angular app's accessibility with codelyzer](https://web.dev/accessible-angular-with-codelyzer/).

View File

@ -66,7 +66,7 @@ The following table lists some of the key AngularJS template features with their
### Bindings/interpolation
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="interpolation"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="interpolation" linenums="false"></code-example>
In Angular, a template expression in curly braces still denotes one-way binding.
@ -102,7 +102,7 @@ The following table lists some of the key AngularJS template features with their
### Pipes
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="uppercase"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="uppercase" linenums="false"></code-example>
In Angular you use similar syntax with the pipe (|) character to filter output, but now you call them **pipes**.
@ -136,7 +136,7 @@ The following table lists some of the key AngularJS template features with their
### Input variables
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="local"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="local" linenums="false"></code-example>
Angular has true template input variables that are explicitly defined using the `let` keyword.
@ -202,10 +202,10 @@ The following are some of the key AngularJS built-in directives and their equiva
### Bootstrapping
<code-example hideCopy path="ajs-quick-reference/src/main.ts" header="main.ts"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/main.ts" header="main.ts" linenums="false"></code-example>
<br>
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts" header="app.module.ts"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts" header="app.module.ts" linenums="false"></code-example>
Angular doesn't have a bootstrap directive.
@ -245,7 +245,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### ngClass
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngClass"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngClass" linenums="false"></code-example>
In Angular, the `ngClass` directive works similarly.
@ -291,7 +291,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### Bind to the `click` event
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="event-binding"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="event-binding" linenums="false"></code-example>
AngularJS event-based directives do not exist in Angular.
@ -338,7 +338,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### Component decorator
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false"></code-example>
In Angular, the template no longer specifies its associated controller.
@ -401,7 +401,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### Bind to the `href` property
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="href"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="href" linenums="false"></code-example>
Angular uses property binding; there is no built-in *href* directive.
@ -412,7 +412,7 @@ The following are some of the key AngularJS built-in directives and their equiva
In Angular, `href` is no longer used for routing. Routing uses `routerLink`, as shown in the following example.
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="router-link"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="router-link" linenums="false"></code-example>
For more information on routing, see the [RouterLink binding](guide/router#router-link)
@ -445,7 +445,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### *ngIf
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngIf"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngIf" linenums="false"></code-example>
The `*ngIf` directive in Angular works the same as the `ng-if` directive in AngularJS. It removes
@ -480,7 +480,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### ngModel
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngModel"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngModel" linenums="false"></code-example>
In Angular, **two-way binding** is denoted by `[()]`, descriptively referred to as a "banana in a box". This syntax is a shortcut for defining both property binding (from the component to the view)
@ -516,7 +516,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### *ngFor
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngFor"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngFor" linenums="false"></code-example>
The `*ngFor` directive in Angular is similar to the `ng-repeat` directive in AngularJS. It repeats
@ -559,7 +559,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### Bind to the `hidden` property
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="hidden"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="hidden" linenums="false"></code-example>
Angular uses property binding; there is no built-in *show* directive.
@ -598,7 +598,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### Bind to the `src` property
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="src"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="src" linenums="false"></code-example>
Angular uses property binding; there is no built-in *src* directive.
@ -635,7 +635,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### ngStyle
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngStyle"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngStyle" linenums="false"></code-example>
In Angular, the `ngStyle` directive works similarly. It sets a CSS style on an HTML element based on an expression.
@ -690,7 +690,7 @@ The following are some of the key AngularJS built-in directives and their equiva
### ngSwitch
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngSwitch"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngSwitch" linenums="false"></code-example>
In Angular, the `ngSwitch` directive works similarly.
@ -765,7 +765,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### currency
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="currency"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="currency" linenums="false"></code-example>
The Angular `currency` pipe is similar although some of the parameters have changed.
@ -793,7 +793,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### date
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="date"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="date" linenums="false"></code-example>
The Angular `date` pipe is similar.
@ -847,7 +847,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### json
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="json"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="json" linenums="false"></code-example>
The Angular `json` pipe does the same thing.
@ -876,7 +876,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### slice
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="slice"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="slice" linenums="false"></code-example>
The `SlicePipe` does the same thing but the *order of the parameters is reversed*, in keeping
@ -907,7 +907,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### lowercase
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="lowercase"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="lowercase" linenums="false"></code-example>
The Angular `lowercase` pipe does the same thing.
@ -935,7 +935,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### number
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="number"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="number" linenums="false"></code-example>
The Angular `number` pipe is similar.
@ -1068,7 +1068,7 @@ The Angular code is shown using TypeScript.
### NgModules
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts" linenums="false"></code-example>
NgModules, defined with the `NgModule` decorator, serve the same purpose:
@ -1109,7 +1109,7 @@ The Angular code is shown using TypeScript.
### Component decorator
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false"></code-example>
Angular adds a decorator to the component class to provide any required metadata.
@ -1145,7 +1145,7 @@ The Angular code is shown using TypeScript.
### Component class
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="class"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="class" linenums="false"></code-example>
In Angular, you create a component class.
@ -1184,7 +1184,7 @@ The Angular code is shown using TypeScript.
### Dependency injection
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="di"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="di" linenums="false"></code-example>
In Angular, you pass in dependencies as arguments to the component class constructor.
@ -1254,7 +1254,7 @@ also encapsulate a style sheet within a specific component.
### Styles configuration
<code-example hideCopy path="ajs-quick-reference/.angular-cli.1.json" region="styles"></code-example>
<code-example hideCopy path="ajs-quick-reference/.angular-cli.1.json" region="styles" linenums="false"></code-example>
With the Angular CLI, you can configure your global styles in the `angular.json` file.
You can rename the extension to `.scss` to use sass.
@ -1263,7 +1263,7 @@ also encapsulate a style sheet within a specific component.
In Angular, you can use the `styles` or `styleUrls` property of the `@Component` metadata to define
a style sheet for a particular component.
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="style-url"></code-example>
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="style-url" linenums="false"></code-example>
This allows you to set appropriate styles for individual components that wont leak into

View File

@ -1,148 +0,0 @@
# Angular compiler options
When you use [AOT compilation](guide/aot-compiler), you can control how your application is compiled by specifying *template* compiler options in the `tsconfig.json` [TypeScript configuration file](guide/typescript-configuration).
The template options object, `angularCompilerOptions`, is a sibling to the `compilerOptions` object that supplies standard options to the TypeScript compiler.
```json
{
"compilerOptions": {
"experimentalDecorators": true,
...
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"preserveWhitespaces": true,
...
}
}
```
This page describes the available Angular template compiler options.
### `allowEmptyCodegenFiles`
When true, generate all possible files even if they are empty. Default is false. Used by the Bazel build rules to simplify how Bazel rules track file dependencies. Do not use this option outside of the Bazel rules.
### `annotationsAs`
Modifies how Angular-specific annotations are emitted to improve tree-shaking. Non-Angular annotations are not affected. One of `static fields` (the default) or `decorators`.
* By default, the compiler replaces decorators with a static field in the class, which allows advanced tree-shakers like [Closure compiler](https://github.com/google/closure-compiler) to remove unused classes.
* The `decorators` value leaves the decorators in place, which makes compilation faster. TypeScript emits calls to the` __decorate` helper. Use `--emitDecoratorMetadata` for runtime reflection (but note taht the resulting code will not properly tree-shake.
### `annotateForClosureCompiler`
When true, use [Tsickle](https://github.com/angular/tsickle) to annotate the emitted JavaScript with [JSDoc](http://usejsdoc.org/) comments needed by the
[Closure Compiler](https://github.com/google/closure-compiler). Default is false.
### `disableExpressionLowering`
When true (the default), transforms code that is or could be used in an annotation, to allow it to be imported from template factory modules. See [metadata rewriting](guide/aot-compiler#metadata-rewriting) for more information.
When `false`, disables this rewriting, requiring the rewriting to be done manually.
### `disableTypeScriptVersionCheck`
When `true`, the compiler does not check the TypeScript version and does not report an error when an unsupported version of TypeScript is used. Not recommended, as unsupported versions of TypeScript might have undefined behavior. Default is false.
### `enableResourceInlining`
When true, replaces the `templateUrl` and `styleUrls` property in all `@Component` decorators with inlined contents in `template` and `styles` properties.
When enabled, the `.js` output of `ngc` does not include any lazy-loaded template or style URLs.
{@a enablelegacytemplate}
### `enableLegacyTemplate`
When true, enables use of the `<template>` element, which was deprecated in Angular 4.0, in favor of `<ng-template>` (to avoid colliding with the DOM's element of the same name). Default is false. Might be required by some third-party Angular libraries. |
### `flatModuleId`
The module ID to use for importing a flat module (when `flatModuleOutFile` is true). References generated by the template compiler use this module name when importing symbols
from the flat module. Ignored if `flatModuleOutFile` is false.
### `flatModuleOutFile`
When true, generates a flat module index of the given file name and the corresponding flat module metadata. Use to create flat modules that are packaged similarly to `@angular/core` and `@angular/common`. When this option is used, the `package.json` for the library should refer
to the generated flat module index instead of the library index file.
Produces only one `.metadata.json` file, which contains all the metadata necessary
for symbols exported from the library index. In the generated `.ngfactory.js` files, the flat
module index is used to import symbols that includes both the public API from the library index
as well as shrowded internal symbols.
By default the `.ts` file supplied in the `files` field is assumed to be the library index.
If more than one `.ts` file is specified, `libraryIndex` is used to select the file to use.
If more than one `.ts` file is supplied without a `libraryIndex`, an error is produced.
A flat module index `.d.ts` and `.js` is created with the given `flatModuleOutFile` name in the same location as the library index `.d.ts` file.
For example, if a library uses the `public_api.ts` file as the library index of the module, the `tsconfig.json` `files` field would be `["public_api.ts"]`.
The `flatModuleOutFile` options could then be set to (for example) `"index.js"`, which produces `index.d.ts` and `index.metadata.json` files.
The `module` field of the library's `package.json` would be `"index.js"` and the `typings` field
would be `"index.d.ts"`.
### `fullTemplateTypeCheck`
When true (recommended), enables the [binding expression validation](guide/aot-compiler#binding-expression-validation) phase of the template compiler, which uses TypeScript to validate binding expressions.
Default is currently false.
### `generateCodeForLibraries`
When true (the default), generates factory files (`.ngfactory.js` and `.ngstyle.js`)
for `.d.ts` files with a corresponding `.metadata.json` file.
When false, factory files are generated only for `.ts` files. Do this when using factory summaries.
### `preserveWhitespaces`
When false (the default), removes blank text nodes from compiled templates, which results in smaller emitted template factory modules. Set to true to preserve blank text nodes.
### `skipMetadataEmit`
When true, does not to produce `.metadata.json` files. Default is `false`.
The `.metadata.json` files contain information needed by the template compiler from a `.ts`
file that is not included in the `.d.ts` file produced by the TypeScript compiler.
This information includes, for example, the content of annotations (such as a component's template), which TypeScript emits to the `.js` file but not to the `.d.ts` file.
You can set to `true` when using factory summaries, because the factory summaries
include a copy of the information that is in the `.metadata.json` file.
Set to `true` if you are using TypeScript's `--outFile` option, because the metadata files
are not valid for this style of TypeScript output. However, we do not recommend using `--outFile` with Angular. Use a bundler, such as [webpack](https://webpack.js.org/), instead.
### `skipTemplateCodegen`
When true, does not emit `.ngfactory.js` and `.ngstyle.js` files. This turns off most of the template compiler and disables the reporting of template diagnostics.
Can be used to instruct the template compiler to produce `.metadata.json` files for distribution with an `npm` package while avoiding the production of `.ngfactory.js` and `.ngstyle.js` files that cannot be distributed to `npm`.
### `strictMetadataEmit`
When true, reports an error to the `.metadata.json` file if `"skipMetadataEmit"` is `false`.
Default is false. Use only when `"skipMetadataEmit"` is false and `"skipTemplateCodeGen"` is true.
This option is intended to validate the `.metadata.json` files emitted for bundling with an `npm` package. The validation is strict and can emit errors for metadata that would never produce an error when used by the template compiler. You can choose to suppress the error emitted by this option for an exported symbol by including `@dynamic` in the comment documenting the symbol.
It is valid for `.metadata.json` files to contain errors.
The template compiler reports these errors if the metadata is used to determine the contents of an annotation.
The metadata collector cannot predict the symbols that are designed for use in an annotation, so it preemptively includes error nodes in the metadata for the exported symbols.
The template compiler can then use the error nodes to report an error if these symbols are used.
If the client of a library intends to use a symbol in an annotation, the template compiler does not normally report this until the client uses the symbol.
This option allows detection of these errors during the build phase of
the library and is used, for example, in producing Angular libraries themselves.
### `strictInjectionParameters`
When true (recommended), reports an error for a supplied parameter whose injection type cannot be determined. When false (currently the default), constructor parameters of classes marked with `@Injectable` whose type cannot be resolved produce a warning.
### `trace`
When true, prints extra information while compiling templates. Default is false.

View File

@ -37,7 +37,8 @@ To get started with adding Angular animations to your project, import the animat
Import `BrowserAnimationsModule`, which introduces the animation capabilities into your Angular root application module.
<code-example path="animations/src/app/app.module.1.ts" header="src/app/app.module.ts" language="typescript"></code-example>
<code-example path="animations/src/app/app.module.1.ts" header="src/app/app.module.ts" language="typescript" linenums="false">
</code-example>
<div class="alert is-helpful">
@ -128,7 +129,7 @@ The third argument, `easing`, controls how the animation [accelerates and decele
<div class="alert is-helpful">
**Note:** See the Material Design website's topic on [Natural easing curves](https://material.io/design/motion/speed.html#easing) for general information on easing curves.
**Note:** See the Angular Material Design website's topic on [Natural easing curves](https://material.io/design/motion/speed.html#easing) for general information on easing curves.
</div>
This example provides a state transition from `open` to `closed` with a one second transition between states.
@ -179,7 +180,9 @@ In this example, we'll name the trigger `openClose`, and attach it to the `butto
Animations are defined in the metadata of the component that controls the HTML element to be animated. Put the code that defines your animations under the `animations:` property within the `@Component()` decorator.
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" language="typescript" region="component"></code-example>
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" language="typescript"
region="component" linenums="false">
</code-example>
When you've defined an animation trigger for a component, you can attach it to an element in that component's template by wrapping the trigger name in brackets and preceding it with an `@` symbol. Then, you can bind the trigger to a template expression using standard Angular property binding syntax as shown below, where `triggerName` is the name of the trigger, and `expression` evaluates to a defined animation state.

View File

@ -21,7 +21,7 @@ Angular offers two ways to compile your application:
1. **_Just-in-Time_ (JIT)**, which compiles your app in the browser at runtime.
1. **_Ahead-of-Time_ (AOT)**, which compiles your app at build time.
JIT compilation is the default when you run the [`ng build`](cli/build) (build only) or [`ng serve`](cli/serve) (build and serve locally) CLI commands:
JIT compilation is the default when you run the [`ng build`](cli/build) (build only) or [`ng serve`](cli/serve) (build and serve locally) CLI commands:
<code-example language="sh" class="code-shell">
ng build
@ -79,9 +79,11 @@ there are fewer opportunities for injection attacks.
When you use the Angular AOT compiler, you can control your app compilation in two ways:
* By [specifying Angular metadata](#metadata-aot), as described below.
* By providing template compiler options in the `tsconfig.json` file.
* By providing options in the `tsconfig.json` [TypeScript configuration file](guide/typescript-configuration). See [Angular compiler options](guide/angular-compiler-options).
For more information, see [Angular template compiler options](#compiler-options).
* By [specifying Angular metadata](#metadata-aot).
{@a metadata-aot}
@ -943,7 +945,7 @@ import { calculateValue } from './utilities';
To correct this error, export a function from the module and refer to the function in a `useFactory` provider instead.
<code-example>
<code-example linenums="false">
// CORRECTED
import { calculateValue } from './utilities';
@ -976,7 +978,7 @@ The compiler does not support references to variables assigned by [destructuring
For example, you cannot write something like this:
<code-example>
<code-example linenums="false">
// ERROR
import { configuration } from './configuration';
@ -992,7 +994,7 @@ const {foo, bar} = configuration;
To correct this error, refer to non-destructured values.
<code-example>
<code-example linenums="false">
// CORRECTED
import { configuration } from './configuration';
...
@ -1039,7 +1041,7 @@ you can finesse the problem in four steps:
Here's an illustrative example.
<code-example>
<code-example linenums="false">
// CORRECTED
import { Inject } from '@angular/core';
@ -1062,7 +1064,7 @@ uses the `@Inject(WINDOW)` to generate the injection code.
Angular does something similar with the `DOCUMENT` token so you can inject the browser's `document` object (or an abstraction of it, depending upon the platform in which the application runs).
<code-example>
<code-example linenums="false">
import { Inject } from '@angular/core';
import { DOCUMENT } from '@angular/platform-browser';
@ -1099,7 +1101,7 @@ that you referenced in metadata.
The compiler can understand simple enum values but not complex values such as those derived from computed properties.
<code-example>
<code-example linenums="false">
// ERROR
enum Colors {
Red = 1,
@ -1163,7 +1165,7 @@ Chuck: After reviewing your PR comment I'm still at a loss. See [comment there](
In the validation phase, the Angular template compiler uses the TypeScript compiler to validate the
binding expressions in templates. Enable this phase explicitly by adding the compiler
option `"fullTemplateTypeCheck"` in the `"angularCompilerOptions"` of the project's `tsconfig.json` (see
[Angular Compiler Options](guide/angular-compiler-options)).
[Angular Compiler Options](#compiler-options)).
Template validation produces error messages when a type error is detected in a template binding
expression, similar to how type errors are reported by the TypeScript compiler against code in a `.ts`
@ -1309,7 +1311,7 @@ Chuck: After reviewing your PR comment I'm still at a loss. See [comment there](
{@a tsconfig-extends}
## Configuration inheritance with extends
Similar to TypeScript Compiler, Angular Compiler also supports `extends` in the `tsconfig.json` on `angularCompilerOptions`. A tsconfig file can inherit configurations from another file using the `extends` property.
The `extends` is a top level property parallel to `compilerOptions` and `angularCompilerOptions`.
The `extends` is a top level property parallel to `compilerOptions` and `angularCompilerOptions`.
The configuration from the base file are loaded first, then overridden by those in the inheriting config file.
Example:
```json
@ -1327,3 +1329,198 @@ Similar to TypeScript Compiler, Angular Compiler also supports `extends` in the
}
```
More information about tsconfig extends can be found in the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
{@a compiler-options}
## Angular template compiler options
The template compiler options are specified as members of the `"angularCompilerOptions"` object in the `tsconfig.json` file. Specify template compiler options along with the options supplied to the TypeScript compiler as shown here:
```json
{
"compilerOptions": {
"experimentalDecorators": true,
...
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"preserveWhitespaces": true,
...
}
}
```
The following section describes the Angular's template compiler options.
### *enableResourceInlining*
This option instructs the compiler to replace the `templateUrl` and `styleUrls` property in all `@Component` decorators with inlined contents in `template` and `styles` properties.
When enabled, the `.js` output of `ngc` will have no lazy-loaded `templateUrl` or `styleUrls`.
### *skipMetadataEmit*
This option tells the compiler not to produce `.metadata.json` files.
The option is `false` by default.
`.metadata.json` files contain information needed by the template compiler from a `.ts`
file that is not included in the `.d.ts` file produced by the TypeScript compiler. This information contains,
for example, the content of annotations (such as a component's template), which TypeScript
emits to the `.js` file but not to the `.d.ts` file.
This option should be set to `true` if you are using TypeScript's `--outFile` option, because the metadata files
are not valid for this style of TypeScript output. It is not recommended to use `--outFile` with
Angular. Use a bundler, such as [webpack](https://webpack.js.org/), instead.
This option can also be set to `true` when using factory summaries because the factory summaries
include a copy of the information that is in the `.metadata.json` file.
### *strictMetadataEmit*
This option tells the template compiler to report an error to the `.metadata.json`
file if `"skipMetadataEmit"` is `false`. This option is `false` by default. This should only be used when `"skipMetadataEmit"` is `false` and `"skipTemplateCodeGen"` is `true`.
This option is intended to validate the `.metadata.json` files emitted for bundling with an `npm` package. The validation is strict and can emit errors for metadata that would never produce an error when used by the template compiler. You can choose to suppress the error emitted by this option for an exported symbol by including `@dynamic` in the comment documenting the symbol.
It is valid for `.metadata.json` files to contain errors. The template compiler reports these errors
if the metadata is used to determine the contents of an annotation. The metadata
collector cannot predict the symbols that are designed for use in an annotation, so it will preemptively
include error nodes in the metadata for the exported symbols. The template compiler can then use the error
nodes to report an error if these symbols are used. If the client of a library intends to use a symbol in an annotation, the template compiler will not normally report
this until the client uses the symbol. This option allows detecting these errors during the build phase of
the library and is used, for example, in producing Angular libraries themselves.
### *skipTemplateCodegen*
This option tells the compiler to suppress emitting `.ngfactory.js` and `.ngstyle.js` files. When set,
this turns off most of the template compiler and disables reporting template diagnostics.
This option can be used to instruct the
template compiler to produce `.metadata.json` files for distribution with an `npm` package while
avoiding the production of `.ngfactory.js` and `.ngstyle.js` files that cannot be distributed to
`npm`.
### *strictInjectionParameters*
When set to `true`, this options tells the compiler to report an error for a parameter supplied
whose injection type cannot be determined. When this option is not provided or is `false`, constructor parameters of classes marked with `@Injectable` whose type cannot be resolved will
produce a warning.
*Note*: It is recommended to change this option explicitly to `true` as this option will default to `true` in the future.
### *flatModuleOutFile*
When set to `true`, this option tells the template compiler to generate a flat module
index of the given file name and the corresponding flat module metadata. Use this option when creating
flat modules that are packaged similarly to `@angular/core` and `@angular/common`. When this option
is used, the `package.json` for the library should refer
to the generated flat module index instead of the library index file. With this
option only one `.metadata.json` file is produced, which contains all the metadata necessary
for symbols exported from the library index. In the generated `.ngfactory.js` files, the flat
module index is used to import symbols that includes both the public API from the library index
as well as shrowded internal symbols.
By default the `.ts` file supplied in the `files` field is assumed to be the library index.
If more than one `.ts` file is specified, `libraryIndex` is used to select the file to use.
If more than one `.ts` file is supplied without a `libraryIndex`, an error is produced. A flat module
index `.d.ts` and `.js` will be created with the given `flatModuleOutFile` name in the same
location as the library index `.d.ts` file. For example, if a library uses the
`public_api.ts` file as the library index of the module, the `tsconfig.json` `files` field
would be `["public_api.ts"]`. The `flatModuleOutFile` options could then be set to, for
example `"index.js"`, which produces `index.d.ts` and `index.metadata.json` files. The
library's `package.json`'s `module` field would be `"index.js"` and the `typings` field
would be `"index.d.ts"`.
### *flatModuleId*
This option specifies the preferred module id to use for importing a flat module.
References generated by the template compiler will use this module name when importing symbols
from the flat module.
This is only meaningful when `flatModuleOutFile` is also supplied. Otherwise the compiler ignores
this option.
### *generateCodeForLibraries*
This option tells the template compiler to generate factory files (`.ngfactory.js` and `.ngstyle.js`)
for `.d.ts` files with a corresponding `.metadata.json` file. This option defaults to
`true`. When this option is `false`, factory files are generated only for `.ts` files.
This option should be set to `false` when using factory summaries.
### *fullTemplateTypeCheck*
This option tells the compiler to enable the [binding expression validation](#binding-expression-validation)
phase of the template compiler which uses TypeScript to validate binding expressions.
This option is `false` by default.
*Note*: It is recommended to set this to `true` because this option will default to `true` in the future.
### *annotateForClosureCompiler*
This option tells the compiler to use [Tsickle](https://github.com/angular/tsickle) to annotate the emitted
JavaScript with [JSDoc](http://usejsdoc.org/) comments needed by the
[Closure Compiler](https://github.com/google/closure-compiler). This option defaults to `false`.
### *annotationsAs*
Use this option to modify how the Angular specific annotations are emitted to improve tree-shaking. Non-Angular
annotations and decorators are unaffected. Default is `static fields`.
<style>
td, th {vertical-align: top}
</style>
<table>
<tr>
<th>Value</th>
<th>Description</th>
</tr>
<tr>
<td><code>decorators</code></td>
<td>Leave the decorators in place. This makes compilation faster. TypeScript will emit calls to the __decorate helper. Use <code>--emitDecoratorMetadata</code> for runtime reflection. However, the resulting code will not properly tree-shake.</td>
</tr>
<tr>
<td><code>static fields</code></td>
<td>Replace decorators with a static field in the class. Allows advanced tree-shakers like
<a href="https://github.com/google/closure-compiler">Closure compiler</a> to remove unused classes.</td>
</tr>
</table>
### *trace*
This tells the compiler to print extra information while compiling templates.
### *enableLegacyTemplate*
Use of the `<template>` element was deprecated starting in Angular 4.0 in favor of using
`<ng-template>` to avoid colliding with the DOM's element of the same name. Setting this option to
`true` enables the use of the deprecated `<template>` element. This option
is `false` by default. This option might be required by some third-party Angular libraries.
### *disableExpressionLowering*
The Angular template compiler transforms code that is used, or could be used, in an annotation
to allow it to be imported from template factory modules. See
[metadata rewriting](#metadata-rewriting) for more information.
Setting this option to `false` disables this rewriting, requiring the rewriting to be
done manually.
### *disableTypeScriptVersionCheck*
When `true`, this option tells the compiler not to check the TypeScript version.
The compiler will skip checking and will not error out when an unsupported version of TypeScript is used.
Setting this option to `true` is not recommended because unsupported versions of TypeScript might have undefined behavior.
This option is `false` by default.
### *preserveWhitespaces*
This option tells the compiler whether to remove blank text nodes from compiled templates.
As of v6, this option is `false` by default, which results in smaller emitted template factory modules.
### *allowEmptyCodegenFiles*
Tells the compiler to generate all the possible generated files even if they are empty. This option is
`false` by default. This is an option used by the Bazel build rules and is needed to simplify
how Bazel rules track file dependencies. It is not recommended to use this option outside of the Bazel
rules.

View File

@ -10,7 +10,7 @@ Learn more in [The App Shell Model](https://developers.google.com/web/fundamenta
## Step 1: Prepare the application
You can do this with the following CLI command:
<code-example language="bash">
<code-example format="." language="bash" linenums="false">
ng new my-app --routing
</code-example>
@ -20,7 +20,7 @@ For an existing application, you have to manually add the `RouterModule` and def
Use the CLI to automatically create the app shell.
<code-example language="bash">
<code-example format="." language="bash" linenums="false">
ng generate app-shell --client-project my-app --universal-project server-app
</code-example>
@ -29,7 +29,7 @@ ng generate app-shell --client-project my-app --universal-project server-app
After running this command you will notice that the `angular.json` configuration file has been updated to add two new targets, with a few other changes.
<code-example language="json">
<code-example format="." language="json" linenums="false">
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
@ -52,7 +52,7 @@ After running this command you will notice that the `angular.json` configuration
Use the CLI to build the `app-shell` target.
<code-example language="bash">
<code-example format="." language="bash" linenums="false">
ng run my-app:app-shell
</code-example>

View File

@ -10,12 +10,12 @@ For example, individual components define and control each of the following view
You define a component's application logic&mdash;what it does to support the view&mdash;inside a class.
The class interacts with the view through an API of properties and methods.
For example, `HeroListComponent` has a `heroes` property that holds an array of heroes.
Its `selectHero()` method sets a `selectedHero` property when the user clicks to choose a hero from that list.
The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor.
For example, `HeroListComponent` has a `heroes` property that holds an array of heroes.
Its `selectHero()` method sets a `selectedHero` property when the user clicks to choose a hero from that list.
The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor.
The service is provided to the component through the dependency injection system.
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (class)" region="class"></code-example>
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" header="src/app/hero-list.component.ts (class)" region="class"></code-example>
Angular creates, updates, and destroys components as the user moves through the application. Your app can take action at each moment in this lifecycle through optional [lifecycle hooks](guide/lifecycle-hooks), like `ngOnInit()`.
@ -31,7 +31,7 @@ In addition to containing or pointing to the template, the `@Component` metadata
Here's an example of basic metadata for `HeroListComponent`.
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (metadata)" region="metadata"></code-example>
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" header="src/app/hero-list.component.ts (metadata)" region="metadata"></code-example>
This example shows some of the most useful `@Component` configuration options:
@ -40,7 +40,7 @@ Angular inserts an instance of the `HeroListComponent` view between those tags.
* `templateUrl`: The module-relative address of this component's HTML template. Alternatively, you can provide the HTML template inline, as the value of the `template` property. This template defines the component's *host view*.
* `providers`: An array of [providers](guide/glossary#provider) for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
* `providers`: An array of [providers](guide/glossary#provider) for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
## Templates and views
@ -69,7 +69,7 @@ This template uses typical HTML elements like `<h2>` and `<p>`, and also includ
* The `*ngFor` directive tells Angular to iterate over a list.
* `{{hero.name}}`, `(click)`, and `[hero]` bind program data to and from the DOM, responding to user input. See more about [data binding](#data-binding) below.
* The `<app-hero-detail>` tag in the example is an element that represents a new component, `HeroDetailComponent`.
* The `<app-hero-detail>` tag in the example is an element that represents a new component, `HeroDetailComponent`.
`HeroDetailComponent` (code not shown) defines the hero-detail child view of `HeroListComponent`.
Notice how custom components like this mix seamlessly with native HTML in the same layouts.
@ -87,7 +87,7 @@ The following diagram shows the four forms of data binding markup. Each form has
This example from the `HeroListComponent` template uses three of these forms.
<code-example path="architecture/src/app/hero-list.component.1.html" header="src/app/hero-list.component.html (binding)" region="binding"></code-example>
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" header="src/app/hero-list.component.html (binding)" region="binding"></code-example>
* The `{{hero.name}}` [*interpolation*](guide/displaying-data#interpolation)
displays the component's `hero.name` property value within the `<li>` element.
@ -97,11 +97,11 @@ displays the component's `hero.name` property value within the `<li>` element.
* The `(click)` [*event binding*](guide/user-input#binding-to-user-input-events) calls the component's `selectHero` method when the user clicks a hero's name.
Two-way data binding (used mainly in [template-driven forms](guide/forms))
combines property and event binding in a single notation.
Two-way data binding (used mainly in [template-driven forms](guide/forms))
combines property and event binding in a single notation.
Here's an example from the `HeroDetailComponent` template that uses two-way data binding with the `ngModel` directive.
<code-example path="architecture/src/app/hero-detail.component.html" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
In two-way binding, a data property value flows to the input box from the component as with property binding.
The user's changes also flow back to the component, resetting the property to the latest value,
@ -151,20 +151,20 @@ Angular templates are *dynamic*. When Angular renders them, it transforms the DO
A component is technically a directive.
However, components are so distinctive and central to Angular applications that Angular
defines the `@Component()` decorator, which extends the `@Directive()` decorator with
defines the `@Component()` decorator, which extends the `@Directive()` decorator with
template-oriented features.
In addition to components, there are two other kinds of directives: *structural* and *attribute*.
In addition to components, there are two other kinds of directives: *structural* and *attribute*.
Angular defines a number of directives of both kinds, and you can define your own using the `@Directive()` decorator.
Just as for components, the metadata for a directive associates the decorated class with a `selector` element that you use to insert it into HTML. In templates, directives typically appear within an element tag as attributes, either by name or as the target of an assignment or a binding.
#### Structural directives
*Structural directives* alter layout by adding, removing, and replacing elements in the DOM.
*Structural directives* alter layout by adding, removing, and replacing elements in the DOM.
The example template uses two built-in structural directives to add application logic to how the view is rendered.
<code-example path="architecture/src/app/hero-list.component.1.html" header="src/app/hero-list.component.html (structural)" region="structural"></code-example>
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" header="src/app/hero-list.component.html (structural)" region="structural"></code-example>
* [`*ngFor`](guide/displaying-data#ngFor) is an iterative; it tells Angular to stamp out one `<li>` per hero in the `heroes` list.
* [`*ngIf`](guide/displaying-data#ngIf) is a conditional; it includes the `HeroDetail` component only if a selected hero exists.
@ -176,7 +176,7 @@ In templates they look like regular HTML attributes, hence the name.
The `ngModel` directive, which implements two-way data binding, is an example of an attribute directive. `ngModel` modifies the behavior of an existing element (typically `<input>`) by setting its display value property and responding to change events.
<code-example path="architecture/src/app/hero-detail.component.html" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
Angular has more pre-defined directives that either alter the layout structure
(for example, [ngSwitch](guide/template-syntax#ngSwitch))

View File

@ -23,7 +23,7 @@ An NgModule is defined by a class decorated with `@NgModule()`. The `@NgModule()
Here's a simple root NgModule definition.
<code-example path="architecture/src/app/mini-app.ts" region="module" header="src/app/app.module.ts"></code-example>
<code-example path="architecture/src/app/mini-app.ts" region="module" header="src/app/app.module.ts" linenums="false"></code-example>
<div class="alert is-helpful">
@ -69,9 +69,9 @@ In JavaScript each *file* is a module and all objects defined in the file belong
The module declares some objects to be public by marking them with the `export` key word.
Other JavaScript modules use *import statements* to access public objects from other modules.
<code-example path="architecture/src/app/app.module.ts" region="imports"></code-example>
<code-example path="architecture/src/app/app.module.ts" region="imports" linenums="false"></code-example>
<code-example path="architecture/src/app/app.module.ts" region="export"></code-example>
<code-example path="architecture/src/app/app.module.ts" region="export" linenums="false"></code-example>
<div class="alert is-helpful">
<a href="http://exploringjs.com/es6/ch_modules.html">Learn more about the JavaScript module system on the web.</a>
@ -87,17 +87,17 @@ Angular loads as a collection of JavaScript modules. You can think of them as li
For example, import Angular's `Component` decorator from the `@angular/core` library like this.
<code-example path="architecture/src/app/app.component.ts" region="import"></code-example>
<code-example path="architecture/src/app/app.component.ts" region="import" linenums="false"></code-example>
You also import NgModules from Angular *libraries* using JavaScript import statements.
For example, the following code imports the `BrowserModule` NgModule from the `platform-browser` library.
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module"></code-example>
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false"></code-example>
In the example of the simple root module above, the application module needs material from within
`BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this.
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports"></code-example>
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports" linenums="false"></code-example>
In this way you're using the Angular and JavaScript module systems *together*. Although it's easy to confuse the two systems, which share the common vocabulary of "imports" and "exports", you will become familiar with the different contexts in which they are used.

View File

@ -1,7 +1,7 @@
# Introduction to services and dependency injection
*Service* is a broad category encompassing any value, function, or feature that an app needs.
A service is typically a class with a narrow, well-defined purpose.
A service is typically a class with a narrow, well-defined purpose.
It should do something specific and do it well.
Angular distinguishes components from services to increase modularity and reusability.
@ -14,9 +14,9 @@ in order to mediate between the view (rendered by the template)
and the application logic (which often includes some notion of a *model*).
A component can delegate certain tasks to services, such as fetching data from the server,
validating user input, or logging directly to the console.
validating user input, or logging directly to the console.
By defining such processing tasks in an *injectable service class*, you make those tasks
available to any component.
available to any component.
You can also make your app more adaptable by injecting different providers of the same kind of service,
as appropriate in different circumstances.
@ -28,21 +28,21 @@ available to components through *dependency injection*.
Here's an example of a service class that logs to the browser console.
<code-example path="architecture/src/app/logger.service.ts" header="src/app/logger.service.ts (class)" region="class"></code-example>
<code-example path="architecture/src/app/logger.service.ts" linenums="false" header="src/app/logger.service.ts (class)" region="class"></code-example>
Services can depend on other services. For example, here's a `HeroService` that depends on the `Logger` service, and also uses `BackendService` to get heroes. That service in turn might depend on the `HttpClient` service to fetch heroes asynchronously from a server.
<code-example path="architecture/src/app/hero.service.ts" header="src/app/hero.service.ts (class)" region="class"></code-example>
<code-example path="architecture/src/app/hero.service.ts" linenums="false" header="src/app/hero.service.ts (class)" region="class"></code-example>
## Dependency injection (DI)
<img src="generated/images/guide/architecture/dependency-injection.png" alt="Service" class="left">
DI is wired into the Angular framework and used everywhere to provide new components with the services or other things they need.
Components consume services; that is, you can *inject* a service into a component, giving the component access to that service class.
Components consume services; that is, you can *inject* a service into a component, giving the component access to that service class.
To define a class as a service in Angular, use the `@Injectable()` decorator to provide the metadata that allows Angular to inject it into a component as a *dependency*.
Similarly, use the `@Injectable()` decorator to indicate that a component or other class (such as another service, a pipe, or an NgModule) *has* a dependency.
To define a class as a service in Angular, use the `@Injectable()` decorator to provide the metadata that allows Angular to inject it into a component as a *dependency*.
Similarly, use the `@Injectable()` decorator to indicate that a component or other class (such as another service, a pipe, or an NgModule) *has* a dependency.
* The *injector* is the main mechanism. Angular creates an application-wide injector for you during the bootstrap process, and additional injectors as needed. You don't have to create injectors.
@ -50,19 +50,19 @@ Similarly, use the `@Injectable()` decorator to indicate that a component or oth
* A *provider* is an object that tells an injector how to obtain or create a dependency.
For any dependency that you need in your app, you must register a provider with the app's injector,
so that the injector can use the provider to create new instances.
For any dependency that you need in your app, you must register a provider with the app's injector,
so that the injector can use the provider to create new instances.
For a service, the provider is typically the service class itself.
<div class="alert is-helpful">
A dependency doesn't have to be a service&mdash;it could be a function, for example, or a value.
A dependency doesn't have to be a service&mdash;it could be a function, for example, or a value.
</div>
When Angular creates a new instance of a component class, it determines which services or other dependencies that component needs by looking at the constructor parameter types. For example, the constructor of `HeroListComponent` needs `HeroService`.
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (constructor)" region="ctor"></code-example>
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" header="src/app/hero-list.component.ts (constructor)" region="ctor"></code-example>
When Angular discovers that a component depends on a service, it first checks if the injector has any existing instances of that service. If a requested service instance doesn't yet exist, the injector makes one using the registered provider, and adds it to the injector before returning the service to Angular.
@ -78,26 +78,26 @@ The process of `HeroService` injection looks something like this.
You must register at least one *provider* of any service you are going to use.
The provider can be part of the service's own metadata, making that service available everywhere,
or you can register providers with specific modules or components.
or you can register providers with specific modules or components.
You register providers in the metadata of the service (in the `@Injectable()` decorator),
or in the `@NgModule()` or `@Component()` metadata
or in the `@NgModule()` or `@Component()` metadata
* By default, the Angular CLI command [`ng generate service`](cli/generate) registers a provider with the root injector for your service by including provider metadata in the `@Injectable()` decorator. The tutorial uses this method to register the provider of HeroService class definition.
```
```
@Injectable({
providedIn: 'root',
})
```
```
When you provide the service at the root level, Angular creates a single, shared instance of `HeroService`
and injects it into any class that asks for it.
and injects it into any class that asks for it.
Registering the provider in the `@Injectable()` metadata also allows Angular to optimize an app
by removing the service from the compiled app if it isn't used.
by removing the service from the compiled app if it isn't used.
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator,
```
```
@NgModule({
providers: [
BackendService,
@ -105,12 +105,12 @@ or in the `@NgModule()` or `@Component()` metadata
],
...
})
```
```
* When you register a provider at the component level, you get a new instance of the
service with each new instance of that component.
service with each new instance of that component.
At the component level, register a service provider in the `providers` property of the `@Component()` metadata.
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (component providers)" region="providers"></code-example>
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" header="src/app/hero-list.component.ts (component providers)" region="providers"></code-example>
For more detailed information, see the [Dependency Injection](guide/dependency-injection) section.

View File

@ -37,13 +37,13 @@ This page demonstrates building a simple _appHighlight_ attribute
directive to set an element's background color
when the user hovers over that element. You can apply it like this:
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html (applied)" region="applied"></code-example>
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html (applied)" region="applied"></code-example>
{@a write-directive}
Please note that directives _do not_ support namespaces.
<code-example path="attribute-directives/src/app/app.component.avoid.html" header="src/app/app.component.avoid.html (unsupported)" region="unsupported"></code-example>
<code-example path="attribute-directives/src/app/app.component.avoid.html" linenums="false" header="src/app/app.component.avoid.html (unsupported)" region="unsupported"></code-example>
### Write the directive code
@ -101,7 +101,7 @@ Now edit the generated `src/app/highlight.directive.ts` to look as follows:
The `import` statement specifies an additional `ElementRef` symbol from the Angular `core` library:
You use the `ElementRef` in the directive's constructor
to [inject](guide/dependency-injection) a reference to the host DOM element,
to [inject](guide/dependency-injection) a reference to the host DOM element,
the element to which you applied `appHighlight`.
`ElementRef` grants direct access to the host DOM element
@ -140,12 +140,12 @@ and respond by setting or clearing the highlight color.
Begin by adding `HostListener` to the list of imported symbols.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
Then add two eventhandlers that respond when the mouse enters or leaves,
each adorned by the `HostListener` decorator.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"></code-example>
The `@HostListener` decorator lets you subscribe to events of the DOM
element that hosts an attribute directive, the `<p>` in this case.
@ -166,7 +166,7 @@ The handlers delegate to a helper method that sets the color on the host DOM ele
The helper method, `highlight`, was extracted from the constructor.
The revised constructor simply declares the injected `el: ElementRef`.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (constructor)" region="ctor"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (constructor)" region="ctor"></code-example>
Here's the updated directive in full:
@ -187,11 +187,11 @@ Currently the highlight color is hard-coded _within_ the directive. That's infle
In this section, you give the developer the power to set the highlight color while applying the directive.
Begin by adding `Input` to the list of symbols imported from `@angular/core`.
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
Add a `highlightColor` property to the directive class like this:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (highlightColor)" region="color"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (highlightColor)" region="color"></code-example>
{@a input}
@ -204,19 +204,19 @@ Without that input metadata, Angular rejects the binding; see [below](guide/attr
Try it by adding the following directive binding variations to the `AppComponent` template:
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html (excerpt)" region="color-1"></code-example>
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html (excerpt)" region="color-1"></code-example>
Add a `color` property to the `AppComponent`.
<code-example path="attribute-directives/src/app/app.component.1.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
<code-example path="attribute-directives/src/app/app.component.1.ts" linenums="false" header="src/app/app.component.ts (class)" region="class"></code-example>
Let it control the highlight color with a property binding.
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html (excerpt)" region="color-2"></code-example>
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html (excerpt)" region="color-2"></code-example>
That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this.
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (color)" region="color"></code-example>
The `[appHighlight]` attribute binding both applies the highlighting directive to the `<p>` element
and sets the directive's highlight color with a property binding.
@ -225,7 +225,7 @@ That's a crisp, compact syntax.
You'll have to rename the directive's `highlightColor` property to `appHighlight` because that's now the color property binding name.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2"></code-example>
This is disagreeable. The word, `appHighlight`, is a terrible property name and it doesn't convey the property's intent.
@ -237,23 +237,23 @@ Fortunately you can name the directive property whatever you want _and_ **_alias
Restore the original property name and specify the selector as the alias in the argument to `@Input`.
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (color property with alias)" region="color"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" header="src/app/highlight.directive.ts (color property with alias)" region="color"></code-example>
_Inside_ the directive the property is known as `highlightColor`.
_Outside_ the directive, where you bind to it, it's known as `appHighlight`.
You get the best of both worlds: the property name you want and the binding syntax you want:
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (color)" region="color"></code-example>
Now that you're binding via the alias to the `highlightColor`, modify the `onMouseEnter()` method to use that property.
If someone neglects to bind to `appHighlightColor`, highlight the host element in red:
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" header="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"></code-example>
Here's the latest version of the directive class.
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (excerpt)"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" header="src/app/highlight.directive.ts (excerpt)"></code-example>
## Write a harness to try it
@ -263,11 +263,11 @@ lets you pick the highlight color with a radio button and bind your color choice
Update <code>app.component.html</code> as follows:
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (v2)" region="v2"></code-example>
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (v2)" region="v2"></code-example>
Revise the `AppComponent.color` so that it has no initial value.
<code-example path="attribute-directives/src/app/app.component.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
<code-example path="attribute-directives/src/app/app.component.ts" linenums="false" header="src/app/app.component.ts (class)" region="class"></code-example>
Here are the harness and directive in action.
@ -287,12 +287,12 @@ Let the template developer set the default color.
Add a second **input** property to `HighlightDirective` called `defaultColor`:
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (defaultColor)" region="defaultColor"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" header="src/app/highlight.directive.ts (defaultColor)" region="defaultColor"></code-example>
Revise the directive's `onMouseEnter` so that it first tries to highlight with the `highlightColor`,
then with the `defaultColor`, and falls back to "red" if both properties are undefined.
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" header="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter"></code-example>
How do you bind to a second property when you're already binding to the `appHighlight` attribute name?
@ -300,7 +300,7 @@ As with components, you can add as many directive property bindings as you need
The developer should be able to write the following template HTML to both bind to the `AppComponent.color`
and fall back to "violet" as the default color.
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (defaultColor)" region="defaultColor"></code-example>
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (defaultColor)" region="defaultColor"></code-example>
Angular knows that the `defaultColor` binding belongs to the `HighlightDirective`
because you made it _public_ with the `@Input` decorator.
@ -342,11 +342,11 @@ You can also experience and download the <live-example title="Attribute Directiv
In this demo, the `highlightColor` property is an ***input*** property of
the `HighlightDirective`. You've seen it applied without an alias:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
You've seen it with an alias:
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
Either way, the `@Input` decorator tells Angular that this property is
_public_ and available for binding by a parent component.
@ -378,7 +378,7 @@ You can tell if `@Input` is needed by the position of the property name in a bin
Now apply that reasoning to the following example:
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (color)" region="color"></code-example>
* The `color` property in the expression on the right belongs to the template's component.
The template and its component trust each other.

View File

@ -106,16 +106,19 @@ To use a directive, component, or pipe in a module, you must do a few things:
Those three steps look like the following. In the file where you create your directive, export it.
The following example, named `ItemDirective` is the default directive structure that the CLI generates in its own file, `item.directive.ts`:
<code-example path="bootstrapping/src/app/item.directive.ts" region="directive" header="src/app/item.directive.ts"></code-example>
<code-example path="bootstrapping/src/app/item.directive.ts" region="directive" header="src/app/item.directive.ts" linenums="false">
</code-example>
The key point here is that you have to export it so you can import it elsewhere. Next, import it
into the NgModule, in this example `app.module.ts`, with a JavaScript import statement:
<code-example path="bootstrapping/src/app/app.module.ts" region="directive-import" header="src/app/app.module.ts"></code-example>
<code-example path="bootstrapping/src/app/app.module.ts" region="directive-import" header="src/app/app.module.ts" linenums="false">
</code-example>
And in the same file, add it to the `@NgModule` `declarations` array:
<code-example path="bootstrapping/src/app/app.module.ts" region="declarations" header="src/app/app.module.ts"></code-example>
<code-example path="bootstrapping/src/app/app.module.ts" region="declarations" header="src/app/app.module.ts" linenums="false">
</code-example>
Now you could use your `ItemDirective` in a component. This example uses `AppModule`, but you'd do it the same way for a feature module. For more about directives, see [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives). You'd also use the same technique for [pipes](guide/pipes) and components.

View File

@ -53,7 +53,7 @@ Angular supports most recent browsers. This includes the following specific vers
IE
</td>
<td>
11, 10, 9 ("compatibility view" mode not supported)
11<br>10<br>9
</td>
</tr>
<tr>
@ -89,7 +89,7 @@ Angular supports most recent browsers. This includes the following specific vers
</td>
<td>
Nougat (7.0), Marshmallow (6.0), Lollipop (5.0, 5.1), KitKat (4.4)
Nougat (7.0)<br>Marshmallow (6.0)<br>Lollipop (5.0, 5.1)<br>KitKat (4.4)
</td>
</tr>
@ -127,27 +127,25 @@ In Angular CLI version 8 and higher, applications are built using *differential
This strategy allows you to continue to build your web application to support multiple browsers, but only load the necessary code that the browser needs.
For more information about how this works, see [Differential Loading](guide/deployment#differential-loading) in the [Deployment guide](guide/deployment).
## Enabling polyfills with CLI projects
## Enabling polyfills
The [Angular CLI](cli) provides support for polyfills.
If you are not using the CLI to create your projects, see [Polyfill instructions for non-CLI users](#non-cli).
[Angular CLI](cli) users enable polyfills through the `src/polyfills.ts` file that
the CLI created with your project.
When you create a project with the `ng new` command, a `src/polyfills.ts` configuration file is created as part of your project folder.
This file incorporates the mandatory and many of the optional polyfills as JavaScript `import` statements.
* The npm packages for the [_mandatory_ polyfills](#polyfill-libs) (such as `zone.js`) are installed automatically for you when you create your project with `ng new`, and their corresponding `import` statements are already enabled in the `src/polyfills.ts` configuration file.
The npm packages for the _mandatory_ polyfills (such as `zone.js`) were installed automatically for you when you created your project and their corresponding `import` statements are ready to go. You probably won't touch these.
* If you need an _optional_ polyfill, you must install its npm package, then uncomment or create the corresponding import statement in the `src/polyfills.ts` configuration file.
For example, if you need the optional [web animations polyfill](http://caniuse.com/#feat=web-animation), you could install it with `npm`, using the following command (or the `yarn` equivalent):
But if you need an optional polyfill, you'll have to install its npm package.
For example, [if you need the web animations polyfill](http://caniuse.com/#feat=web-animation), you could install it with `npm`, using the following command (or the `yarn` equivalent):
<code-example language="sh" class="code-shell">
# install the optional web animations polyfill
# note that the web-animations-js polyfill is only here as an example
# it isn't a strict requirement of Angular anymore (more below)
npm install --save web-animations-js
</code-example>
You can then add the import statement in the `src/polyfills.ts` file.
For many polyfills, you can simply un-comment the corresponding `import` statement in the file, as in the following example.
Then open the `polyfills.ts` file and un-comment the corresponding `import` statement as in the following example:
<code-example header="src/polyfills.ts">
/**
@ -157,14 +155,23 @@ For many polyfills, you can simply un-comment the corresponding `import` stateme
import 'web-animations-js'; // Run `npm install --save web-animations-js`.
</code-example>
If the polyfill you want is not already in `polyfills.ts` file, add the `import` statement by hand.
If you can't find the polyfill you want in `polyfills.ts`,
add it yourself, following the same pattern:
1. install the npm package
1. `import` the file in `polyfills.ts`
<div class="alert is-helpful">
Non-CLI users should follow the instructions [below](#non-cli).
</div>
{@a polyfill-libs}
### Mandatory polyfills
These are the polyfills required to run an Angular application on each supported browser:
<table>
<tr style="vertical-align: top">
@ -182,13 +189,12 @@ These are the polyfills required to run an Angular application on each supported
<tr style="vertical-align: top">
<td>
Chrome, Firefox, Edge, <br>
Safari, Android, IE 10+
Chrome, Firefox, Edge, Safari 9+
</td>
<td>
[ES2015](guide/browser-support#core-es6)
[ES7/reflect](guide/browser-support#core-es7-reflect) (JIT only)
</td>
@ -197,12 +203,26 @@ These are the polyfills required to run an Angular application on each supported
<tr style="vertical-align: top">
<td>
IE 9
Safari 7 & 8, IE10 & 11, Android 4.1+
</td>
<td>
ES2015<br>[classList](guide/browser-support#classlist)
[ES6](guide/browser-support#core-es6)
</td>
</tr>
<tr style="vertical-align: top">
<td>
IE9
</td>
<td>
[ES6<br>classList](guide/browser-support#classlist)
</td>
@ -215,6 +235,12 @@ These are the polyfills required to run an Angular application on each supported
Some features of Angular may require additional polyfills.
For example, the animations library relies on the standard web animation API, which is only available in Chrome and Firefox today.
(note that the dependency of web-animations-js in Angular is only necessary if `AnimationBuilder` is used.)
Here are the features which may require additional polyfills:
<table>
<tr style="vertical-align: top">
@ -237,8 +263,31 @@ Some features of Angular may require additional polyfills.
<td>
[AnimationBuilder](api/animations/AnimationBuilder).
(Standard animation support does not require polyfills.)
[JIT compilation](guide/aot-compiler).
Required to reflect for metadata.
</td>
<td>
[ES7/reflect](guide/browser-support#core-es7-reflect)
</td>
<td>
All current browsers. Enabled by default.
Can remove if you always use AOT and only use Angular decorators.
</td>
</tr>
<tr style="vertical-align: top">
<td>
[Animations](guide/animations)
<br>Only if `Animation Builder` is used within the application--standard
animation support in Angular doesn't require any polyfills (as of NG6).
</td>
@ -249,9 +298,8 @@ Some features of Angular may require additional polyfills.
</td>
<td>
<p>If AnimationBuilder is used, enables scrubbing
support for IE/Edge and Safari.
(Chrome and Firefox support this natively).</p>
<p>If AnimationBuilder is used then the polyfill will enable scrubbing
support for IE/Edge and Safari (Chrome and Firefox support this natively).</p>
</td>
</tr>
@ -260,10 +308,15 @@ Some features of Angular may require additional polyfills.
<td>
If you use the following deprecated i18n pipes:
If you use the following deprecated i18n pipes:
[date](api/common/DeprecatedDatePipe),
[currency](api/common/DeprecatedCurrencyPipe),
[decimal](api/common/DeprecatedDecimalPipe),
[percent](api/common/DeprecatedPercentPipe)
</td>
@ -275,7 +328,7 @@ Some features of Angular may require additional polyfills.
</td>
<td>
All but Chrome, Firefox, Edge, IE 11 and Safari 10
All but Chrome, Firefox, Edge, IE11 and Safari 10
</td>
</tr>
@ -284,7 +337,9 @@ Some features of Angular may require additional polyfills.
<td>
[NgClass](api/common/NgClass) on SVG elements
[NgClass](api/common/NgClass)
on SVG elements
</td>
<td>
@ -294,7 +349,7 @@ Some features of Angular may require additional polyfills.
</td>
<td>
IE 10, IE 11
IE10, IE11
</td>
</tr>
@ -303,7 +358,9 @@ Some features of Angular may require additional polyfills.
<td>
[Http](guide/http) when sending and receiving binary data
[Http](guide/http)
when sending and receiving binary data
</td>
<td>
@ -326,8 +383,9 @@ Some features of Angular may require additional polyfills.
<td>
[Router](guide/router) when using
[hash-based routing](guide/router#appendix-locationstrategy-and-browser-url-styles)
[Router](guide/router)
when using [hash-based routing](guide/router#appendix-locationstrategy-and-browser-url-styles)
</td>
<td>
@ -346,9 +404,8 @@ Some features of Angular may require additional polyfills.
### Suggested polyfills
The following polyfills are used to test the framework itself. They are a good starting point for an application.
### Suggested polyfills ##
Below are the polyfills which are used to test the framework itself. They are a good starting point for an application.
<table>
@ -369,6 +426,24 @@ The following polyfills are used to test the framework itself. They are a good s
</tr>
<tr>
<td>
<a id='core-es7-reflect' href="https://github.com/zloirock/core-js/tree/v2/fn/reflect">ES7/reflect</a>
</td>
<td>
MIT
</td>
<td>
0.5KB
</td>
</tr>
<tr>
<td>
@ -391,7 +466,7 @@ The following polyfills are used to test the framework itself. They are a good s
<td>
<a id='core-es6' href="https://github.com/zloirock/core-js">ES2015</a>
<a id='core-es6' href="https://github.com/zloirock/core-js">ES6</a>
</td>
@ -520,14 +595,11 @@ The following polyfills are used to test the framework itself. They are a good s
computed with the <a href="http://closure-compiler.appspot.com/home">closure compiler</a>.
{@a non-cli}
## Polyfills for non-CLI users
If you are not using the CLI, add your polyfill scripts directly to the host web page (`index.html`).
If you are not using the CLI, you should add your polyfill scripts directly to the host web page (`index.html`), perhaps like this.
For example:
<code-example header="src/index.html" language="html">
<code-example header="src/index.html">
&lt;!-- pre-zone polyfills -->
&lt;script src="node_modules/core-js/client/shim.min.js">&lt;/script>
&lt;script src="node_modules/web-animations-js/web-animations.min.js">&lt;/script>

View File

@ -214,17 +214,14 @@ Each budget entry is a JSON object with the following properties:
<tr>
<td>type</td>
<td>
<td>The type of budget. One of:
The type of budget. One of:
* `bundle` - The size of a specific bundle.
* `initial` - The initial size of the app.
* `allScript` - The size of all scripts.
* `all` - The size of the entire app.
* `anyComponentStyle` - This size of any one component stylesheet.
* `anyScript` - The size of any one script.
* `any` - The size of any file.
* bundle - The size of a specific bundle.
* initial - The initial size of the app.
* allScript - The size of all scripts.
* all - The size of the entire app.
* anyScript - The size of any one script.
* any - The size of any file.
</td>
</tr>

View File

@ -185,7 +185,8 @@ is available to <code>declarations</code> of this module.</p>
</td>
</tr><tr>
<td><code><b>@Injectable()</b><br>class MyService() {}</code></td>
<td><p>Declares that a class can be provided and injected by other classes. Without this decorator, the compiler won't generate enough metadata to allow the class to be created properly when it's injected somewhere.</p>
<td><p>Declares that a class has dependencies that should be injected into the constructor when the dependency injector is creating an instance of this class.
</p>
</td>
</tr>
</tbody></table>

View File

@ -191,7 +191,7 @@ For our example builder, we expect the `options` value to be a `JsonObject` with
We can provide the following schema for type validation of these values.
<code-example language="json">
<code-example format="." language="json" linenums="false">
{
"$schema": "http://json-schema.org/schema",
@ -222,7 +222,7 @@ To link our builder implementation with its schema and name, we need to create a
Create a file named `builders.json` file that looks like this.
<code-example language="json">
<code-example format="." language="json" linenums="false">
{
"builders": {
@ -238,7 +238,7 @@ Create a file named `builders.json` file that looks like this.
In the `package.json` file, add a `builders` key that tells the Architect tool where to find our builder definition file.
<code-example language="json">
<code-example format="." language="json" linenums="false">
{
"name": "@example/command-runner",
@ -253,11 +253,11 @@ In the `package.json` file, add a `builders` key that tells the Architect tool w
</code-example>
The official name of our builder is now ` @example/command-runner:command`.
The first part of this is the package name (resolved using node resolution), and the second part is the builder name (resolved using the `builders.json` file).
The first part of this is the package name (resolved using node resolution), and the second part is the builder name (resolved using the `builder.json` file).
Using one of our `options` is very straightforward, we did this in the previous section when we accessed `options.command`.
<code-example language="typescript">
<code-example format="." language="typescript" linenums="false">
context.reportStatus(`Executing "${options.command}"...`);
const child = childProcess.spawn(options.command, options.args, { stdio: 'pipe' });
@ -274,33 +274,33 @@ The Architect tool uses the target definition to resolve input options for a giv
The `angular.json` file has a section for each project, and the "architect" section of each project configures targets for builders used by CLI commands such as 'build', 'test', and 'lint'.
By default, for example, the `build` command runs the builder `@angular-devkit/build-angular:browser` to perform the build task, and passes in default option values as specified for the `build` target in `angular.json`.
<code-example language="json">
<code-example format="." language="json" linenums="false">
{
"myApp": {
...
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/myApp",
"index": "src/index.html",
...
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
...
}
}
},
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/myApp",
"index": "src/index.html",
...
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
...
}
}
},
...
</code-example>
@ -361,7 +361,7 @@ npm install @example/command-runner
If we create a new project with `ng new builder-test`, the generated `angular.json` file looks something like this, with only default builder configurations.
<code-example language="json">
<code-example format="." language="json" linenums="false">
{
// ...
@ -413,19 +413,21 @@ We need to update the `angular.json` file to add a target for this builder to th
* The configurations key is optional, we'll leave it out for now.
<code-example language="json">
<code-example format="." language="json" linenums="false">
{
"projects": {
"builder-test": {
"architect": {
"touch": {
"builder": "@example/command-runner:command",
"options": {
"command": "touch",
"args": [
"src/main.ts"
]
"builder-test": {
"touch": {
"builder": "@example/command-runner:command",
"options": {
"command": "touch",
"args": [
"src/main.ts"
]
}
}
},
"build": {
@ -493,16 +495,16 @@ Use integration testing for your builder, so that you can use the Architect sche
Heres an example of a test that runs the command builder.
The test uses the builder to run the `ls` command, then validates that it ran successfully and listed the proper files.
<code-example language="typescript">
<code-example format="." language="typescript" linenums="false">
import { Architect } from '@angular-devkit/architect';
import { Architect, ArchitectHost } from '@angular-devkit/architect';
import { TestingArchitectHost } from '@angular-devkit/architect/testing';
// Our builder forwards the STDOUT of the command to the logger.
import { logging, schema } from '@angular-devkit/core';
describe('Command Runner Builder', () => {
let architect: Architect;
let architectHost: TestingArchitectHost;
let architectHost: ArchitectHost;
beforeEach(async () => {
const registry = new schema.CoreSchemaRegistry();
@ -590,4 +592,4 @@ The CLI Builder API provides a new way of changing the behavior of the Angular C
* We recommend that you use integration tests to test Architect builders. You can use unit tests to validate the logic that the builder executes.
* If your builder returns an Observable, it should clean up in the teardown logic of that Observable.
* If your builder returns an Observable, it should clean up in the teardown logic of that Observable.

View File

@ -38,7 +38,7 @@ The following example demonstrates how to use `query()` and `stagger()` function
* Animate each element on screen for 0.5 seconds using a custom-defined easing curve, simultaneously fading it in and un-transforming it.
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="page-animations" language="typescript"></code-example>
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="page-animations" language="typescript" linenums="false"></code-example>
## Parallel animation using group() function
@ -51,7 +51,7 @@ You've seen how to add a delay between each successive animation. But you may al
In the following example, using groups on both `:enter` and `:leave` allow for two different timing configurations. They're applied to the same element in parallel, but run independently.
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" header="src/app/hero-list-groups.component.ts (excerpt)" language="typescript"></code-example>
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" header="src/app/hero-list-groups.component.ts (excerpt)" language="typescript" linenums="false"></code-example>
## Sequential vs. parallel animations
@ -74,7 +74,7 @@ The HTML template contains a trigger called `filterAnimation`.
The component file contains three transitions.
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="filter-animations" language="typescript"></code-example>
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="filter-animations" language="typescript" linenums="false"></code-example>
The animation does the following:
@ -101,4 +101,4 @@ You may also be interested in the following:
* [Introduction to Angular animations](guide/animations)
* [Transition and triggers](guide/transition-and-triggers)
* [Reusable animations](guide/reusable-animations)
* [Route transition animations](guide/route-animations)
* [Route transition animations](guide/route-animations)

View File

@ -21,7 +21,8 @@ One way to do this is to set the `styles` property in the component metadata.
The `styles` property takes an array of strings that contain CSS code.
Usually you give it one string, as in the following example:
<code-example path="component-styles/src/app/hero-app.component.ts" header="src/app/hero-app.component.ts"></code-example>
<code-example path="component-styles/src/app/hero-app.component.ts" header="src/app/hero-app.component.ts" linenums="false">
</code-example>
## Style scope
@ -70,7 +71,8 @@ Use the `:host` pseudo-class selector to target styles in the element that *host
targeting elements *inside* the component's template).
<code-example path="component-styles/src/app/hero-details.component.css" region="host" header="src/app/hero-details.component.css"></code-example>
<code-example path="component-styles/src/app/hero-details.component.css" region="host" header="src/app/hero-details.component.css" linenums="false">
</code-example>
The `:host` selector is the only way to target the host element. You can't reach
the host element from inside the component with other selectors because it's not part of the
@ -81,7 +83,8 @@ including another selector inside parentheses after `:host`.
The next example targets the host element again, but only when it also has the `active` CSS class.
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" header="src/app/hero-details.component.css"></code-example>
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" header="src/app/hero-details.component.css" linenums="false">
</code-example>
### :host-context
@ -96,7 +99,8 @@ up to the document root. The `:host-context()` selector is useful when combined
The following example applies a `background-color` style to all `<h2>` elements *inside* the component, only
if some ancestor element has the CSS class `theme-light`.
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" header="src/app/hero-details.component.css"></code-example>
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" header="src/app/hero-details.component.css" linenums="false">
</code-example>
### (deprecated) `/deep/`, `>>>`, and `::ng-deep`
@ -111,7 +115,9 @@ can bleed into other components.
The following example targets all `<h3>` elements, from the host element down
through this component to all of its child elements in the DOM.
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" header="src/app/hero-details.component.css"></code-example>
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" header="src/app/hero-details.component.css" linenums="false">
</code-example>
The `/deep/` combinator also has the aliases `>>>`, and `::ng-deep`.
@ -298,7 +304,8 @@ Choose from the following modes:
To set the components encapsulation mode, use the `encapsulation` property in the component metadata:
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" header="src/app/quest-summary.component.ts"></code-example>
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" header="src/app/quest-summary.component.ts" linenums="false">
</code-example>
`ShadowDom` view encapsulation only works on browsers that have native support
for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the

View File

@ -12,7 +12,7 @@ A simple example might be a button that sends users to your company website, tha
Use the Angular CLI to generate a new library skeleton with the following command:
<code-example language="bash">
<code-example format="." language="bash">
ng generate library my-lib
</code-example>
@ -35,7 +35,7 @@ The workspace configuration file, `angular.json`, is updated with a project of t
You can build, test, and lint the project with CLI commands:
<code-example language="bash">
<code-example format="." language="bash">
ng build my-lib
ng test my-lib
ng lint my-lib
@ -106,7 +106,7 @@ To learn more, see [Schematics Overview](guide/schematics) and [Schematicsfor
Use the Angular CLI and the npm package manager to build and publish your library as an npm package.
Libraries are built in [AoT mode](guide/aot-compiler) by default, so you do not need to specify the `-prod` flag when building for publication.
<code-example language="bash">
<code-example format="." language="bash">
ng build my-lib
cd dist/my-lib
npm publish
@ -158,7 +158,7 @@ You don't have to publish your library to the npm package manager in order to us
To use your own library in an app:
* Build the library. You cannot use a library before it is built.
<code-example language="bash">
<code-example format="." language="bash">
ng build my-lib
</code-example>
@ -190,6 +190,6 @@ Every time a file is changed a partial build is performed that emits the amended
Incremental builds can be run as a backround process in your dev environment. To take advantage of this feature add the `--watch` flag to the build command:
<code-example language="bash">
<code-example format="." language="bash">
ng build my-lib --watch
</code-example>

View File

@ -21,14 +21,18 @@ constructor, and lets the framework provide them.
The following example shows that `AppComponent` declares its dependence on `LoggerService` and `UserContext`.
<code-example path="dependency-injection-in-action/src/app/app.component.ts" region="ctor" header="src/app/app.component.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/app.component.ts" region="ctor" header="src/app/app.component.ts" linenums="false">
</code-example>
`UserContext` in turn depends on both `LoggerService` and
`UserService`, another service that gathers information about a particular user.
<code-example path="dependency-injection-in-action/src/app/user-context.service.ts" region="injectables" header="user-context.service.ts (injection)"></code-example>
<code-example path="dependency-injection-in-action/src/app/user-context.service.ts" region="injectables" header="user-context.service.ts (injection)" linenums="false">
</code-example>
When Angular creates `AppComponent`, the DI framework creates an instance of `LoggerService` and starts to create `UserContextService`.
@ -181,13 +185,17 @@ This `HeroBiosAndContactsComponent` is a revision of `HeroBiosComponent` which y
Focus on the template:
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="template" header="dependency-injection-in-action/src/app/hero-bios.component.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="template" header="dependency-injection-in-action/src/app/hero-bios.component.ts" linenums="false">
</code-example>
Now there's a new `<hero-contact>` element between the `<hero-bio>` tags.
Angular *projects*, or *transcludes*, the corresponding `HeroContactComponent` into the `HeroBioComponent` view,
placing it in the `<ng-content>` slot of the `HeroBioComponent` template.
<code-example path="dependency-injection-in-action/src/app/hero-bio.component.ts" region="template" header="src/app/hero-bio.component.ts (template)"></code-example>
<code-example path="dependency-injection-in-action/src/app/hero-bio.component.ts" region="template" header="src/app/hero-bio.component.ts (template)" linenums="false">
</code-example>
The result is shown below, with the hero's telephone number from `HeroContactComponent` projected above the hero description.
@ -204,7 +212,9 @@ Here's `HeroContactComponent`, which demonstrates the qualifying decorators.
Focus on the constructor parameters.
<code-example path="dependency-injection-in-action/src/app/hero-contact.component.ts" region="ctor-params" header="src/app/hero-contact.component.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/hero-contact.component.ts" region="ctor-params" header="src/app/hero-contact.component.ts" linenums="false">
</code-example>
The `@Host()` function decorating the `heroCache` constructor property ensures that
you get a reference to the cache service from the parent `HeroBioComponent`.
@ -289,7 +299,9 @@ whose `nativeElement` property exposes the DOM element for the directive to mani
The sample code applies the directive's `myHighlight` attribute to two `<div>` tags,
first without a value (yielding the default color) and then with an assigned color value.
<code-example path="dependency-injection-in-action/src/app/app.component.html" region="highlight" header="src/app/app.component.html (highlight)"></code-example>
<code-example path="dependency-injection-in-action/src/app/app.component.html" region="highlight" header="src/app/app.component.html (highlight)" linenums="false">
</code-example>
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
@ -313,7 +325,9 @@ Angular passes this token to the injector and assigns the result to the paramete
The following is a typical example.
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="ctor" header="src/app/hero-bios.component.ts (component constructor injection)"></code-example>
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="ctor" header="src/app/hero-bios.component.ts (component constructor injection)" linenums="false">
</code-example>
Angular asks the injector for the service associated with `LoggerService`
@ -372,7 +386,9 @@ You can also use a value provider in a unit test to provide mock data in place o
The `HeroOfTheMonthComponent` example has two value providers.
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-value" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-value" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" linenums="false">
</code-example>
* The first provides an existing instance of the `Hero` class to use for the `Hero` token, rather than
requiring the injector to create a new instance with `new` or use its own cached instance.
@ -411,7 +427,9 @@ extend the default class, or emulate the behavior of the real class in a test ca
The following code shows two examples in `HeroOfTheMonthComponent`.
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-class" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-class" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" linenums="false">
</code-example>
The first provider is the *de-sugared*, expanded form of the most typical case in which the
class to be created (`HeroService`) is also the provider's dependency injection token.
@ -430,7 +448,9 @@ Components outside the tree continue to receive the original `LoggerService` ins
`DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message:
<code-example path="dependency-injection-in-action/src/app/date-logger.service.ts" region="date-logger-service" header="src/app/date-logger.service.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/date-logger.service.ts" region="date-logger-service" header="src/app/date-logger.service.ts" linenums="false">
</code-example>
{@a useexisting}
@ -452,11 +472,15 @@ You might want to shrink that API surface to just the members you actually need.
In this example, the `MinimalLogger` [class-interface](#class-interface) reduces the API to two members:
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="src/app/minimal-logger.service.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="src/app/minimal-logger.service.ts" linenums="false">
</code-example>
The following example puts `MinimalLogger` to use in a simplified version of `HeroOfTheMonthComponent`.
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.1.ts" header="src/app/hero-of-the-month.component.ts (minimal version)"></code-example>
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.1.ts" header="src/app/hero-of-the-month.component.ts (minimal version)" linenums="false">
</code-example>
The `HeroOfTheMonthComponent` constructor's `logger` parameter is typed as `MinimalLogger`, so only the `logs` and `logInfo` members are visible in a TypeScript-aware editor.
@ -508,7 +532,9 @@ The `runnersUpFactory()` returns the *provider factory function*, which can use
the passed-in state value and the injected services `Hero` and `HeroService`.
<code-example path="dependency-injection-in-action/src/app/runners-up.ts" region="factory-synopsis" header="runners-up.ts (excerpt)"></code-example>
<code-example path="dependency-injection-in-action/src/app/runners-up.ts" region="factory-synopsis" header="runners-up.ts (excerpt)" linenums="false">
</code-example>
The provider factory function (returned by `runnersUpFactory()`) returns the actual dependency object,
the string of names.
@ -552,7 +578,9 @@ as the token for a provider of `LoggerService`.
`MinimalLogger` is an abstract class.
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="dependency-injection-in-action/src/app/minimal-logger.service.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="dependency-injection-in-action/src/app/minimal-logger.service.ts" linenums="false">
</code-example>
An abstract class is usually a base class that you can extend.
In this app, however there is no class that inherits from `MinimalLogger`.
@ -578,7 +606,9 @@ Using a class as an interface gives you the characteristics of an interface in a
To minimize memory cost, however, the class should have *no implementation*.
The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript for a constructor function.
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" region="minimal-logger-transpiled" header="dependency-injection-in-action/src/app/minimal-logger.service.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" region="minimal-logger-transpiled" header="dependency-injection-in-action/src/app/minimal-logger.service.ts" linenums="false">
</code-example>
Notice that it doesn't have any members. It never grows no matter how many members you add to the class,
as long as those members are typed but not implemented.
@ -605,11 +635,15 @@ another token that happens to have the same name.
You encountered them twice in the *Hero of the Month* example,
in the *title* value provider and in the *runnersUp* factory provider.
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="provide-injection-token" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="provide-injection-token" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" linenums="false">
</code-example>
You created the `TITLE` token like this:
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="injection-token" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="injection-token" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" linenums="false">
</code-example>
The type parameter, while optional, conveys the dependency's type to developers and tooling.
The token description is another developer aid.
@ -699,7 +733,9 @@ appear *above* the class definition.
Break the circularity with `forwardRef`.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)" linenums="false">
</code-example>
<!--- Waiting for good examples

View File

@ -2,16 +2,16 @@
Application components often need to share information.
You can often use loosely coupled techniques for sharing information,
such as data binding and service sharing,
but sometimes it makes sense for one component to have a direct reference to another component.
such as data binding and service sharing,
but sometimes it makes sense for one component to have a direct reference to another component.
You need a direct reference, for instance, to access values or call methods on that component.
Obtaining a component reference is a bit tricky in Angular.
Angular components themselves do not have a tree that you can
Angular components themselves do not have a tree that you can
inspect or navigate programmatically. The parent-child relationship is indirect,
established through the components' [view objects](guide/glossary#view).
Each component has a *host view*, and can have additional *embedded views*.
Each component has a *host view*, and can have additional *embedded views*.
An embedded view in component A is the
host view of component B, which can in turn have embedded view.
This means that there is a [view hierarchy](guide/glossary#view-hierarchy) for each component,
@ -40,14 +40,18 @@ In the following example, the parent `AlexComponent` has several children includ
{@a alex}
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-1" header="parent-finder.component.ts (AlexComponent v.1)"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-1" header="parent-finder.component.ts (AlexComponent v.1)" linenums="false">
</code-example>
*Cathy* reports whether or not she has access to *Alex*
after injecting an `AlexComponent` into her constructor:
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="cathy" header="parent-finder.component.ts (CathyComponent)"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="cathy" header="parent-finder.component.ts (CathyComponent)" linenums="false">
</code-example>
@ -94,13 +98,17 @@ inject its parent via the parent's base class*.
The sample's `CraigComponent` explores this question. [Looking back](#alex),
you see that the `Alex` component *extends* (*inherits*) from a class named `Base`.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" header="parent-finder.component.ts (Alex class signature)"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" header="parent-finder.component.ts (Alex class signature)" linenums="false">
</code-example>
The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="craig" header="parent-finder.component.ts (CraigComponent)"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="craig" header="parent-finder.component.ts (CraigComponent)" linenums="false">
</code-example>
@ -130,7 +138,9 @@ and add that provider to the `providers` array of the `@Component()` metadata fo
{@a alex-providers}
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)" linenums="false">
</code-example>
[Parent](#parent-token) is the provider's class interface token.
@ -139,7 +149,9 @@ The [*forwardRef*](guide/dependency-injection-in-action#forwardref) breaks the c
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter,
the same way you've done it before.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="carol-class" header="parent-finder.component.ts (CarolComponent class)"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="carol-class" header="parent-finder.component.ts (CarolComponent class)" linenums="false">
</code-example>
@ -165,7 +177,9 @@ That means he must both *inject* the `Parent` class interface to get *Alice* and
Here's *Barry*.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="barry" header="parent-finder.component.ts (BarryComponent)"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="barry" header="parent-finder.component.ts (BarryComponent)" linenums="false">
</code-example>
@ -215,7 +229,9 @@ You [learned earlier](guide/dependency-injection-in-action#class-interface) that
The example defines a `Parent` class interface.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="parent" header="parent-finder.component.ts (Parent class-interface)"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="parent" header="parent-finder.component.ts (Parent class-interface)" linenums="false">
</code-example>
@ -225,7 +241,9 @@ Such a narrow interface helps decouple the child component class from its parent
A component that could serve as a parent *should* implement the class interface as the `AliceComponent` does.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-class-signature" header="parent-finder.component.ts (AliceComponent class signature)"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-class-signature" header="parent-finder.component.ts (AliceComponent class signature)" linenums="false">
</code-example>
@ -233,7 +251,9 @@ Doing so adds clarity to the code. But it's not technically necessary.
Although `AlexComponent` has a `name` property, as required by its `Base` class,
its class signature doesn't mention `Parent`.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" header="parent-finder.component.ts (AlexComponent class signature)"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" header="parent-finder.component.ts (AlexComponent class signature)" linenums="false">
</code-example>
@ -257,15 +277,21 @@ It doesn't in this example *only* to demonstrate that the code will compile and
Writing variations of the same parent *alias provider* gets old quickly,
especially this awful mouthful with a [*forwardRef*](guide/dependency-injection-in-action#forwardref).
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
You can extract that logic into a helper function like the following.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-the-parent" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-the-parent" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
Now you can add a simpler, more meaningful parent provider to your components.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
You can do better. The current version of the helper function can only alias the `Parent` class interface.
@ -273,10 +299,14 @@ The application might have a variety of parent types, each with its own class in
Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent class interface.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-parent" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-parent" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
And here's how you could use it with a different parent type.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="beth-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="beth-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>

View File

@ -68,11 +68,13 @@ using the `Logger` token.
Another class, `EvenBetterLogger`, might display the user name in the log message.
This logger gets the user from an injected `UserService` instance.
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger"></code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger" linenums="false">
</code-example>
The injector needs providers for both this new logging service and its dependent `UserService`. Configure this alternative logger with the `useClass` provider-definition key, like `BetterLogger`. The following array specifies both providers in the `providers` metadata option of the parent module or component.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5"></code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5" linenums="false">
</code-example>
{@a aliased-class-providers}
@ -90,11 +92,13 @@ when a component asks for either the new or the old logger.
If you try to alias `OldLogger` to `NewLogger` with `useClass`, you end up with two different `NewLogger` instances in your app.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a"></code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false">
</code-example>
To make sure there is only one instance of `NewLogger`, alias `OldLogger` with the `useExisting` option.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b"></code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b" linenums="false">
</code-example>
{@a value-provider}
@ -106,11 +110,13 @@ configure the injector with the `useValue` option
The following code defines a variable that creates such an object to play the logger role.
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger"></code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger" linenums="false">
</code-example>
The following provider object uses the `useValue` key to associate the variable with the `Logger` token.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7"></code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7" linenums="false">
</code-example>
{@a non-class-dependencies}
@ -124,7 +130,8 @@ like the title of the application or the address of a web API endpoint.
These configuration objects aren't always instances of a class.
They can be object literals, as shown in the following example.
<code-example path="dependency-injection/src/app/app.config.ts" region="config" header="src/app/app.config.ts (excerpt)"></code-example>
<code-example path="dependency-injection/src/app/app.config.ts" region="config" header="src/app/app.config.ts (excerpt)" linenums="false">
</code-example>
{@a interface-not-valid-token}
@ -134,9 +141,11 @@ The `HERO_DI_CONFIG` constant conforms to the `AppConfig` interface.
Unfortunately, you cannot use a TypeScript interface as a token.
In TypeScript, an interface is a design-time artifact, and doesn't have a runtime representation (token) that the DI framework can use.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface"></code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface"></code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface" linenums="false">
</code-example>
<div class="alert is-helpful">
@ -154,19 +163,22 @@ Another solution to choosing a provider token for non-class dependencies is
to define and use an `InjectionToken` object.
The following example shows how to define such a token.
<code-example path="dependency-injection/src/app/app.config.ts" region="token" header="src/app/app.config.ts"></code-example>
<code-example path="dependency-injection/src/app/app.config.ts" region="token" header="src/app/app.config.ts" linenums="false">
</code-example>
The type parameter, while optional, conveys the dependency's type to developers and tooling.
The token description is another developer aid.
Register the dependency provider using the `InjectionToken` object:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9"></code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9" linenums="false">
</code-example>
Now you can inject the configuration object into any constructor that needs it, with
the help of an `@Inject()` parameter decorator.
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" header="src/app/app.component.ts"></code-example>
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" header="src/app/app.component.ts" linenums="false">
</code-example>
<div class="alert is-helpful">
@ -203,19 +215,22 @@ who is authorized and who isn't.
To resolve this, we give the `HeroService` constructor a boolean flag to control display of secret heroes.
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" header="src/app/heroes/hero.service.ts (excerpt)"></code-example>
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" header="src/app/heroes/hero.service.ts (excerpt)" linenums="false">
</code-example>
You can inject `Logger`, but you can't inject the `isAuthorized` flag. Instead, you can use a factory provider to create a new logger instance for `HeroService`.
A factory provider needs a factory function.
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" header="src/app/heroes/hero.service.provider.ts (excerpt)"></code-example>
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" header="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
</code-example>
Although `HeroService` has no access to `UserService`, the factory function does.
You inject both `Logger` and `UserService` into the factory provider
and let the injector pass them along to the factory function.
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" header="src/app/heroes/hero.service.provider.ts (excerpt)"></code-example>
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" header="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
</code-example>
* The `useFactory` field tells Angular that the provider is a factory function whose implementation is `heroServiceFactory`.
@ -307,13 +322,13 @@ Thus, services in the NgModule `providers` array or at component level are not t
The following example of non-tree-shakable providers in Angular configures a service provider for the injector of an NgModule.
<code-example path="dependency-injection/src/app/tree-shaking/service-and-module.ts" header="src/app/tree-shaking/service-and-modules.ts"></code-example>
<code-example path="dependency-injection/src/app/tree-shaking/service-and-module.ts" header="src/app/tree-shaking/service-and-modules.ts" linenums="false"> </code-example>
You can then import this module into your application module
to make the service available for injection in your app,
as in the following example.
<code-example path="dependency-injection/src/app/tree-shaking/app.module.ts" header="src/app/tree-shaking/app.modules.ts"></code-example>
<code-example path="dependency-injection/src/app/tree-shaking/app.module.ts" header="src/app/tree-shaking/app.modules.ts" linenums="false"> </code-example>
When `ngc` runs, it compiles `AppModule` into a module factory, which contains definitions for all the providers declared in all the modules it includes. At runtime, this factory becomes an injector that instantiates these services.
@ -325,11 +340,11 @@ You can make a provider tree-shakable by specifying it in the `@Injectable()` de
The following example shows the tree-shakable equivalent to the `ServiceModule` example above.
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" header="src/app/tree-shaking/service.ts"></code-example>
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" header="src/app/tree-shaking/service.ts" linenums="false"> </code-example>
The service can be instantiated by configuring a factory function, as in the following example.
<code-example path="dependency-injection/src/app/tree-shaking/service.0.ts" header="src/app/tree-shaking/service.0.ts"></code-example>
<code-example path="dependency-injection/src/app/tree-shaking/service.0.ts" header="src/app/tree-shaking/service.0.ts" linenums="false"> </code-example>
<div class="alert is-helpful">

View File

@ -196,7 +196,8 @@ Listing dependencies as constructor parameters may be all you need to test appli
For example, you can create a new `HeroListComponent` with a mock service that you can manipulate
under test.
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" header="src/app/test.component.ts"></code-example>
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" header="src/app/test.component.ts" linenums="false">
</code-example>
<div class="alert is-helpful">
@ -258,7 +259,8 @@ In simple examples, the dependency value is an *instance*, and
the class *type* serves as its own lookup key.
Here you get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" header="src/app/injector.component.ts"></code-example>
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" header="src/app/injector.component.ts" linenums="false">
</code-example>
The behavior is similar when you write a constructor that requires an injected class-based dependency.
When you define a constructor parameter with the `HeroService` class type,
@ -285,7 +287,8 @@ constructor parameter with `@Optional()`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="import-optional">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor"></code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor" linenums="false">
</code-example>
When using `@Optional()`, your code must be prepared for a null value. If you
don't register a logger provider anywhere, the injector sets the

View File

@ -152,7 +152,7 @@ The list is by no means exhaustive, but should provide you with a good starting
[rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html) to the `.htaccess` file as shown
(https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):
<code-example>
<code-example format=".">
RewriteEngine On
&#35 If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
@ -175,7 +175,7 @@ modified to serve `index.html`:
* [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown
[here](http://stackoverflow.com/a/26152011/2116927):
<code-example format='.' language="xml">
<code-example format='.' language="xml" linenums="false">
&lt;system.webServer&gt;
&lt;rewrite&gt;
&lt;rules&gt;
@ -207,7 +207,7 @@ and to
* [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
[rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites).
<code-example language="json">
<code-example format="." language="json">
"rewrites": [ {
"source": "**",
"destination": "/index.html"
@ -436,7 +436,7 @@ When you create a production build using [`ng build --prod`](cli/build), the CLI
The `index.html` file is also modified during the build process to include script tags that enable differential loading. See the sample output below from the `index.html` file produced during a build using `ng build`.
<code-example language="html">
<code-example language="html" format="." linenums="false">
&lt;body>
&lt;app-root>&lt;/app-root>
&lt;script src="runtime-es2015.js" type="module">&lt;/script>
@ -479,7 +479,7 @@ not IE 9-11 # For IE 9-11 support, remove 'not'.
The `tsconfig.json` looks like this:
<code-example language="json">
<code-example language="json" format="." linenums="false">
{
"compileOnSave": false,
@ -550,7 +550,7 @@ To maintain the benefits of differential loading, however, a better option is to
To do this for `ng serve`, create a new file, `tsconfig-es5.app.json` next to `tsconfig.app.json` with the following content.
<code-example language="json">
<code-example language="json" format="." linenums="false">
{
"extends": "./tsconfig.app.json",
@ -563,7 +563,7 @@ To do this for `ng serve`, create a new file, `tsconfig-es5.app.json` next to `t
In `angular.json` add two new configuration sections under the `build` and `serve` targets to point to the new TypeScript configuration.
<code-example language="json">
<code-example language="json" format="." linenums="false">
"build": {
"builder": "@angular-devkit/build-angular:browser",
@ -610,7 +610,7 @@ ng serve --configuration es5
Create a new file, `tsconfig-es5.spec.json` next to `tsconfig.spec.json` with the following content.
<code-example language="json">
<code-example language="json" format="." linenums="false">
{
"extends": "./tsconfig.spec.json",
@ -621,7 +621,7 @@ Create a new file, `tsconfig-es5.spec.json` next to `tsconfig.spec.json` with th
</code-example>
<code-example language="json">
<code-example language="json" format="." linenums="false">
"test": {
"builder": "@angular-devkit/build-angular:karma",
@ -649,7 +649,7 @@ ng test --configuration es5
Create an [ES5 serve configuration](guide/deployment#configuring-serve-for-es5) as explained above, and configuration an ES5 configuration for the E2E target.
<code-example language="json">
<code-example language="json" format="." linenums="false">
"test": {
"builder": "@angular-devkit/build-angular:protractor",

View File

@ -178,7 +178,7 @@ For more information, see [/deep/, >>>, and ::ng-deep](guide/component-styles#de
{@a template-tag}
### &lt;template&gt; tag
The `<template>` tag was deprecated in v4 to avoid colliding with the DOM's element of the same name (such as when using web components). Use `<ng-template>` instead. For more information, see the [Ahead-of-Time Compilation](guide/angular-compiler-options#enablelegacytemplate) guide.
The `<template>` tag was deprecated in v4 to avoid colliding with the DOM's element of the same name (such as when using web components). Use `<ng-template>` instead. For more information, see the [Ahead-of-Time Compilation](guide/aot-compiler#enablelegacytemplate) guide.
@ -368,7 +368,7 @@ These two properties have subtle differences, so switching to `textContent` unde
All of the `wtf*` APIs are deprecated and will be removed in a future version.
{@a webworker-apps}
### Running Angular applications in platform-webworker
### Running Angular applications in platform-webworker
The `@angular/platform-*` packages enable Angular to be run in different contexts. For examples,
`@angular/platform-server` enables Angular to be run on the server, and `@angular/platform-browser`
@ -382,7 +382,7 @@ worker is not the best strategy for most applications.
Going forward, we will focus our efforts related to web workers around their primary use case of
offloading CPU-intensive, non-critical work needed for initial rendering (such as in-memory search
and image processing). Learn more in the
and image processing). Learn more in the
[guide to Using Web Workers with the Angular CLI](guide/web-worker).
As of Angular version 8, all `platform-webworker` APIs are deprecated.
@ -465,99 +465,3 @@ For more information about using `@angular/common/http`, see the [HttpClient gui
| `MockBackend` | [`HttpTestingController`](/api/common/http/testing/HttpTestingController) |
| `MockConnection` | [`HttpTestingController`](/api/common/http/testing/HttpTestingController) |
## Renderer to Renderer2 migration
### Migration Overview
The `Renderer` class has been marked as deprecated since Angular version 4. This section provides guidance on migrating from this deprecated API to the newer `Renderer2` API and what it means for your app.
### Why should I migrate to Renderer2?
The deprecated `Renderer` class has been removed in version 9 of Angular, so it's necessary to migrate to a supported API. Using `Renderer2` is the recommended strategy because it supports a similar set of functionality to `Renderer`. The API surface is quite large (with 19 methods), but the schematic should simplify this process for your applications.
### Is there action required on my end?
No. The schematic should handle most cases with the exception of `Renderer.animate()` and `Renderer.setDebugInfo()`, which already arent supported.
### What are the `__ngRendererX` methods? Why are they necessary?
Some methods either don't have exact equivalents in `Renderer2`, or they correspond to more than one expression. For example, both renderers have a `createElement()` method, but they're not equal because a call such as `renderer.createElement(parentNode, namespaceAndName)` in the `Renderer` corresponds to the following block of code in `Renderer2`:
```ts
const [namespace, name] = splitNamespace(namespaceAndName);
const el = renderer.createElement(name, namespace);
if (parentNode) {
renderer.appendChild(parentNode, el);
}
return el;
```
Migration has to guarantee that the return values of functions and types of variables stay the same. To handle the majority of cases safely, the schematic declares helper functions at the bottom of the user's file. These helpers encapsulate your own logic and keep the replacements inside your code down to a single function call. Here's an example of how the `createElement()` migration looks:
**Before:**
```ts
public createAndAppendElement() {
const el = this.renderer.createElement('span');
el.textContent = 'hello world';
return el;
}
```
**After:**
<code-example>
public createAndAppendElement() {
const el = __ngRendererCreateElement(this.renderer, this.element, 'span');
el.textContent = 'hello world';
return el;
}
// Generated code at the bottom of the file
__ngRendererCreateElement(renderer: any, parentNode: any, nameAndNamespace: any) {
const [namespace, name] = __ngRendererSplitNamespace(namespaceAndName);
const el = renderer.createElement(name, namespace);
if (parentNode) {
renderer.appendChild(parentNode, el);
}
return el;
}
__ngRendererSplitNamespace(nameAndNamespace: any) {
// returns the split name and namespace
}
</code-example>
When implementing these helper functions, the schematic ensures that they're only declared once per file and that their names are unique enough that there's a small chance of colliding with pre-existing functions in your code. The schematic also keeps their parameter types as `any` so that it doesn't have to insert extra logic that ensures that their values have the correct type.
### Im a library author. Should I run this migration?
**Library authors should definitely use this migration to move away from the `Renderer`. Otherwise, the libraries won't work with applications built with version 9.**
### Full list of method migrations
The following table shows all methods that the migration maps from `Renderer` to `Renderer2`.
|Renderer|Renderer2|
|---|---|
|`listen(renderElement, name, callback)`|`listen(renderElement, name, callback)`|
|`setElementProperty(renderElement, propertyName, propertyValue)`|`setProperty(renderElement, propertyName, propertyValue)`|
|`setText(renderNode, text)`|`setValue(renderNode, text)`|
|`listenGlobal(target, name, callback)`|`listen(target, name, callback)`|
|`selectRootElement(selectorOrNode, debugInfo?)`|`selectRootElement(selectorOrNode)`|
|`createElement(parentElement, name, debugInfo?)`|`appendChild(parentElement, createElement(name))`|
|`setElementStyle(el, style, value?)`|`value == null ? removeStyle(el, style) : setStyle(el, style, value)`
|`setElementAttribute(el, name, value?)`|`attributeValue == null ? removeAttribute(el, name) : setAttribute(el, name, value)`
|`createText(parentElement, value, debugInfo?)`|`appendChild(parentElement, createText(value))`|
|`createTemplateAnchor(parentElement)`|`appendChild(parentElement, createComment(''))`|
|`setElementClass(renderElement, className, isAdd)`|`isAdd ? addClass(renderElement, className) : removeClass(renderElement, className)`|
|`projectNodes(parentElement, nodes)`|`for (let i = 0; i < nodes.length; i<ins></ins>) { appendChild(parentElement, nodes<i>); }`|
|`attachViewAfter(node, viewRootNodes)`|`const parentElement = parentNode(node); const nextSibling = nextSibling(node); for (let i = 0; i < viewRootNodes.length; i<ins></ins>) { insertBefore(parentElement, viewRootNodes<i>, nextSibling);}`|
|`detachView(viewRootNodes)`|`for (let i = 0; i < viewRootNodes.length; i<ins></ins>) {const node = viewRootNodes<i>; const parentElement = parentNode(node); removeChild(parentElement, node);}`|
|`destroyView(hostElement, viewAllNodes)`|`for (let i = 0; i < viewAllNodes.length; i<ins></ins>) { destroyNode(viewAllNodes<i>); }`|
|`setBindingDebugInfo()`|This function is a noop in `Renderer2`.|
|`createViewRoot(hostElement)`|Should be replaced with a reference to `hostElement`|
|`invokeElementMethod(renderElement, methodName, args?)`|`(renderElement as any)<methodName>.apply(renderElement, args);`|
|`animate(element, startingStyles, keyframes, duration, delay, easing, previousPlayers?)`|Throws an error (same behavior as `Renderer.animate()`)|

View File

@ -31,7 +31,7 @@ The easiest way to display a component property
is to bind the property name through interpolation.
With interpolation, you put the property name in the view template, enclosed in double curly braces: `{{myHero}}`.
Use the CLI command [`ng new displaying-data`](cli/new) to create a workspace and app named `displaying-data`.
Use the CLI command [`ng new displaying-data`](cli/new) to create a workspace and app named `displaying-data`.
Delete the <code>app.component.html</code> file. It is not needed for this example.
@ -41,7 +41,9 @@ changing the template and the body of the component.
When you're done, it should look like this:
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts"></code-example>
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts">
</code-example>
@ -51,7 +53,9 @@ The template displays the two component properties using double curly brace
interpolation:
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
<code-example path="displaying-data/src/app/app.component.1.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
</code-example>
@ -93,7 +97,9 @@ The CSS `selector` in the `@Component` decorator specifies an element named `<ap
That element is a placeholder in the body of your `index.html` file:
<code-example path="displaying-data/src/index.html" header="src/index.html (body)" region="body"></code-example>
<code-example path="displaying-data/src/index.html" linenums="false" header="src/index.html (body)" region="body">
</code-example>
@ -127,7 +133,7 @@ is simpler without the additional HTML file.
In either style, the template data bindings have the same access to the component's properties.
<div class="alert is-helpful">
By default, the Angular CLI command [`ng generate component`](cli/generate) generates components with a template file. You can override that with:
<code-example hideCopy language="sh" class="code-shell">
@ -142,7 +148,9 @@ In either style, the template data bindings have the same access to the componen
Although this example uses variable assignment to initialize the components, you could instead declare and initialize the properties using a constructor:
<code-example path="displaying-data/src/app/app-ctor.component.1.ts" region="class"></code-example>
<code-example path="displaying-data/src/app/app-ctor.component.1.ts" linenums="false" region="class">
</code-example>
@ -155,7 +163,9 @@ This app uses more terse "variable assignment" style simply for brevity.
To display a list of heroes, begin by adding an array of hero names to the component and redefine `myHero` to be the first name in the array.
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (class)" region="class">
</code-example>
@ -163,7 +173,9 @@ Now use the Angular `ngFor` directive in the template to display
each item in the `heroes` list.
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
</code-example>
@ -172,7 +184,9 @@ in the `<li>` element is the Angular "repeater" directive.
It marks that `<li>` element (and its children) as the "repeater template":
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (li)" region="li"></code-example>
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (li)" region="li">
</code-example>
@ -237,7 +251,9 @@ of hero names into an array of `Hero` objects. For that you'll need a `Hero` cla
With the following code:
<code-example path="displaying-data/src/app/hero.ts" header="src/app/hero.ts"></code-example>
<code-example path="displaying-data/src/app/hero.ts" linenums="false" header="src/app/hero.ts">
</code-example>
@ -249,7 +265,9 @@ The declaration of the constructor parameters takes advantage of a TypeScript sh
Consider the first parameter:
<code-example path="displaying-data/src/app/hero.ts" header="src/app/hero.ts (id)" region="id"></code-example>
<code-example path="displaying-data/src/app/hero.ts" linenums="false" header="src/app/hero.ts (id)" region="id">
</code-example>
@ -267,7 +285,9 @@ After importing the `Hero` class, the `AppComponent.heroes` property can return
of `Hero` objects:
<code-example path="displaying-data/src/app/app.component.3.ts" header="src/app/app.component.ts (heroes)" region="heroes"></code-example>
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (heroes)" region="heroes">
</code-example>
@ -276,7 +296,9 @@ At the moment it displays the hero's `id` and `name`.
Fix that to display only the hero's `name` property.
<code-example path="displaying-data/src/app/app.component.3.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
</code-example>
@ -294,7 +316,9 @@ The Angular `ngIf` directive inserts or removes an element based on a _truthy/fa
To see it in action, add the following paragraph at the bottom of the template:
<code-example path="displaying-data/src/app/app.component.ts" header="src/app/app.component.ts (message)" region="message"></code-example>
<code-example path="displaying-data/src/app/app.component.ts" linenums="false" header="src/app/app.component.ts (message)" region="message">
</code-example>

View File

@ -428,7 +428,7 @@ You control the _code-example_ output by setting one or more of its attributes:
* `region`- displays the source file fragment with that region name; regions are identified by _docregion_ markup in the source file, as explained [below](#region "Displaying a code fragment").
* `linenums`- value may be `true`, `false`, or a `number`. When not specified, line numbers default to `false` (i.e. no line numbers are displayed). The rarely used `number` option starts line numbering at the given value. `linenums=4` sets the starting line number to 4.
* `linenums`- value may be `true`, `false`, or a `number`. When not specified, line numbers are automatically displayed when there are greater than 10 lines of code. The rarely used `number` option starts line numbering at the given value. `linenums=4` sets the starting line number to 4.
* `class`- code snippets can be styled with the CSS classes `no-box`, `code-shell`, and `avoid`.
@ -465,6 +465,8 @@ A couple of observations:
1. Omitting the `header` is fine when the source of the fragment is obvious. We just said that this is a fragment of the `app.module.ts` file which was displayed immediately above, in full, with a header.
There's no need to repeat the header.
1. The line numbers disappeared. By default, the doc viewer omits line numbers when there are fewer than 10 lines of code; it adds line numbers after that. You can turn line numbers on or off explicitly by setting the `linenums` attribute.
#### Example of bad code
Sometimes you want to display an example of bad code or bad design.
@ -494,18 +496,18 @@ Code tabs display code much like _code examples_ do. The added advantage is tha
#### Code-tabs attributes
* `linenums`: The value can be `true`, `false` or a number indicating the starting line number. If not specified, it defaults to `false`.
* `linenums`: The value can be `true`, `false` or a number indicating the starting line number. If not specified, line numbers are enabled only when code for a tab pane has greater than 10 lines of code.
#### Code-pane attributes
* `path` - a file in the content/examples folder
* `header` - seen in the header of a tab
* `linenums` - overrides the `linenums` property at the `code-tabs` level for this particular pane. The value can be `true`, `false` or a number indicating the starting line number. If not specified, it defaults to `false`.
* `linenums` - overrides the `linenums` property at the `code-tabs` level for this particular pane. The value can be `true`, `false` or a number indicating the starting line number. If not specified, line numbers are enabled only when the number of lines of code are greater than 10.
The next example displays multiple code tabs, each with its own header.
It demonstrates control over display of line numbers at both the `<code-tabs>` and `<code-pane>` levels.
<code-tabs linenums="true">
<code-tabs linenums="false">
<code-pane
header="app.component.html"
path="docs-style-guide/src/app/app.component.html">
@ -513,7 +515,7 @@ It demonstrates control over display of line numbers at both the `<code-tabs>` a
<code-pane
header="app.component.ts"
path="docs-style-guide/src/app/app.component.ts"
linenums="false">
linenums="true">
</code-pane>
<code-pane
header="app.component.css (heroes)"
@ -528,11 +530,11 @@ It demonstrates control over display of line numbers at both the `<code-tabs>` a
Here's the markup for that example.
Note how the `linenums` attribute in the `<code-tabs>` explicitly enables numbering for all panes.
The `linenums` attribute in the second pane disables line numbering for _itself only_.
Note how the `linenums` attribute in the `<code-tabs>` explicitly disables numbering for all panes.
The `linenums` attribute in the second pane restores line numbering for _itself only_.
```html
<code-tabs linenums="true">
<code-tabs linenums="false">
<code-pane
header="app.component.html"
path="docs-style-guide/src/app/app.component.html">
@ -540,7 +542,7 @@ The `linenums` attribute in the second pane disables line numbering for _itself
<code-pane
header="app.component.ts"
path="docs-style-guide/src/app/app.component.ts"
linenums="false">
linenums="true">
</code-pane>
<code-pane
header="app.component.css (heroes)"
@ -665,7 +667,7 @@ Examine the `src/app/app.component.ts` file which defines two nested _#docregion
The inner, `class-skeleton` region appears twice, once to capture the code that opens the class definition and once to capture the code that closes the class definition.
<code-example>
<code-example linenums="false">
// #docplaster
...
// #docregion class, class-skeleton

View File

@ -35,7 +35,9 @@ The ad banner uses a helper directive called `AdDirective` to
mark valid insertion points in the template.
<code-example path="dynamic-component-loader/src/app/ad.directive.ts" header="src/app/ad.directive.ts"></code-example>
<code-example path="dynamic-component-loader/src/app/ad.directive.ts" header="src/app/ad.directive.ts" linenums="false">
</code-example>
@ -60,7 +62,9 @@ To apply the `AdDirective`, recall the selector from `ad.directive.ts`,
where to dynamically load components.
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" header="src/app/ad-banner.component.ts (template)"></code-example>
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" header="src/app/ad-banner.component.ts (template)" linenums="false">
</code-example>
@ -87,7 +91,9 @@ With its `getAds()` method, `AdBannerComponent` cycles through the array of `AdI
and loads a new component every 3 seconds by calling `loadComponent()`.
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="class" header="src/app/ad-banner.component.ts (excerpt)"></code-example>
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="class" header="src/app/ad-banner.component.ts (excerpt)" linenums="false">
</code-example>
@ -144,7 +150,9 @@ dynamically loaded components since they load at runtime.
To ensure that the compiler still generates a factory,
add dynamically loaded components to the `NgModule`'s `entryComponents` array:
<code-example path="dynamic-component-loader/src/app/app.module.ts" region="entry-components" header="src/app/app.module.ts (entry components)"></code-example>
<code-example path="dynamic-component-loader/src/app/app.module.ts" region="entry-components" header="src/app/app.module.ts (entry components)" linenums="false">
</code-example>

View File

@ -77,14 +77,18 @@ appropriate controls dynamically.
via the `type` property.
<code-example path="dynamic-form/src/app/question-textbox.ts" header="src/app/question-textbox.ts"></code-example>
<code-example path="dynamic-form/src/app/question-textbox.ts" header="src/app/question-textbox.ts" linenums="false">
</code-example>
`DropdownQuestion` presents a list of choices in a select box.
<code-example path="dynamic-form/src/app/question-dropdown.ts" header="src/app/question-dropdown.ts"></code-example>
<code-example path="dynamic-form/src/app/question-dropdown.ts" header="src/app/question-dropdown.ts" linenums="false">
</code-example>
@ -93,7 +97,9 @@ In a nutshell, the form group consumes the metadata from the question model and
allows you to specify default values and validation rules.
<code-example path="dynamic-form/src/app/question-control.service.ts" header="src/app/question-control.service.ts"></code-example>
<code-example path="dynamic-form/src/app/question-control.service.ts" header="src/app/question-control.service.ts" linenums="false">
</code-example>
{@a form-component}

View File

@ -1,5 +1,12 @@
# Entry Components
#### Prerequisites:
A basic understanding of the following concepts:
* [Bootstrapping](guide/bootstrapping).
<hr />
An entry component is any component that Angular loads imperatively, (which means youre not referencing it in the template), by type. You specify an entry component by bootstrapping it in an NgModule, or including it in a routing definition.
<div class="alert is-helpful">

View File

@ -1,6 +1,12 @@
# Feature Modules
Feature modules are NgModules for the purpose of organizing code.
Feature modules are NgModules for the purpose of organizing code.
#### Prerequisites
A basic understanding of the following:
* [Bootstrapping](guide/bootstrapping).
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
* [Frequently Used Modules](guide/frequent-ngmodules).
For the final sample app with a feature module that this page describes,
see the <live-example></live-example>.
@ -66,7 +72,8 @@ ng generate component customer-dashboard/CustomerDashboard
This generates a folder for the new component within the customer-dashboard folder and updates the feature module with the `CustomerDashboardComponent` info:
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="customer-dashboard-component" header="src/app/customer-dashboard/customer-dashboard.module.ts"></code-example>
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="customer-dashboard-component" header="src/app/customer-dashboard/customer-dashboard.module.ts" linenums="false">
</code-example>
@ -76,7 +83,8 @@ The `CustomerDashboardComponent` is now in the JavaScript import list at the top
To incorporate the feature module into your app, you have to let the root module, `app.module.ts`, know about it. Notice the `CustomerDashboardModule` export at the bottom of `customer-dashboard.module.ts`. This exposes it so that other modules can get to it. To import it into the `AppModule`, add it to the imports in `app.module.ts` and to the `imports` array:
<code-example path="feature-modules/src/app/app.module.ts" region="app-module" header="src/app/app.module.ts"></code-example>
<code-example path="feature-modules/src/app/app.module.ts" region="app-module" header="src/app/app.module.ts" linenums="false">
</code-example>
Now the `AppModule` knows about the feature module. If you were to add any service providers to the feature module, `AppModule` would know about those too, as would any other feature modules. However, NgModules dont expose their components.
@ -86,18 +94,21 @@ Now the `AppModule` knows about the feature module. If you were to add any servi
When the CLI generated the `CustomerDashboardComponent` for the feature module, it included a template, `customer-dashboard.component.html`, with the following markup:
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard/customer-dashboard.component.html" region="feature-template" header="src/app/customer-dashboard/customer-dashboard/customer-dashboard.component.html"></code-example>
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard/customer-dashboard.component.html" region="feature-template" header="src/app/customer-dashboard/customer-dashboard/customer-dashboard.component.html" linenums="false">
</code-example>
To see this HTML in the `AppComponent`, you first have to export the `CustomerDashboardComponent` in the `CustomerDashboardModule`. In `customer-dashboard.module.ts`, just beneath the `declarations` array, add an `exports` array containing `CustomerDashboardComponent`:
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="component-exports" header="src/app/customer-dashboard/customer-dashboard.module.ts"></code-example>
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="component-exports" header="src/app/customer-dashboard/customer-dashboard.module.ts" linenums="false">
</code-example>
Next, in the `AppComponent`, `app.component.html`, add the tag `<app-customer-dashboard>`:
<code-example path="feature-modules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html"></code-example>
<code-example path="feature-modules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html" linenums="false">
</code-example>
Now, in addition to the title that renders by default, the `CustomerDashboardComponent` template renders too:

View File

@ -4,7 +4,7 @@ You develop applications in the context of an Angular [workspace](guide/glossary
The Angular CLI `ng new` command creates a workspace.
<code-example language="bash">
<code-example language="bash" linenums="false">
ng new &lt;my-project&gt;
</code-example>
@ -111,7 +111,7 @@ An `e2e/` folder at the top level contains source files for a set of end-to-end
For a multi-project workspace, application-specific end-to-end tests are in the project root, under `projects/project-name/e2e/`.
<code-example language="none">
<code-example language="none" linenums="false">
e2e/
src/ (end-to-end tests for my-app)
app.e2e-spec.ts
@ -131,13 +131,13 @@ A multi-project workspace is suitable for an enterprise that uses a single repos
If you intend to have multiple projects in a workspace, you can skip the initial application generation when you create the workspace, and give the workspace a unique name.
The following command creates a workspace with all of the workspace-wide configuration files, but no root-level application.
<code-example language="bash">
<code-example language="bash" linenums="false">
ng new my-workspace --createApplication="false"
</code-example>
You can then generate apps and libraries with names that are unique within the workspace.
<code-example language="bash">
<code-example language="bash" linenums="false">
cd my-workspace
ng generate application my-first-app
</code-example>
@ -148,7 +148,7 @@ The first explicitly generated application goes into the `projects/` folder alon
Newly generated libraries are also added under `projects/`.
When you create projects this way, the file structure of the workspace is entirely consistent with the structure of the [workspace configuration file](guide/workspace-config), `angular.json`.
<code-example language="none">
<code-example language="none" linenums="false">
my-workspace/
... (workspace-wide config files)
projects/ (generated applications and libraries)

View File

@ -29,7 +29,9 @@ either a list of validation errors, which results in an INVALID status, or null,
You can then inspect the control's state by exporting `ngModel` to a local template variable.
The following example exports `NgModel` into a variable called `name`:
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-with-error-msg" header="template/hero-form-template.component.html (name)"></code-example>
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-with-error-msg" header="template/hero-form-template.component.html (name)" linenums="false">
</code-example>
Note the following:
@ -90,7 +92,8 @@ built-in validators&mdash;this time, in function form. See below:
{@a reactive-component-class}
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="form-group" header="reactive/hero-form-reactive.component.ts (validator functions)"></code-example>
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="form-group" header="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
</code-example>
Note that:
@ -103,7 +106,8 @@ for the template.
If you look at the template for the name input again, it is fairly similar to the template-driven example.
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg" header="reactive/hero-form-reactive.component.html (name with error msg)"></code-example>
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg" header="reactive/hero-form-reactive.component.html (name with error msg)" linenums="false">
</code-example>
Key takeaways:
@ -121,7 +125,8 @@ Consider the `forbiddenNameValidator` function from previous
[examples](guide/form-validation#reactive-component-class) in
this guide. Here's what the definition of that function looks like:
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" header="shared/forbidden-name.directive.ts (forbiddenNameValidator)"></code-example>
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" header="shared/forbidden-name.directive.ts (forbiddenNameValidator)" linenums="false">
</code-example>
The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name and returns a validator function.
@ -143,7 +148,8 @@ at which point the form uses the last value emitted for validation.
In reactive forms, custom validators are fairly simple to add. All you have to do is pass the function directly
to the `FormControl`.
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="custom-validator" header="reactive/hero-form-reactive.component.ts (validator functions)"></code-example>
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="custom-validator" header="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
</code-example>
### Adding to template-driven forms
@ -155,7 +161,8 @@ The corresponding `ForbiddenValidatorDirective` serves as a wrapper around the `
Angular recognizes the directive's role in the validation process because the directive registers itself
with the `NG_VALIDATORS` provider, a provider with an extensible collection of validators.
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" header="shared/forbidden-name.directive.ts (providers)"></code-example>
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" header="shared/forbidden-name.directive.ts (providers)" linenums="false">
</code-example>
The directive class then implements the `Validator` interface, so that it can easily integrate
with Angular forms. Here is the rest of the directive to help you get an idea of how it all
@ -166,7 +173,9 @@ comes together:
Once the `ForbiddenValidatorDirective` is ready, you can simply add its selector, `appForbiddenName`, to any input element to activate it. For example:
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-input" header="template/hero-form-template.component.html (forbidden-name-input)"></code-example>
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-input" header="template/hero-form-template.component.html (forbidden-name-input)" linenums="false">
</code-example>
<div class="alert is-helpful">
@ -236,7 +245,8 @@ const heroForm = new FormGroup({
The validator code is as follows:
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-validator" header="shared/identity-revealed.directive.ts"></code-example>
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-validator" header="shared/identity-revealed.directive.ts" linenums="false">
</code-example>
The identity validator implements the `ValidatorFn` interface. It takes an Angular control object as an argument and returns either null if the form is valid, or `ValidationErrors` otherwise.
@ -245,7 +255,8 @@ First we retrieve the child controls by calling the `FormGroup`'s [get](api/form
If the values do not match, the hero's identity remains secret, and we can safely return null. Otherwise, the hero's identity is revealed and we must mark the form as invalid by returning an error object.
Next, to provide better user experience, we show an appropriate error message when the form is invalid.
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="cross-validation-error-message" header="reactive/hero-form-template.component.html"></code-example>
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="cross-validation-error-message" header="reactive/hero-form-template.component.html" linenums="false">
</code-example>
Note that we check if:
- the `FormGroup` has the cross validation error returned by the `identityRevealed` validator,
@ -254,13 +265,16 @@ Note that we check if:
### Adding to template driven forms
First we must create a directive that will wrap the validator function. We provide it as the validator using the `NG_VALIDATORS` token. If you are not sure why, or you do not fully understand the syntax, revisit the previous [section](guide/form-validation#adding-to-template-driven-forms).
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-directive" header="shared/identity-revealed.directive.ts"></code-example>
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-directive" header="shared/identity-revealed.directive.ts" linenums="false">
</code-example>
Next, we have to add the directive to the html template. Since the validator must be registered at the highest level in the form, we put the directive on the `form` tag.
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-register-validator" header="template/hero-form-template.component.html"></code-example>
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-register-validator" header="template/hero-form-template.component.html" linenums="false">
</code-example>
To provide better user experience, we show an appropriate error message when the form is invalid.
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-error-message" header="template/hero-form-template.component.html"></code-example>
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-error-message" header="template/hero-form-template.component.html" linenums="false">
</code-example>
Note that we check if:
- the form has the cross validation error returned by the `identityRevealed` validator,
@ -299,7 +313,7 @@ To validate the potential alter ego, we need to consult a central database of al
Let's start by creating the validator class.
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator"></code-example>
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator" linenums="false"></code-example>
As you can see, the `UniqueAlterEgoValidator` class implements the `AsyncValidator` interface. In the constructor, we inject the `HeroesService` that has the following interface:

View File

@ -109,7 +109,9 @@ Using the Angular CLI command [`ng generate class`](cli/generate), generate a ne
With this content:
<code-example path="forms/src/app/hero.ts" header="src/app/hero.ts"></code-example>
<code-example path="forms/src/app/hero.ts" header="src/app/hero.ts">
</code-example>
It's an anemic model with few requirements and no behavior. Perfect for the demo.
@ -120,7 +122,9 @@ The `alterEgo` is optional, so the constructor lets you omit it; note the questi
You can create a new hero like this:
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="SkyDog"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" region="SkyDog">
</code-example>
## Create a form component
@ -138,7 +142,9 @@ Using the Angular CLI command [`ng generate component`](cli/generate), generate
With this content:
<code-example path="forms/src/app/hero-form/hero-form.component.ts" header="src/app/hero-form/hero-form.component.ts (v1)" region="v1"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" header="src/app/hero-form/hero-form.component.ts (v1)" region="v1">
</code-example>
Theres nothing special about this component, nothing form-specific,
nothing to distinguish it from any component you've written before.
@ -170,7 +176,9 @@ Because template-driven forms are in their own module, you need to add the `Form
Update it with the following:
<code-example path="forms/src/app/app.module.ts" header="src/app/app.module.ts"></code-example>
<code-example path="forms/src/app/app.module.ts" header="src/app/app.module.ts">
</code-example>
<div class="alert is-helpful">
@ -196,7 +204,9 @@ Update it with the following:
Replace the contents of its template with the following:
<code-example path="forms/src/app/app.component.html" header="src/app/app.component.html"></code-example>
<code-example path="forms/src/app/app.component.html" header="src/app/app.component.html">
</code-example>
<div class="alert is-helpful">
@ -211,7 +221,9 @@ Replace the contents of its template with the following:
Update the template file with the following contents:
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="start" header="src/app/hero-form/hero-form.component.html"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="start" header="src/app/hero-form/hero-form.component.html">
</code-example>
The language is simply HTML5. You're presenting two of the `Hero` fields, `name` and `alterEgo`, and
opening them up for user input in input boxes.
@ -247,7 +259,9 @@ Bootstrap gives the form a little style.
To add the stylesheet, open `styles.css` and add the following import line at the top:
<code-example path="forms/src/styles.1.css" header="src/styles.css"></code-example>
<code-example path="forms/src/styles.1.css" linenums="false" header="src/styles.css">
</code-example>
## Add powers with _*ngFor_
@ -260,7 +274,9 @@ a technique seen previously in the [Displaying Data](guide/displaying-data) page
Add the following HTML *immediately below* the *Alter Ego* group:
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (powers)" region="powers"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (powers)" region="powers">
</code-example>
This code repeats the `<option>` tag for each power in the list of powers.
The `pow` template input variable is a different power in each iteration;
@ -291,7 +307,9 @@ makes binding the form to the model easy.
Find the `<input>` tag for *Name* and update it like this:
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-1"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-1">
</code-example>
<div class="alert is-helpful">
@ -307,7 +325,9 @@ You need one more addition to display the data. Declare
a template variable for the form. Update the `<form>` tag with
`#heroForm="ngForm"` as follows:
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="template-variable"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="template-variable">
</code-example>
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
@ -371,7 +391,9 @@ Then you can confirm that two-way data binding works *for the entire hero model*
After revision, the core of the form should look like this:
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModel-2"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModel-2">
</code-example>
<div class="alert is-helpful">
@ -471,7 +493,9 @@ You can leverage those class names to change the appearance of the control.
Temporarily add a [template reference variable](guide/template-syntax#ref-vars) named `spy`
to the _Name_ `<input>` tag and use it to display the input's CSS classes.
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-2"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-2">
</code-example>
Now run the app and look at the _Name_ input box.
Follow these steps *precisely*:
@ -511,11 +535,15 @@ on the left of the input box:
You achieve this effect by adding these class definitions to a new `forms.css` file
that you add to the project as a sibling to `index.html`:
<code-example path="forms/src/assets/forms.css" header="src/assets/forms.css"></code-example>
<code-example path="forms/src/assets/forms.css" header="src/assets/forms.css">
</code-example>
Update the `<head>` of `index.html` to include this style sheet:
<code-example path="forms/src/index.html" header="src/index.html (styles)" region="styles"></code-example>
<code-example path="forms/src/index.html" linenums="false" header="src/index.html (styles)" region="styles">
</code-example>
## Show and hide validation error messages
@ -536,7 +564,9 @@ To achieve this effect, extend the `<input>` tag with the following:
Here's an example of an error message added to the _name_ input box:
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="name-with-error-msg"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="name-with-error-msg">
</code-example>
You need a template reference variable to access the input box's Angular control from within the template.
Here you created a variable called `name` and gave it the value "ngModel".
@ -553,7 +583,9 @@ Here you created a variable called `name` and gave it the value "ngModel".
You control visibility of the name error message by binding properties of the `name`
control to the message `<div>` element's `hidden` property.
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg">
</code-example>
In this example, you hide the message when the control is valid or pristine;
"pristine" means the user hasn't changed the value since it was displayed in this form.
@ -577,9 +609,13 @@ power to valid values.
Now you'll add a new hero in this form.
Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method.
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-no-reset" header="src/app/hero-form/hero-form.component.html (New Hero button)"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-no-reset" header="src/app/hero-form/hero-form.component.html (New Hero button)">
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="new-hero" header="src/app/hero-form/hero-form.component.ts (New Hero method)"></code-example>
</code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="new-hero" header="src/app/hero-form/hero-form.component.ts (New Hero method)" linenums="false">
</code-example>
Run the application again, click the *New Hero* button, and the form clears.
The *required* bars to the left of the input box are red, indicating invalid `name` and `power` properties.
@ -598,7 +634,9 @@ Replacing the hero object *did not restore the pristine state* of the form contr
You have to clear all of the flags imperatively, which you can do
by calling the form's `reset()` method after calling the `newHero()` method.
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-form-reset" header="src/app/hero-form/hero-form.component.html (Reset the form)"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-form-reset" header="src/app/hero-form/hero-form.component.html (Reset the form)">
</code-example>
Now clicking "New Hero" resets both the form and its control flags.
@ -613,7 +651,9 @@ A "form submit" is useless at the moment.
To make it useful, bind the form's `ngSubmit` event property
to the hero form component's `onSubmit()` method:
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (ngSubmit)" region="ngSubmit"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (ngSubmit)" region="ngSubmit">
</code-example>
You'd already defined a template reference variable,
`#heroForm`, and initialized it with the value "ngForm".
@ -624,7 +664,9 @@ You'll bind the form's overall validity via
the `heroForm` variable to the button's `disabled` property
using an event binding. Here's the code:
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (submit-button)" region="submit-button"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (submit-button)" region="submit-button">
</code-example>
If you run the application now, you find that the button is enabled&mdash;although
it doesn't do anything useful yet.
@ -661,13 +703,17 @@ hide the data entry area and display something else.
Wrap the form in a `<div>` and bind
its `hidden` property to the `HeroFormComponent.submitted` property.
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="edit-div"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="edit-div">
</code-example>
The main form is visible from the start because the
`submitted` property is false until you submit the form,
as this fragment from the `HeroFormComponent` shows:
<code-example path="forms/src/app/hero-form/hero-form.component.ts" header="src/app/hero-form/hero-form.component.ts (submitted)" region="submitted"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" header="src/app/hero-form/hero-form.component.ts (submitted)" region="submitted">
</code-example>
When you click the *Submit* button, the `submitted` flag becomes true and the form disappears
as planned.
@ -675,7 +721,9 @@ as planned.
Now the app needs to show something else while the form is in the submitted state.
Add the following HTML below the `<div>` wrapper you just wrote:
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="submitted"></code-example>
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="submitted">
</code-example>
There's the hero again, displayed read-only with interpolation bindings.
This `<div>` appears only while the component is in the submitted state.

View File

@ -1,5 +1,12 @@
# Frequently Used Modules
#### Prerequisites
A basic understanding of [Bootstrapping](guide/bootstrapping).
<hr>
An Angular app needs at least one module that serves as the root module.
As you add features to your app, you can add them in modules.
The following are frequently used Angular modules with examples

View File

@ -748,7 +748,7 @@ For more information, see [Schematics](guide/schematics) and [Integrating Librar
Schematics come with their own command-line tool.
Using Node 6.9 or above, install the Schematics CLI globally:
<code-example language="bash">
<code-example format="." language="bash">
npm install -g @angular-devkit/schematics-cli
</code-example>
@ -763,7 +763,7 @@ NgModules are delivered within scoped packages whose names begin with the Angula
Import a scoped package in the same way that you import a normal package.
<code-example path="architecture/src/app/app.component.ts" header="architecture/src/app/app.component.ts (import)" region="import">
<code-example path="architecture/src/app/app.component.ts" linenums="false" header="architecture/src/app/app.component.ts (import)" region="import">
</code-example>

View File

@ -19,7 +19,7 @@ an NgModule, directive-level injectors follow the structure of the component hie
The choices you make about where to configure providers lead to differences in the final bundle size, service _scope_, and service _lifetime_.
When you specify providers in the `@Injectable()` decorator of the service itself (typically at the app root level), optimization tools such as those used by the CLI's production builds can perform *tree shaking*, which removes services that aren't used by your app. Tree shaking results in smaller bundle sizes.
When you specify providers in the `@Injectable()` decorator of the service itself (typically at the app root level), optimization tools such as those used by the CLI's production builds can perform *tree shaking*, which removes services that aren't used by your app. Tree shaking results in smaller bundle sizes.
* Learn more about [tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers).
@ -29,12 +29,12 @@ You're likely to inject `UserService` in many places throughout the app and will
<header>Platform injector</header>
When you use `providedIn:'root'`, you are configuring the root injector for the _app_, which is the injector for `AppModule`.
The actual root of the entire injector hierarchy is a _platform injector_ that is the parent of app-root injectors.
The actual root of the entire injector hierarchy is a _platform injector_ that is the parent of app-root injectors.
This allows multiple apps to share a platform configuration. For example, a browser has only one URL bar, no matter how many apps you have running.
The platform injector is used internally during bootstrap, to configure platform-specific dependencies. You can configure additional platform-specific providers at the platform level by supplying `extraProviders` using the `platformBrowser()` function.
The platform injector is used internally during bootstrap, to configure platform-specific dependencies. You can configure additional platform-specific providers at the platform level by supplying `extraProviders` using the `platformBrowser()` function.
Learn more about dependency resolution through the injector hierarchy:
Learn more about dependency resolution through the injector hierarchy:
[What you always wanted to know about Angular Dependency Injection tree](https://blog.angularindepth.com/angular-dependency-injection-and-tree-shakeable-tokens-4588a8f70d5d)
@ -42,24 +42,24 @@ Learn more about dependency resolution through the injector hierarchy:
*NgModule-level* providers can be specified with `@NgModule()` `providers` metadata option, or in the `@Injectable()` `providedIn` option (with some module other than the root `AppModule`).
Use the `@NgModule()` `providers` option if a module is [lazy loaded](guide/lazy-loading-ngmodules). The module's own injector is configured with the provider when that module is loaded, and Angular can inject the corresponding services in any class it creates in that module. If you use the `@Injectable()` option `providedIn: MyLazyloadModule`, the provider could be shaken out at compile time, if it is not used anywhere else in the app.
Use the `@NgModule()` `providers` option if a module is [lazy loaded](guide/lazy-loading-ngmodules). The module's own injector is configured with the provider when that module is loaded, and Angular can inject the corresponding services in any class it creates in that module. If you use the `@Injectable()` option `providedIn: MyLazyloadModule`, the provider could be shaken out at compile time, if it is not used anywhere else in the app.
* Learn more about [tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers).
For both root-level and module-level injectors, a service instance lives for the life of the app or module, and Angular injects this one service instance in every class that needs it.
*Component-level* providers configure each component instance's own injector.
Angular can only inject the corresponding services in that component instance or one of its descendant component instances.
Angular can't inject the same service instance anywhere else.
*Component-level* providers configure each component instance's own injector.
Angular can only inject the corresponding services in that component instance or one of its descendant component instances.
Angular can't inject the same service instance anywhere else.
A component-provided service may have a limited lifetime.
Each new instance of the component gets its own instance of the service.
A component-provided service may have a limited lifetime.
Each new instance of the component gets its own instance of the service.
When the component instance is destroyed, so is that service instance.
In our sample app, `HeroComponent` is created when the application starts
In our sample app, `HeroComponent` is created when the application starts
and is never destroyed,
so the `HeroService` instance created for `HeroComponent` lives for the life of the app.
If you want to restrict `HeroService` access to `HeroComponent` and its nested
so the `HeroService` instance created for `HeroComponent` lives for the life of the app.
If you want to restrict `HeroService` access to `HeroComponent` and its nested
`HeroListComponent`, provide `HeroService` at the component level, in `HeroComponent` metadata.
* See more [examples of component-level injection](#component-injectors) below.
@ -67,32 +67,32 @@ If you want to restrict `HeroService` access to `HeroComponent` and its nested
{@a register-providers-injectable}
### @Injectable-level configuration
### @Injectable-level configuration
The `@Injectable()` decorator identifies every service class. The `providedIn` metadata option for a service class configures a specific injector (typically `root`)
to use the decorated class as a provider of the service.
When an injectable class provides its own service to the `root` injector, the service is available anywhere the class is imported.
to use the decorated class as a provider of the service.
When an injectable class provides its own service to the `root` injector, the service is available anywhere the class is imported.
The following example configures a provider for `HeroService` using the `@Injectable()` decorator on the class.
<code-example path="dependency-injection/src/app/heroes/hero.service.0.ts" header="src/app/heroes/heroes.service.ts"></code-example>
<code-example path="dependency-injection/src/app/heroes/hero.service.0.ts" header="src/app/heroes/heroes.service.ts" linenums="false"> </code-example>
This configuration tells Angular that the app's root injector is responsible for creating an
This configuration tells Angular that the app's root injector is responsible for creating an
instance of `HeroService` by invoking its constructor,
and for making that instance available across the application.
and for making that instance available across the application.
Providing a service with the app's root injector is a typical case,
and the CLI sets up this kind of a provider automatically for you
when generating a new service.
when generating a new service.
However, you might not always want to provide your service at the root level.
You might, for instance, want users to explicitly opt-in to using the service.
Instead of specifying the `root` injector, you can set `providedIn` to a specific NgModule.
Instead of specifying the `root` injector, you can set `providedIn` to a specific NgModule.
For example, in the following excerpt, the `@Injectable()` decorator configures a provider
that is available in any injector that includes the `HeroModule`.
<code-example path="dependency-injection/src/app/heroes/hero.service.4.ts" header="src/app/heroes/hero.service.ts"></code-example>
<code-example path="dependency-injection/src/app/heroes/hero.service.4.ts" header="src/app/heroes/hero.service.ts" linenums="false"> </code-example>
This is generally no different from configuring the injector of the NgModule itself,
except that the service is tree-shakable if the NgModule doesn't use it.
@ -108,16 +108,18 @@ and leave it up to the app whether to provide the service.
You can configure a provider at the module level using the `providers` metadata option for a non-root NgModule, in order to limit the scope of the provider to that module.
This is the equivalent of specifying the non-root module in the `@Injectable()` metadata, except that the service provided via `providers` is not tree-shakable.
You generally don't need to specify `AppModule` with `providedIn`, because the app's `root` injector is the `AppModule` injector.
You generally don't need to specify `AppModule` with `providedIn`, because the app's `root` injector is the `AppModule` injector.
However, if you configure a app-wide provider in the `@NgModule()` metadata for `AppModule`,
it overrides one configured for `root` in the `@Injectable()` metadata.
You can do this to configure a non-default provider of a service that is shared with multiple apps.
it overrides one configured for `root` in the `@Injectable()` metadata.
You can do this to configure a non-default provider of a service that is shared with multiple apps.
Here is an example of the case where the component router configuration includes
a non-default [location strategy](guide/router#location-strategy) by listing its provider
in the `providers` list of the `AppModule`.
<code-example path="dependency-injection-in-action/src/app/app.module.ts" region="providers" header="src/app/app.module.ts (providers)"></code-example>
<code-example path="dependency-injection-in-action/src/app/app.module.ts" region="providers" header="src/app/app.module.ts (providers)" linenums="false">
</code-example>
{@a register-providers-component}
@ -128,18 +130,19 @@ Individual components within an NgModule have their own injectors.
You can limit the scope of a provider to a component and its children
by configuring the provider at the component level using the `@Component` metadata.
The following example is a revised `HeroesComponent` that specifies `HeroService` in its `providers` array. `HeroService` can provide heroes to instances of this component, or to any child component instances.
The following example is a revised `HeroesComponent` that specifies `HeroService` in its `providers` array. `HeroService` can provide heroes to instances of this component, or to any child component instances.
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" header="src/app/heroes/heroes.component.ts"></code-example>
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" header="src/app/heroes/heroes.component.ts" linenums="false">
</code-example>
### Element injectors
An injector does not actually belong to a component, but rather to the component instance's anchor element in the DOM. A different component instance on a different DOM element uses a different injector.
Components are a special type of directive, and the `providers` property of
`@Component()` is inherited from `@Directive()`.
`@Component()` is inherited from `@Directive()`.
Directives can also have dependencies, and you can configure providers
in their `@Directive()` metadata.
in their `@Directive()` metadata.
When you configure a provider for a component or directive using the `providers` property, that provider belongs to the injector for the anchor DOM element. Components and directives on the same element share an injector.
<!--- TBD with examples
@ -165,16 +168,16 @@ When a component requests a dependency, Angular tries to satisfy that dependency
If the component's injector lacks the provider, it passes the request up to its parent component's injector.
If that injector can't satisfy the request, it passes the request along to the next parent injector up the tree.
The requests keep bubbling up until Angular finds an injector that can handle the request or runs out of ancestor injectors.
If it runs out of ancestors, Angular throws an error.
If it runs out of ancestors, Angular throws an error.
If you have registered a provider for the same DI token at different levels, the first one Angular encounters is the one it uses to provide the dependency. If, for example, a provider is registered locally in the component that needs a service, Angular doesn't look for another provider of the same service.
If you have registered a provider for the same DI token at different levels, the first one Angular encounters is the one it uses to provide the dependency. If, for example, a provider is registered locally in the component that needs a service, Angular doesn't look for another provider of the same service.
<div class="alert is-helpful">
You can cap the bubbling by adding the `@Host()` parameter decorator on the dependant-service parameter
in a component's constructor.
The hunt for providers stops at the injector for the host element of the component.
in a component's constructor.
The hunt for providers stops at the injector for the host element of the component.
* See an [example](guide/dependency-injection-in-action#qualify-dependency-lookup) of using `@Host` together with `@Optional`, another parameter decorator that lets you handle the null case if no provider is found.
@ -194,7 +197,7 @@ The guide sample offers some scenarios where you might want to do so.
### Scenario: service isolation
Architectural reasons may lead you to restrict access to a service to the application domain where it belongs.
Architectural reasons may lead you to restrict access to a service to the application domain where it belongs.
For example, the guide sample includes a `VillainsListComponent` that displays a list of villains.
It gets those villains from a `VillainsService`.
@ -204,7 +207,9 @@ that would make the `VillainsService` available everywhere in the application, i
Instead, you can provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this:
<code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" header="src/app/villains-list.component.ts (metadata)" region="metadata"></code-example>
<code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" linenums="false" header="src/app/villains-list.component.ts (metadata)" region="metadata">
</code-example>
By providing `VillainsService` in the `VillainsListComponent` metadata and nowhere else,
the service becomes available only in the `VillainsListComponent` and its sub-component tree.
@ -268,7 +273,9 @@ Every component would share the same service instance, and each component would
To prevent this, we configure the component-level injector of `HeroTaxReturnComponent` to provide the service, using the `providers` property in the component metadata.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" header="src/app/hero-tax-return.component.ts (providers)" region="providers"></code-example>
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" linenums="false" header="src/app/hero-tax-return.component.ts (providers)" region="providers">
</code-example>
The `HeroTaxReturnComponent` has its own provider of the `HeroTaxReturnService`.
Recall that every component _instance_ has its own injector.

View File

@ -11,7 +11,7 @@ You can run the <live-example></live-example> that accompanies this guide.
<div class="alert is-helpful">
The sample app does not require a data server.
It relies on the
It relies on the
[Angular _in-memory-web-api_](https://github.com/angular/in-memory-web-api/blob/master/README.md),
which replaces the _HttpClient_ module's `HttpBackend`.
The replacement service simulates the behavior of a REST-like backend.
@ -22,50 +22,50 @@ Look at the `AppModule` _imports_ to see how it is configured.
## Setup
Before you can use the `HttpClient`, you need to import the Angular `HttpClientModule`.
Before you can use the `HttpClient`, you need to import the Angular `HttpClientModule`.
Most apps do so in the root `AppModule`.
<code-example
<code-example
path="http/src/app/app.module.ts"
region="sketch"
header="app/app.module.ts (excerpt)">
header="app/app.module.ts (excerpt)" linenums="false">
</code-example>
Having imported `HttpClientModule` into the `AppModule`, you can inject the `HttpClient`
into an application class as shown in the following `ConfigService` example.
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="proto"
header="app/config/config.service.ts (excerpt)">
header="app/config/config.service.ts (excerpt)" linenums="false">
</code-example>
## Getting JSON data
Applications often request JSON data from the server.
For example, the app might need a configuration file on the server, `config.json`,
Applications often request JSON data from the server.
For example, the app might need a configuration file on the server, `config.json`,
that specifies resource URLs.
<code-example
<code-example
path="http/src/assets/config.json"
header="assets/config.json">
header="assets/config.json" linenums="false">
</code-example>
The `ConfigService` fetches this file with a `get()` method on `HttpClient`.
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="getConfig_1"
header="app/config/config.service.ts (getConfig v.1)">
header="app/config/config.service.ts (getConfig v.1)" linenums="false">
</code-example>
A component, such as `ConfigComponent`, injects the `ConfigService` and calls
the `getConfig` service method.
<code-example
<code-example
path="http/src/app/config/config.component.ts"
region="v1"
header="app/config/config.component.ts (showConfig v.1)">
header="app/config/config.component.ts (showConfig v.1)" linenums="false">
</code-example>
Because the service method returns an `Observable` of configuration data,
@ -93,12 +93,12 @@ the component, even in simple cases like this one.
The subscribe callback above requires bracket notation to extract the data values.
<code-example
<code-example
path="http/src/app/config/config.component.ts"
region="v1_callback">
region="v1_callback" linenums="false">
</code-example>
You can't write `data.heroesUrl` because TypeScript correctly complains that the `data` object from the service does not have a `heroesUrl` property.
You can't write `data.heroesUrl` because TypeScript correctly complains that the `data` object from the service does not have a `heroesUrl` property.
The `HttpClient.get()` method parsed the JSON server response into the anonymous `Object` type. It doesn't know what the shape of that object is.
@ -106,48 +106,48 @@ You can tell `HttpClient` the type of the response to make consuming the output
First, define an interface with the correct shape:
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="config-interface">
region="config-interface" linenums="false">
</code-example>
Then, specify that interface as the `HttpClient.get()` call's type parameter in the service:
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="getConfig_2"
header="app/config/config.service.ts (getConfig v.2)">
region="getConfig_2"
header="app/config/config.service.ts (getConfig v.2)" linenums="false">
</code-example>
The callback in the updated component method receives a typed data object, which is
easier and safer to consume:
<code-example
<code-example
path="http/src/app/config/config.component.ts"
region="v2"
header="app/config/config.component.ts (showConfig v.2)">
header="app/config/config.component.ts (showConfig v.2)" linenums="false">
</code-example>
### Reading the full response
The response body doesn't return all the data you may need. Sometimes servers return special headers or status codes to indicate certain conditions that are important to the application workflow.
The response body doesn't return all the data you may need. Sometimes servers return special headers or status codes to indicate certain conditions that are important to the application workflow.
Tell `HttpClient` that you want the full response with the `observe` option:
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="getConfigResponse">
region="getConfigResponse" linenums="false">
</code-example>
Now `HttpClient.get()` returns an `Observable` of typed `HttpResponse` rather than just the JSON data.
The component's `showConfigResponse()` method displays the response headers as well as the configuration:
<code-example
<code-example
path="http/src/app/config/config.component.ts"
region="showConfigResponse"
region="showConfigResponse"
header="app/config/config.component.ts (showConfigResponse)"
>
linenums="false">
</code-example>
As you can see, the response object has a `body` property of the correct type.
@ -158,11 +158,11 @@ What happens if the request fails on the server, or if a poor network connection
You _could_ handle in the component by adding a second callback to the `.subscribe()`:
<code-example
<code-example
path="http/src/app/config/config.component.ts"
region="v3"
region="v3"
header="app/config/config.component.ts (showConfig v.3 with error handling)"
>
linenums="false">
</code-example>
It's certainly a good idea to give the user some kind of feedback when data access fails.
@ -180,15 +180,15 @@ Or something could go wrong on the client-side such as a network error that prev
The `HttpClient` captures both kinds of errors in its `HttpErrorResponse` and you can inspect that response to figure out what really happened.
Error inspection, interpretation, and resolution is something you want to do in the _service_,
not in the _component_.
Error inspection, interpretation, and resolution is something you want to do in the _service_,
not in the _component_.
You might first devise an error handler like this one:
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="handleError"
header="app/config/config.service.ts (handleError)">
region="handleError"
header="app/config/config.service.ts (handleError)" linenums="false">
</code-example>
Notice that this handler returns an RxJS [`ErrorObservable`](#rxjs) with a user-friendly error message.
@ -198,10 +198,10 @@ even a "bad" one.
Now you take the `Observables` returned by the `HttpClient` methods
and _pipe them through_ to the error handler.
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="getConfig_3"
header="app/config/config.service.ts (getConfig v.3 with error handler)">
region="getConfig_3"
header="app/config/config.service.ts (getConfig v.3 with error handler)" linenums="false">
</code-example>
### `retry()`
@ -215,10 +215,10 @@ The simplest is called `retry()` and it automatically re-subscribes to a failed
_Pipe_ it onto the `HttpClient` method result just before the error handler.
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="getConfig"
header="app/config/config.service.ts (getConfig with retry)">
region="getConfig"
header="app/config/config.service.ts (getConfig with retry)" linenums="false">
</code-example>
{@a rxjs}
@ -229,17 +229,17 @@ You will encounter more RxJS artifacts as you continue below.
[RxJS](http://reactivex.io/rxjs/) is a library for composing asynchronous and callback-based code
in a _functional, reactive style_.
Many Angular APIs, including `HttpClient`, produce and consume RxJS `Observables`.
Many Angular APIs, including `HttpClient`, produce and consume RxJS `Observables`.
RxJS itself is out-of-scope for this guide. You will find many learning resources on the web.
While you can get by with a minimum of RxJS knowledge, you'll want to grow your RxJS skills over time in order to use `HttpClient` effectively.
If you're following along with these code snippets, note that you must import the RxJS observable and operator symbols that appear in those snippets. These `ConfigService` imports are typical.
<code-example
<code-example
path="http/src/app/config/config.service.ts"
region="rxjs-imports"
header="app/config/config.service.ts (RxJS imports)">
region="rxjs-imports"
header="app/config/config.service.ts (RxJS imports)" linenums="false">
</code-example>
## Requesting non-JSON data
@ -247,24 +247,24 @@ If you're following along with these code snippets, note that you must import th
Not all APIs return JSON data. In this next example,
a `DownloaderService` method reads a text file from the server
and logs the file contents, before returning those contents to the caller
as an `Observable<string>`.
as an `Observable<string>`.
<code-example
<code-example
path="http/src/app/downloader/downloader.service.ts"
region="getTextFile"
header="app/downloader/downloader.service.ts (getTextFile)">
region="getTextFile"
header="app/downloader/downloader.service.ts (getTextFile)" linenums="false">
</code-example>
`HttpClient.get()` returns a string rather than the default JSON because of the `responseType` option.
The RxJS `tap` operator (as in "wiretap") lets the code inspect good and error values passing through the observable without disturbing them.
The RxJS `tap` operator (as in "wiretap") lets the code inspect good and error values passing through the observable without disturbing them.
A `download()` method in the `DownloaderComponent` initiates the request by subscribing to the service method.
<code-example
<code-example
path="http/src/app/downloader/downloader.component.ts"
region="download"
header="app/downloader/downloader.component.ts (download)">
region="download"
header="app/downloader/downloader.component.ts (download)" linenums="false">
</code-example>
## Sending data to the server
@ -279,28 +279,28 @@ The following sections excerpt methods of the sample's `HeroesService`.
### Adding headers
Many servers require extra headers for save operations.
For example, they may require a "Content-Type" header to explicitly declare
For example, they may require a "Content-Type" header to explicitly declare
the MIME type of the request body.
Or perhaps the server requires an authorization token.
The `HeroesService` defines such headers in an `httpOptions` object that will be passed
to every `HttpClient` save method.
<code-example
<code-example
path="http/src/app/heroes/heroes.service.ts"
region="http-options"
header="app/heroes/heroes.service.ts (httpOptions)">
region="http-options"
header="app/heroes/heroes.service.ts (httpOptions)" linenums="false">
</code-example>
### Making a POST request
Apps often POST data to a server. They POST when submitting a form.
Apps often POST data to a server. They POST when submitting a form.
In the following example, the `HeroesService` posts when adding a hero to the database.
<code-example
<code-example
path="http/src/app/heroes/heroes.service.ts"
region="addHero"
header="app/heroes/heroes.service.ts (addHero)">
region="addHero"
header="app/heroes/heroes.service.ts (addHero)" linenums="false">
</code-example>
The `HttpClient.post()` method is similar to `get()` in that it has a type parameter
@ -314,13 +314,13 @@ It takes two more parameters:
Of course it catches errors in much the same manner [described above](#error-details).
The `HeroesComponent` initiates the actual POST operation by subscribing to
The `HeroesComponent` initiates the actual POST operation by subscribing to
the `Observable` returned by this service method.
<code-example
<code-example
path="http/src/app/heroes/heroes.component.ts"
region="add-hero-subscribe"
header="app/heroes/heroes.component.ts (addHero)">
region="add-hero-subscribe"
header="app/heroes/heroes.component.ts (addHero)" linenums="false">
</code-example>
When the server responds successfully with the newly added hero, the component adds
@ -331,22 +331,22 @@ that hero to the displayed `heroes` list.
This application deletes a hero with the `HttpClient.delete` method by passing the hero's id
in the request URL.
<code-example
<code-example
path="http/src/app/heroes/heroes.service.ts"
region="deleteHero"
header="app/heroes/heroes.service.ts (deleteHero)">
region="deleteHero"
header="app/heroes/heroes.service.ts (deleteHero)" linenums="false">
</code-example>
The `HeroesComponent` initiates the actual DELETE operation by subscribing to
The `HeroesComponent` initiates the actual DELETE operation by subscribing to
the `Observable` returned by this service method.
<code-example
<code-example
path="http/src/app/heroes/heroes.component.ts"
region="delete-hero-subscribe"
header="app/heroes/heroes.component.ts (deleteHero)">
region="delete-hero-subscribe"
header="app/heroes/heroes.component.ts (deleteHero)" linenums="false">
</code-example>
The component isn't expecting a result from the delete operation, so it subscribes without a callback. Even though you are not using the result, you still have to subscribe. Calling the `subscribe()` method _executes_ the observable, which is what initiates the DELETE request.
The component isn't expecting a result from the delete operation, so it subscribes without a callback. Even though you are not using the result, you still have to subscribe. Calling the `subscribe()` method _executes_ the observable, which is what initiates the DELETE request.
<div class="alert is-important">
@ -355,9 +355,9 @@ You must call _subscribe()_ or nothing happens. Just calling `HeroesService.dele
</div>
<code-example
<code-example
path="http/src/app/heroes/heroes.component.ts"
region="delete-hero-no-subscribe">
region="delete-hero-no-subscribe" linenums="false">
</code-example>
{@a always-subscribe}
@ -400,10 +400,10 @@ req.subscribe();
An app will send a PUT request to completely replace a resource with updated data.
The following `HeroesService` example is just like the POST example.
<code-example
<code-example
path="http/src/app/heroes/heroes.service.ts"
region="updateHero"
header="app/heroes/heroes.service.ts (updateHero)">
region="updateHero"
header="app/heroes/heroes.service.ts (updateHero)" linenums="false">
</code-example>
For the reasons [explained above](#always-subscribe), the caller (`HeroesComponent.update()` in this case) must `subscribe()` to the observable returned from the `HttpClient.put()`
@ -427,15 +427,15 @@ You can do more.
You can't directly modify the existing headers within the previous options
object because instances of the `HttpHeaders` class are immutable.
Use the `set()` method instead.
Use the `set()` method instead.
It returns a clone of the current instance with the new changes applied.
Here's how you might update the authorization header (after the old token expired)
Here's how you might update the authorization header (after the old token expired)
before making the next request.
<code-example
<code-example
path="http/src/app/heroes/heroes.service.ts"
region="update-headers">
region="update-headers" linenums="false">
</code-example>
#### URL Parameters
@ -443,9 +443,9 @@ before making the next request.
Adding URL search parameters works a similar way.
Here is a `searchHeroes` method that queries for heroes whose names contain the search term.
<code-example
<code-example
path="http/src/app/heroes/heroes.service.ts"
region="searchHeroes">
region="searchHeroes" linenums="false">
</code-example>
If there is a search term, the code constructs an options object with an HTML URL-encoded search parameter. If the term were "foo", the GET request URL would be `api/heroes/?name=foo`.
@ -461,9 +461,9 @@ a search request for a package with that name to the NPM web API.
Here's a pertinent excerpt from the template:
<code-example
<code-example
path="http/src/app/package-search/package-search.component.html"
region="search"
region="search"
header="app/package-search/package-search.component.html (search)">
</code-example>
@ -473,9 +473,9 @@ Sending a request for every keystroke could be expensive.
It's better to wait until the user stops typing and then send a request.
That's easy to implement with RxJS operators, as shown in this excerpt.
<code-example
<code-example
path="http/src/app/package-search/package-search.component.ts"
region="debounce"
region="debounce"
header="app/package-search/package-search.component.ts (excerpt)">
</code-example>
@ -514,7 +514,7 @@ The `switchMap()` operator has three important characteristics.
it cancels that request and sends a new one.
3. It returns service responses in their original request order, even if the
server returns them out of order.
server returns them out of order.
<div class="alert is-helpful">
@ -526,14 +526,14 @@ consider moving it to a utility function or into the `PackageSearchService` itse
### Intercepting requests and responses
_HTTP Interception_ is a major feature of `@angular/common/http`.
_HTTP Interception_ is a major feature of `@angular/common/http`.
With interception, you declare _interceptors_ that inspect and transform HTTP requests from your application to the server.
The same interceptors may also inspect and transform the server's responses on their way back to the application.
Multiple interceptors form a _forward-and-backward_ chain of request/response handlers.
Interceptors can perform a variety of _implicit_ tasks, from authentication to logging, in a routine, standard way, for every HTTP request/response.
Interceptors can perform a variety of _implicit_ tasks, from authentication to logging, in a routine, standard way, for every HTTP request/response.
Without interception, developers would have to implement these tasks _explicitly_
Without interception, developers would have to implement these tasks _explicitly_
for each `HttpClient` method call.
#### Write an interceptor
@ -541,12 +541,13 @@ for each `HttpClient` method call.
To implement an interceptor, declare a class that implements the `intercept()` method of the `HttpInterceptor` interface.
Here is a do-nothing _noop_ interceptor that simply passes the request through without touching it:
<code-example
<code-example
path="http/src/app/http-interceptors/noop-interceptor.ts"
header="app/http-interceptors/noop-interceptor.ts">
header="app/http-interceptors/noop-interceptor.ts"
linenums="false">
</code-example>
The `intercept` method transforms a request into an `Observable` that eventually returns the HTTP response.
The `intercept` method transforms a request into an `Observable` that eventually returns the HTTP response.
In this sense, each interceptor is fully capable of handling the request entirely by itself.
Most interceptors inspect the request on the way in and forward the (perhaps altered) request to the `handle()` method of the `next` object which implements the [`HttpHandler`](api/common/http/HttpHandler) interface.
@ -563,22 +564,22 @@ This _no-op_ interceptor simply calls `next.handle()` with the original request
#### The _next_ object
The `next` object represents the next interceptor in the chain of interceptors.
The `next` object represents the next interceptor in the chain of interceptors.
The final `next` in the chain is the `HttpClient` backend handler that sends the request to the server and receives the server's response.
Most interceptors call `next.handle()` so that the request flows through to the next interceptor and, eventually, the backend handler.
An interceptor _could_ skip calling `next.handle()`, short-circuit the chain, and [return its own `Observable`](#caching) with an artificial server response.
An interceptor _could_ skip calling `next.handle()`, short-circuit the chain, and [return its own `Observable`](#caching) with an artificial server response.
This is a common middleware pattern found in frameworks such as Express.js.
#### Provide the interceptor
The `NoopInterceptor` is a service managed by Angular's [dependency injection (DI)](guide/dependency-injection) system.
The `NoopInterceptor` is a service managed by Angular's [dependency injection (DI)](guide/dependency-injection) system.
Like other services, you must provide the interceptor class before the app can use it.
Because interceptors are (optional) dependencies of the `HttpClient` service,
you must provide them in the same injector (or a parent of the injector) that provides `HttpClient`.
Because interceptors are (optional) dependencies of the `HttpClient` service,
you must provide them in the same injector (or a parent of the injector) that provides `HttpClient`.
Interceptors provided _after_ DI creates the `HttpClient` are ignored.
This app provides `HttpClient` in the app's root injector, as a side-effect of importing the `HttpClientModule` in `AppModule`.
@ -587,35 +588,35 @@ You should provide interceptors in `AppModule` as well.
After importing the `HTTP_INTERCEPTORS` injection token from `@angular/common/http`,
write the `NoopInterceptor` provider like this:
<code-example
<code-example
path="http/src/app/http-interceptors/index.ts"
region="noop-provider">
region="noop-provider" linenums="false">
</code-example>
Note the `multi: true` option.
This required setting tells Angular that `HTTP_INTERCEPTORS` is a token for a _multiprovider_
Note the `multi: true` option.
This required setting tells Angular that `HTTP_INTERCEPTORS` is a token for a _multiprovider_
that injects an array of values, rather than a single value.
You _could_ add this provider directly to the providers array of the `AppModule`.
However, it's rather verbose and there's a good chance that
However, it's rather verbose and there's a good chance that
you'll create more interceptors and provide them in the same way.
You must also pay [close attention to the order](#interceptor-order)
You must also pay [close attention to the order](#interceptor-order)
in which you provide these interceptors.
Consider creating a "barrel" file that gathers all the interceptor providers into an `httpInterceptorProviders` array, starting with this first one, the `NoopInterceptor`.
<code-example
<code-example
path="http/src/app/http-interceptors/index.ts"
region="interceptor-providers"
header="app/http-interceptors/index.ts">
header="app/http-interceptors/index.ts" linenums="false">
</code-example>
Then import and add it to the `AppModule` _providers array_ like this:
<code-example
<code-example
path="http/src/app/app.module.ts"
region="interceptor-providers"
header="app/app.module.ts (interceptor providers)">
header="app/app.module.ts (interceptor providers)" linenums="false">
</code-example>
As you create new interceptors, add them to the `httpInterceptorProviders` array and
@ -646,8 +647,8 @@ That's because interceptors work at a lower level than those `HttpClient` method
Many interceptors are only concerned with the outgoing request and simply return the event stream from `next.handle()` without modifying it.
But interceptors that examine and modify the response from `next.handle()`
will see all of these events.
But interceptors that examine and modify the response from `next.handle()`
will see all of these events.
Your interceptor should return _every event untouched_ unless it has a _compelling reason to do otherwise_.
#### Immutability
@ -659,39 +660,39 @@ rendering them largely immutable.
They are immutable for a good reason: the app may retry a request several times before it succeeds, which means that the interceptor chain may re-process the same request multiple times.
If an interceptor could modify the original request object, the re-tried operation would start from the modified request rather than the original. Immutability ensures that interceptors see the same request for each try.
TypeScript will prevent you from setting `HttpRequest` readonly properties.
TypeScript will prevent you from setting `HttpRequest` readonly properties.
```javascript
// Typescript disallows the following assignment because req.url is readonly
req.url = req.url.replace('http://', 'https://');
```
To alter the request, clone it first and modify the clone before passing it to `next.handle()`.
To alter the request, clone it first and modify the clone before passing it to `next.handle()`.
You can clone and modify the request in a single step as in this example.
<code-example
<code-example
path="http/src/app/http-interceptors/ensure-https-interceptor.ts"
region="excerpt"
header="app/http-interceptors/ensure-https-interceptor.ts (excerpt)">
region="excerpt"
header="app/http-interceptors/ensure-https-interceptor.ts (excerpt)" linenums="false">
</code-example>
The `clone()` method's hash argument allows you to mutate specific properties of the request while copying the others.
##### The request body
The `readonly` assignment guard can't prevent deep updates and, in particular,
The `readonly` assignment guard can't prevent deep updates and, in particular,
it can't prevent you from modifying a property of a request body object.
```javascript
req.body.name = req.body.name.trim(); // bad idea!
```
If you must mutate the request body, copy it first, change the copy,
If you must mutate the request body, copy it first, change the copy,
`clone()` the request, and set the clone's body with the new body, as in the following example.
<code-example
<code-example
path="http/src/app/http-interceptors/trim-name-interceptor.ts"
region="excerpt"
header="app/http-interceptors/trim-name-interceptor.ts (excerpt)">
region="excerpt"
header="app/http-interceptors/trim-name-interceptor.ts (excerpt)" linenums="false">
</code-example>
##### Clearing the request body
@ -709,21 +710,21 @@ If you set the cloned request body to `null`, Angular knows you intend to clear
#### Set default headers
Apps often use an interceptor to set default headers on outgoing requests.
Apps often use an interceptor to set default headers on outgoing requests.
The sample app has an `AuthService` that produces an authorization token.
Here is its `AuthInterceptor` that injects that service to get the token and
adds an authorization header with that token to every outgoing request:
<code-example
<code-example
path="http/src/app/http-interceptors/auth-interceptor.ts"
header="app/http-interceptors/auth-interceptor.ts">
</code-example>
The practice of cloning a request to set new headers is so common that
The practice of cloning a request to set new headers is so common that
there's a `setHeaders` shortcut for it:
<code-example
<code-example
path="http/src/app/http-interceptors/auth-interceptor.ts"
region="set-header-shortcut">
</code-example>
@ -736,16 +737,16 @@ An interceptor that alters headers can be used for a number of different operati
#### Logging
Because interceptors can process the request and response _together_, they can do things like time and log
an entire HTTP operation.
Because interceptors can process the request and response _together_, they can do things like time and log
an entire HTTP operation.
Consider the following `LoggingInterceptor`, which captures the time of the request,
the time of the response, and logs the outcome with the elapsed time
with the injected `MessageService`.
<code-example
<code-example
path="http/src/app/http-interceptors/logging-interceptor.ts"
region="excerpt"
region="excerpt"
header="app/http-interceptors/logging-interceptor.ts)">
</code-example>
@ -760,20 +761,20 @@ Neither `tap` nor `finalize` touch the values of the observable stream returned
Interceptors can handle requests by themselves, without forwarding to `next.handle()`.
For example, you might decide to cache certain requests and responses to improve performance.
You can delegate caching to an interceptor without disturbing your existing data services.
You can delegate caching to an interceptor without disturbing your existing data services.
The `CachingInterceptor` demonstrates this approach.
<code-example
<code-example
path="http/src/app/http-interceptors/caching-interceptor.ts"
region="v1"
header="app/http-interceptors/caching-interceptor.ts)">
region="v1"
header="app/http-interceptors/caching-interceptor.ts)" linenums="false">
</code-example>
The `isCachable()` function determines if the request is cachable.
In this sample, only GET requests to the npm package search api are cachable.
If the request is not cachable, the interceptor simply forwards the request
If the request is not cachable, the interceptor simply forwards the request
to the next handler in the chain.
If a cachable request is found in the cache, the interceptor returns an `of()` _observable_ with
@ -782,7 +783,7 @@ the cached response, by-passing the `next` handler (and all other interceptors d
If a cachable request is not in cache, the code calls `sendRequest`.
{@a send-request}
<code-example
<code-example
path="http/src/app/http-interceptors/caching-interceptor.ts"
region="send-request">
</code-example>
@ -798,16 +799,16 @@ It _pipes_ the response through the `tap()` operator,
whose callback adds the response to the cache.
The original response continues untouched back up through the chain of interceptors
to the application caller.
to the application caller.
Data services, such as `PackageSearchService`, are unaware that
Data services, such as `PackageSearchService`, are unaware that
some of their `HttpClient` requests actually return cached responses.
{@a cache-refresh}
#### Return a multi-valued _Observable_
The `HttpClient.get()` method normally returns an _observable_
that either emits the data or an error.
The `HttpClient.get()` method normally returns an _observable_
that either emits the data or an error.
Some folks describe it as a "_one and done_" observable.
But an interceptor can change this to an _observable_ that emits more than once.
@ -816,7 +817,7 @@ A revised version of the `CachingInterceptor` optionally returns an _observable_
immediately emits the cached response, sends the request to the NPM web API anyway,
and emits again later with the updated search results.
<code-example
<code-example
path="http/src/app/http-interceptors/caching-interceptor.ts"
region="intercept-refresh">
</code-example>
@ -832,8 +833,8 @@ and adds it to the request before calling `HttpClient.get()`.
</div>
The revised `CachingInterceptor` sets up a server request
whether there's a cached value or not,
The revised `CachingInterceptor` sets up a server request
whether there's a cached value or not,
using the same `sendRequest()` method described [above](#send-request).
The `results$` observable will make the request when subscribed.
@ -848,15 +849,15 @@ Subscribers see a sequence of _two_ responses.
### Listening to progress events
Sometimes applications transfer large amounts of data and those transfers can take a long time.
File uploads are a typical example.
File uploads are a typical example.
Give the users a better experience by providing feedback on the progress of such transfers.
To make a request with progress events enabled, you can create an instance of `HttpRequest`
To make a request with progress events enabled, you can create an instance of `HttpRequest`
with the `reportProgress` option set true to enable tracking of progress events.
<code-example
<code-example
path="http/src/app/uploader/uploader.service.ts"
region="upload-request"
region="upload-request"
header="app/uploader/uploader.service.ts (upload request)">
</code-example>
@ -872,24 +873,24 @@ When using [`HttpClient#request()`](api/common/http/HttpClient#request) with an
Next, pass this request object to the `HttpClient.request()` method, which
returns an `Observable` of `HttpEvents`, the same events processed by interceptors:
<code-example
<code-example
path="http/src/app/uploader/uploader.service.ts"
region="upload-body"
header="app/uploader/uploader.service.ts (upload body)">
region="upload-body"
header="app/uploader/uploader.service.ts (upload body)" linenums="false">
</code-example>
The `getEventMessage` method interprets each type of `HttpEvent` in the event stream.
<code-example
<code-example
path="http/src/app/uploader/uploader.service.ts"
region="getEventMessage"
header="app/uploader/uploader.service.ts (getEventMessage)">
region="getEventMessage"
header="app/uploader/uploader.service.ts (getEventMessage)" linenums="false">
</code-example>
<div class="alert is-helpful">
The sample app for this guide doesn't have a server that accepts uploaded files.
The `UploadInterceptor` in `app/http-interceptors/upload-interceptor.ts`
The `UploadInterceptor` in `app/http-interceptors/upload-interceptor.ts`
intercepts and short-circuits upload requests
by returning an observable of simulated events.
@ -910,44 +911,45 @@ In order to prevent collisions in environments where multiple Angular apps share
<div class="alert is-important">
*Note that `HttpClient` supports only the client half of the XSRF protection scheme.*
Your backend service must be configured to set the cookie for your page, and to verify that
the header is present on all eligible requests.
*Note that `HttpClient` supports only the client half of the XSRF protection scheme.*
Your backend service must be configured to set the cookie for your page, and to verify that
the header is present on all eligible requests.
If not, Angular's default protection will be ineffective.
</div>
### Configuring custom cookie/header names
If your backend service uses different names for the XSRF token cookie or header,
If your backend service uses different names for the XSRF token cookie or header,
use `HttpClientXsrfModule.withOptions()` to override the defaults.
<code-example
<code-example
path="http/src/app/app.module.ts"
region="xsrf">
region="xsrf"
linenums="false">
</code-example>
## Testing HTTP requests
Like any external dependency, the HTTP backend needs to be mocked
so your tests can simulate interaction with a remote server.
The `@angular/common/http/testing` library makes
so your tests can simulate interaction with a remote server.
The `@angular/common/http/testing` library makes
setting up such mocking straightforward.
### Mocking philosophy
Angular's HTTP testing library is designed for a pattern of testing wherein
Angular's HTTP testing library is designed for a pattern of testing wherein
the app executes code and makes requests first.
Then a test expects that certain requests have or have not been made,
performs assertions against those requests,
Then a test expects that certain requests have or have not been made,
performs assertions against those requests,
and finally provide responses by "flushing" each expected request.
At the end, tests may verify that the app has made no unexpected requests.
<div class="alert is-helpful">
You can run <live-example stackblitz="specs">these sample tests</live-example>
You can run <live-example stackblitz="specs">these sample tests</live-example>
in a live coding environment.
The tests described in this guide are in `src/testing/http-client.spec.ts`.
@ -958,23 +960,23 @@ There are also tests of an application data service that call `HttpClient` in
### Setup
To begin testing calls to `HttpClient`,
To begin testing calls to `HttpClient`,
import the `HttpClientTestingModule` and the mocking controller, `HttpTestingController`,
along with the other symbols your tests require.
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="imports"
header="app/testing/http-client.spec.ts (imports)">
region="imports"
header="app/testing/http-client.spec.ts (imports)" linenums="false">
</code-example>
Then add the `HttpClientTestingModule` to the `TestBed` and continue with
the setup of the _service-under-test_.
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="setup"
header="app/testing/http-client.spec.ts(setup)">
region="setup"
header="app/testing/http-client.spec.ts(setup)" linenums="false">
</code-example>
Now requests made in the course of your tests will hit the testing backend instead of the normal backend.
@ -984,44 +986,47 @@ so they can be referenced during the tests.
### Expecting and answering requests
Now you can write a test that expects a GET Request to occur and provides a mock response.
Now you can write a test that expects a GET Request to occur and provides a mock response.
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="get-test"
header="app/testing/http-client.spec.ts(httpClient.get)">
region="get-test"
header="app/testing/http-client.spec.ts(httpClient.get)" linenums="false">
</code-example>
The last step, verifying that no requests remain outstanding, is common enough for you to move it into an `afterEach()` step:
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="afterEach">
region="afterEach"
linenums="false">
</code-example>
#### Custom request expectations
If matching by URL isn't sufficient, it's possible to implement your own matching function.
If matching by URL isn't sufficient, it's possible to implement your own matching function.
For example, you could look for an outgoing request that has an authorization header:
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="predicate">
region="predicate"
linenums="false">
</code-example>
As with the previous `expectOne()`,
As with the previous `expectOne()`,
the test will fail if 0 or 2+ requests satisfy this predicate.
#### Handling more than one request
If you need to respond to duplicate requests in your test, use the `match()` API instead of `expectOne()`.
It takes the same arguments but returns an array of matching requests.
Once returned, these requests are removed from future matching and
It takes the same arguments but returns an array of matching requests.
Once returned, these requests are removed from future matching and
you are responsible for flushing and verifying them.
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="multi-request">
region="multi-request"
linenums="false">
</code-example>
### Testing for errors
@ -1030,14 +1035,16 @@ You should test the app's defenses against HTTP requests that fail.
Call `request.flush()` with an error message, as seen in the following example.
<code-example
<code-example
path="http/src/testing/http-client.spec.ts"
region="404">
region="404"
linenums="false">
</code-example>
Alternatively, you can call `request.error()` with an `ErrorEvent`.
<code-example
path="http/src/testing/http-client.spec.ts"
region="network-error">
region="network-error"
linenums="false">
</code-example>

View File

@ -10,8 +10,8 @@ an AOT-compiled app, translated into French.
{@a angular-i18n}
## Angular and i18n
*Internationalization* is the process of designing and preparing your app to be usable in different languages.
*Localization* is the process of translating your internationalized app into specific languages for particular locales.
*Internationalization* is the process of designing and preparing your app to be usable in different languages.
*Localization* is the process of translating your internationalized app into specific languages for particular locales.
Angular simplifies the following aspects of internationalization:
* Displaying dates, number, percentages, and currencies in a local format.
@ -19,7 +19,7 @@ Angular simplifies the following aspects of internationalization:
* Handling plural forms of words.
* Handling alternative text.
For localization, you can use the [Angular CLI](cli) to generate most of the boilerplate necessary to create files for translators, and to publish your app in multiple languages.
For localization, you can use the [Angular CLI](cli) to generate most of the boilerplate necessary to create files for translators, and to publish your app in multiple languages.
After you have set up your app to use i18n, the CLI can help you with the following steps:
* Extracting localizable text into a file that you can send out to be translated.
* Building and serving the app for a given locale, using the translated text.
@ -85,7 +85,8 @@ The CLI imports the locale data for you when you use the parameter `--configurat
If you want to import locale data for other languages, you can do it manually:
<code-example path="i18n/doc-files/app.locale_data.ts" region="import-locale" header="src/app/app.module.ts"></code-example>
<code-example path="i18n/doc-files/app.locale_data.ts" region="import-locale" header="src/app/app.module.ts" linenums="false">
</code-example>
The first parameter is an object containing the locale data imported from `@angular/common/locales`.
By default, the imported locale data is registered with the locale id that is defined in the Angular
@ -99,7 +100,8 @@ The files in `@angular/common/locales` contain most of the locale data that you
need, but some advanced formatting options might only be available in the extra dataset that you can
import from `@angular/common/locales/extra`. An error message informs you when this is the case.
<code-example path="i18n/doc-files/app.locale_data_extra.ts" region="import-locale-extra" header="src/app/app.module.ts"></code-example>
<code-example path="i18n/doc-files/app.locale_data_extra.ts" region="import-locale-extra" header="src/app/app.module.ts" linenums="false">
</code-example>
<div class="alert is-helpful">
@ -130,9 +132,9 @@ The i18n template translation process has four phases:
* `--i18nFile`=*path to the translation file*
* `--i18nFormat`=*format of the translation file*
* `--i18nLocale`= *locale id*
* `--i18nLocale`= *locale id*
The command replaces the original messages with translated text, and generates a new version of the app in the target language.
The command replaces the original messages with translated text, and generates a new version of the app in the target language.
You need to build and deploy a separate version of the app for each supported language.
@ -144,11 +146,13 @@ text is to be translated.
In the example below, an `<h1>` tag displays a simple English language greeting, "Hello i18n!"
<code-example path="i18n/doc-files/app.component.html" region="greeting" header="src/app/app.component.html"></code-example>
<code-example path="i18n/doc-files/app.component.html" region="greeting" header="src/app/app.component.html" linenums="false">
</code-example>
To mark the greeting for translation, add the `i18n` attribute to the `<h1>` tag.
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute" header="src/app/app.component.html"></code-example>
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute" header="src/app/app.component.html" linenums="false">
</code-example>
<div class="alert is-helpful">
@ -167,7 +171,8 @@ To translate a text message accurately, the translator may need additional infor
You can add a description of the text message as the value of the `i18n` attribute, as shown in the
example below:
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-desc" header="src/app/app.component.html"></code-example>
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-desc" header="src/app/app.component.html" linenums="false">
</code-example>
The translator may also need to know the meaning or intent of the text message within this particular
app context.
@ -175,7 +180,8 @@ app context.
You add context by beginning the `i18n` attribute value with the _meaning_ and
separating it from the _description_ with the `|` character: `<meaning>|<description>`
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-meaning" header="src/app/app.component.html"></code-example>
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-meaning" header="src/app/app.component.html" linenums="false">
</code-example>
All occurrences of a text message that have the same meaning will have the same translation.
A text message that is associated with different meanings can have different translations.
@ -193,7 +199,8 @@ text messages with different descriptions (not different meanings), then they ar
The angular i18n extractor tool generates a file with a translation unit entry for each `i18n`
attribute in a template. By default, it assigns each translation unit a unique id such as this one:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="generated-id"></code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="generated-id" linenums="false">
</code-example>
When you change the translatable text, the extractor tool generates a new id for that translation unit.
You must then update the translation file with the new id.
@ -201,12 +208,14 @@ You must then update the translation file with the new id.
Alternatively, you can specify a custom id in the `i18n` attribute by using the prefix `@@`.
The example below defines the custom id `introductionHeader`:
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-solo-id' header='app/app.component.html'></code-example>
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-solo-id' header='app/app.component.html' linenums="false">
</code-example>
When you specify a custom id, the extractor tool and compiler generate a translation unit with that
custom id.
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="custom-id"></code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="custom-id" linenums="false">
</code-example>
The custom id is persistent. The extractor tool does not change it when the translatable text changes.
Therefore, you do not need to update the translation. This approach makes maintenance easier.
@ -217,11 +226,13 @@ You can use a custom id in combination with a description by including both in t
`i18n` attribute. In the example below, the `i18n` attribute value includes a description, followed
by the custom `id`:
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-id' header='app/app.component.html'></code-example>
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-id' header='app/app.component.html' linenums="false">
</code-example>
You also can add a meaning, as shown in this example:
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-meaning-and-id' header='app/app.component.html'></code-example>
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-meaning-and-id' header='app/app.component.html' linenums="false">
</code-example>
#### Define unique custom ids
@ -264,7 +275,8 @@ However, if you don't want to create a new DOM element merely to facilitate tran
you can wrap the text in an `<ng-container>` element.
The `<ng-container>` is transformed into an html comment:
<code-example path="i18n/src/app/app.component.html" region="i18n-ng-container" header="src/app/app.component.html"></code-example>
<code-example path="i18n/src/app/app.component.html" region="i18n-ng-container" header="src/app/app.component.html" linenums="false">
</code-example>
{@a translate-attributes}
### Translate attributes
@ -272,13 +284,15 @@ The `<ng-container>` is transformed into an html comment:
Displayed text is sometimes supplied as the value of an attribute, rather than the content of tag.
For example, if your template has an image with a `title` attribute, the text value of the `title` attribute needs to be translated.
<code-example path="i18n/doc-files/app.component.html" region="i18n-title" header="src/app/app.component.html"></code-example>
<code-example path="i18n/doc-files/app.component.html" region="i18n-title" header="src/app/app.component.html" linenums="false">
</code-example>
To mark an attribute for translation, add an attribute in the form of `i18n-x`,
where `x` is the name of the attribute to translate. The following example shows how to mark the
`title` attribute for translation by adding the `i18n-title` attribute on the `img` tag:
<code-example path="i18n/src/app/app.component.html" region="i18n-title-translate" header="src/app/app.component.html"></code-example>
<code-example path="i18n/src/app/app.component.html" region="i18n-title-translate" header="src/app/app.component.html" linenums="false">
</code-example>
This technique works for any attribute of any element.
@ -287,8 +301,8 @@ syntax.
## Regular expressions for plurals and selections
Different languages have different pluralization rules and grammatical constructions that add
complexity to the translation task.
Different languages have different pluralization rules and grammatical constructions that add
complexity to the translation task.
You can use regular expressions with the `plural` and `select` clauses to provide patterns that aid translation in these cases.
{@a plural-ICU}
@ -302,7 +316,8 @@ Other languages might express the cardinality differently.
The example below shows how to use a `plural` ICU expression to display one of those three options
based on when the update occurred:
<code-example path="i18n/src/app/app.component.html" region="i18n-plural" header="src/app/app.component.html"></code-example>
<code-example path="i18n/src/app/app.component.html" region="i18n-plural" header="src/app/app.component.html" linenums="false">
</code-example>
* The first parameter is the key. It is bound to the component property (`minutes`), which determines
the number of minutes.
@ -357,7 +372,8 @@ The following format message in the component template binds to the component's
which outputs one of the following string values: "male", "female" or "other".
The message maps those values to the appropriate translations:
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html"></code-example>
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html" linenums="false">
</code-example>
{@a nesting-ICUS}
### Nesting plural and select ICU expressions
@ -379,7 +395,7 @@ Open a terminal window at the root of the app project and run the CLI command `x
ng xi18n
</code-example>
By default, the command creates a file named `messages.xlf` in your `src/` folder.
By default, the command creates a file named `messages.xlf` in your `src/` folder.
<div class="alert is-helpful">
@ -395,7 +411,7 @@ For more information, see the [Angular Ahead-of-Time Webpack Plugin documentatio
{@a other-formats}
### Output options
You can supply command options to change the format, the name, the location, and the source locale of the extracted file.
You can supply command options to change the format, the name, the location, and the source locale of the extracted file.
For example, to create a file in the `src/locale` folder, specify the output path:
<code-example language="sh" class="code-shell">
@ -456,7 +472,7 @@ file. This information is not used by Angular, but external translation tools ma
The `ng xi18n` command generates a translation source file named `messages.xlf` in the project `src`
folder.
The next step is to translate the display strings in this source file into language-specific
The next step is to translate the display strings in this source file into language-specific
translation files. The example in this guide creates a French translation file.
{@a localization-folder}
@ -502,7 +518,8 @@ This sample file is easy to translate without a special editor or knowledge of F
1. Open `messages.fr.xlf` and find the first `<trans-unit>` section:
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello-before" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello-before" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
> This XML element represents the translation of the `<h1>` greeting tag that you marked with the
`i18n` attribute earlier in this guide.
@ -517,11 +534,13 @@ This sample file is easy to translate without a special editor or knowledge of F
and context provided by the source, description, and meaning elements to guide your selection of
the appropriate French translation.
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;, after translation)"></code-example>
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;, after translation)" linenums="false">
</code-example>
3. Translate the other text nodes the same way:
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-other-nodes" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-other-nodes" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
<div class="alert is-important">
@ -547,7 +566,8 @@ must be just below the translation unit for the logo.
To translate a `plural`, translate its ICU format match values:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-plural" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-plural" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
You can add or remove plural cases, with each language having its own cardinality. (See
[CLDR plural rules](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html).)
@ -557,7 +577,8 @@ You can add or remove plural cases, with each language having its own cardinalit
Below is the content of our example `select` ICU expression in the component template:
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html"></code-example>
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html" linenums="false">
</code-example>
The extraction tool broke that into two translation units because ICU expressions are extracted
separately.
@ -567,16 +588,19 @@ In place of the `select` is a placeholder, `<x id="ICU">`, that represents the `
Translate the text and move around the placeholder if necessary, but don't remove it. If you remove
the placeholder, the ICU expression will not be present in your translated app.
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-1" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-1" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
The second translation unit, immediately below the first one, contains the `select` message.
Translate that as well.
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-2" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-2" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
Here they are together, after translation:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-select" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-select" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
{@a translate-nested}
### Translate a nested expression
@ -584,15 +608,18 @@ Here they are together, after translation:
A nested expression is similar to the previous examples. As in the previous example, there are
two translation units. The first one contains the text outside of the nested expression:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-1" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-1" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
The second unit contains the complete nested expression:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-2" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-2" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
And both together:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)"></code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested" header="src/locale/messages.fr.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
The entire template translation is complete. The next section describes how to load that translation
into the app.
@ -756,7 +783,8 @@ behavior of the compiler. You can use it to specify the translation providers:
Then provide the `LOCALE_ID` in the main module:
<code-example path="i18n/doc-files/app.module.ts" header="src/app/app.module.ts"></code-example>
<code-example path="i18n/doc-files/app.module.ts" header="src/app/app.module.ts" linenums="false">
</code-example>
{@a missing-translation}
@ -809,4 +837,4 @@ For example, if the French version of your application is served from https://my
}
```
For more details about how to create scripts to generate an app in multiple languages and how to set up Apache 2 and NGINX to serve them from different subdirectories, read [this tutorial by Philippe Martin](https://dev.to/angular/deploying-an-i18n-angular-app-with-angular-cli-2fb9).
For more details about how to create scripts to generate an app in multiple languages and how to set up Apache 2 to serve them from different subdirectories, read [this tutorial by Philippe Martin](https://medium.com/@feloy/deploying-an-i18n-angular-app-with-angular-cli-fc788f17e358#.1xq4iy6fp).

View File

@ -1,13 +1,23 @@
# Lazy Loading Feature Modules
#### Prerequisites
A basic understanding of the following:
* [Feature Modules](guide/feature-modules).
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
* [Frequently Used Modules](guide/frequent-ngmodules).
* [Types of Feature Modules](guide/module-types).
* [Routing and Navigation](guide/router).
For the final sample app with two lazy loaded modules that this page describes, see the
<live-example></live-example>.
<hr>
## High level view
By default, NgModules are eagerly loaded, which means that as soon as the app loads, so do all the NgModules, whether or not they are immediately necessary. For large apps with lots of routes, consider lazy loading&mdash;a design pattern that loads NgModules as needed. Lazy loading helps keep initial
bundle sizes smaller, which in turn helps decrease load times.
For the final sample app with two lazy loaded modules that this page describes, see the
<live-example></live-example>.
There are three main steps to setting up a lazy loaded feature module:
1. Create the feature module.
@ -88,7 +98,9 @@ placeholder markup in `app.component.html` with a custom nav
so you can easily navigate to your modules in the browser:
<code-example path="lazy-loading-ngmodules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html"></code-example>
<code-example path="lazy-loading-ngmodules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html" linenums="false">
</code-example>
@ -126,7 +138,9 @@ Each feature module acts as a doorway via the router. In the `AppRoutingModule`,
In `AppRoutingModule`, update the `routes` array with the following:
<code-example path="lazy-loading-ngmodules/src/app/app-routing.module.ts" region="const-routes" header="src/app/app-routing.module.ts"></code-example>
<code-example path="lazy-loading-ngmodules/src/app/app-routing.module.ts" region="const-routes" header="src/app/app-routing.module.ts" linenums="false">
</code-example>
The import statements stay the same. The first two paths are the routes to the `CustomersModule` and the `OrdersModule` respectively. Notice that the lazy loading syntax uses `loadChildren` followed by a function that uses the browser's built-in `import('...')` syntax for dynamic imports. The import path is the relative path to the module.
@ -136,7 +150,9 @@ The import statements stay the same. The first two paths are the routes to the `
Next, take a look at `customers.module.ts`. If youre using the CLI and following the steps outlined in this page, you dont have to do anything here. The feature module is like a connector between the `AppRoutingModule` and the feature routing module. The `AppRoutingModule` imports the feature module, `CustomersModule`, and `CustomersModule` in turn imports the `CustomersRoutingModule`.
<code-example path="lazy-loading-ngmodules/src/app/customers/customers.module.ts" region="customers-module" header="src/app/customers/customers.module.ts"></code-example>
<code-example path="lazy-loading-ngmodules/src/app/customers/customers.module.ts" region="customers-module" header="src/app/customers/customers.module.ts" linenums="false">
</code-example>
@ -147,14 +163,18 @@ The `customers.module.ts` file imports the `CustomersRoutingModule` and `Custome
The next step is in `customers-routing.module.ts`. First, import the component at the top of the file with the other JavaScript import statements. Then, add the route to `CustomerListComponent`.
<code-example path="lazy-loading-ngmodules/src/app/customers/customers-routing.module.ts" region="customers-routing-module" header="src/app/customers/customers-routing.module.ts"></code-example>
<code-example path="lazy-loading-ngmodules/src/app/customers/customers-routing.module.ts" region="customers-routing-module" header="src/app/customers/customers-routing.module.ts" linenums="false">
</code-example>
Notice that the `path` is set to an empty string. This is because the path in `AppRoutingModule` is already set to `customers`, so this route in the `CustomersRoutingModule`, is already within the `customers` context. Every route in this routing module is a child route.
Repeat this last step of importing the `OrdersListComponent` and configuring the Routes array for the `orders-routing.module.ts`:
<code-example path="lazy-loading-ngmodules/src/app/orders/orders-routing.module.ts" region="orders-routing-module-detail" header="src/app/orders/orders-routing.module.ts (excerpt)"></code-example>
<code-example path="lazy-loading-ngmodules/src/app/orders/orders-routing.module.ts" region="orders-routing-module-detail" header="src/app/orders/orders-routing.module.ts (excerpt)" linenums="false">
</code-example>
Now, if you view the app in the browser, the three buttons take you to each module.
@ -207,6 +227,3 @@ You may also be interested in the following:
* [Routing and Navigation](guide/router).
* [Providers](guide/providers).
* [Types of Feature Modules](guide/module-types).
* [Route-level code-splitting in Angular](https://web.dev/route-level-code-splitting-in-angular/)
* [Route preloading strategies in Angular](https://web.dev/route-preloading-in-angular/)

View File

@ -23,7 +23,7 @@ Each interface has a single hook method whose name is the interface name prefixe
For example, the `OnInit` interface has a hook method named `ngOnInit()`
that Angular calls shortly after creating the component:
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" header="peek-a-boo.component.ts (excerpt)"></code-example>
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" header="peek-a-boo.component.ts (excerpt)" linenums="false"></code-example>
No directive or component will implement all of the lifecycle hooks.
Angular only calls a directive/component hook method *if it is defined*.
@ -339,13 +339,13 @@ The heroes will never know they're being watched.
The sneaky spy directive is simple, consisting almost entirely of `ngOnInit()` and `ngOnDestroy()` hooks
that log messages to the parent via an injected `LoggerService`.
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" header="src/app/spy.directive.ts"></code-example>
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" header="src/app/spy.directive.ts" linenums="false"></code-example>
You can apply the spy to any native or component element and it'll be initialized and destroyed
at the same time as that element.
Here it is attached to the repeated hero `<div>`:
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" header="src/app/spy.component.html"></code-example>
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" header="src/app/spy.component.html" linenums="false"></code-example>
Each spy's birth and death marks the birth and death of the attached hero `<div>`
with an entry in the *Hook Log* as seen here:
@ -425,7 +425,7 @@ You risk memory leaks if you neglect to do so.
Angular calls its `ngOnChanges()` method whenever it detects changes to ***input properties*** of the component (or directive).
This example monitors the `OnChanges` hook.
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" header="on-changes.component.ts (excerpt)"></code-example>
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" header="on-changes.component.ts (excerpt)" linenums="false"></code-example>
The `ngOnChanges()` method takes an object that maps each changed property name to a
[SimpleChange](api/core/SimpleChange) object holding the current and previous property values.
@ -433,7 +433,7 @@ This hook iterates over the changed properties and logs them.
The example component, `OnChangesComponent`, has two input properties: `hero` and `power`.
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" header="src/app/on-changes.component.ts"></code-example>
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" header="src/app/on-changes.component.ts" linenums="false"></code-example>
The host `OnChangesParentComponent` binds to them like this:
@ -468,7 +468,7 @@ Use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch
The *DoCheck* sample extends the *OnChanges* sample with the following `ngDoCheck()` hook:
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" header="DoCheckComponent (ngDoCheck)"></code-example>
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" header="DoCheckComponent (ngDoCheck)" linenums="false"></code-example>
This code inspects certain _values of interest_, capturing and comparing their current state against previous values.
It writes a special message to the log when there are no substantive changes to the `hero` or the `power`
@ -497,25 +497,25 @@ The *AfterView* sample explores the `AfterViewInit()` and `AfterViewChecked()` h
Here's a child view that displays a hero's name in an `<input>`:
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" header="ChildComponent"></code-example>
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" header="ChildComponent" linenums="false"></code-example>
The `AfterViewComponent` displays this child view *within its template*:
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" header="AfterViewComponent (template)"></code-example>
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" header="AfterViewComponent (template)" linenums="false"></code-example>
The following hooks take action based on changing values *within the child view*,
which can only be reached by querying for the child view via the property decorated with
[@ViewChild](api/core/ViewChild).
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" header="AfterViewComponent (class excerpts)"></code-example>
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" header="AfterViewComponent (class excerpts)" linenums="false"></code-example>
{@a wait-a-tick}
### Abide by the unidirectional data flow rule
The `doSomething()` method updates the screen when the hero name exceeds 10 characters.
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" header="AfterViewComponent (doSomething)"></code-example>
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" header="AfterViewComponent (doSomething)" linenums="false"></code-example>
Why does the `doSomething()` method wait a tick before updating `comment`?
@ -559,7 +559,7 @@ Consider this variation on the [previous _AfterView_](guide/lifecycle-hooks#afte
This time, instead of including the child view within the template, it imports the content from
the `AfterContentComponent`'s parent. Here's the parent's template:
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" header="AfterContentParentComponent (template excerpt)"></code-example>
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" header="AfterContentParentComponent (template excerpt)" linenums="false"></code-example>
Notice that the `<app-child>` tag is tucked between the `<after-content>` tags.
Never put content between a component's element tags *unless you intend to project that content
@ -567,7 +567,7 @@ into the component*.
Now look at the component's template:
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" header="AfterContentComponent (template)"></code-example>
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" header="AfterContentComponent (template)" linenums="false"></code-example>
The `<ng-content>` tag is a *placeholder* for the external content.
It tells Angular where to insert that content.
@ -603,7 +603,7 @@ The following *AfterContent* hooks take action based on changing values in a *co
which can only be reached by querying for them via the property decorated with
[@ContentChild](api/core/ContentChild).
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" header="AfterContentComponent (class excerpts)"></code-example>
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" header="AfterContentComponent (class excerpts)" linenums="false"></code-example>
{@a no-unidirectional-flow-worries}

View File

@ -1,5 +1,16 @@
# Types of Feature Modules
#### Prerequisites
A basic understanding of the following concepts:
* [Feature Modules](guide/feature-modules).
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
* [Frequently Used Modules](guide/frequent-ngmodules).
<hr>
There are five general categories of feature modules which
tend to fall into the following groups:

View File

@ -1,5 +1,15 @@
# NgModule API
#### Prerequisites
A basic understanding of the following concepts:
* [Bootstrapping](guide/bootstrapping).
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
<hr />
## Purpose of `@NgModule`
At a high level, NgModules are a way to organize Angular apps
and they accomplish this through the metadata in the `@NgModule`
decorator.

View File

@ -1,5 +1,13 @@
# NgModule FAQs
#### Prerequisites:
A basic understanding of the following concepts:
* [NgModules](guide/ngmodules).
<hr />
NgModules help organize an application into cohesive blocks of functionality.
This page answers the questions many developers ask about NgModule design and implementation.
@ -470,7 +478,8 @@ You can throw an error or take other remedial action.
Certain NgModules, such as `BrowserModule`, implement such a guard.
Here is a custom constructor for an NgModule called `GreetingModule`.
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts (Constructor)"></code-example>
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts (Constructor)" linenums="false">
</code-example>
<hr/>

View File

@ -1,9 +1,13 @@
# JavaScript Modules vs. NgModules
#### Prerequisites
A basic understanding of [JavaScript/ECMAScript modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/).
<hr>
JavaScript and Angular use modules to organize code, and
though they organize it differently, Angular apps rely on both.
## JavaScript modules
In JavaScript, modules are individual files with JavaScript code in them. To make whats in them available, you write an export statement, usually after the relevant code, like this:
@ -20,8 +24,6 @@ import { AppComponent } from './app.component';
JavaScript modules help you namespace, preventing accidental global variables.
For more information on JavaScript modules, see [JavaScript/ECMAScript modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/).
## NgModules
<!-- KW-- perMisko: let's discuss. This does not answer the question why it is different. Also, last sentence is confusing.-->

View File

@ -1,5 +1,13 @@
# NgModules
#### Prerequisites
A basic understanding of the following concepts:
* [Bootstrapping](guide/bootstrapping).
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
<hr>
**NgModules** configure the injector and the compiler and help organize related things together.
An NgModule is a class marked by the `@NgModule` decorator.
@ -12,6 +20,7 @@ For an example app showcasing all the techniques that NgModules related pages
cover, see the <live-example></live-example>. For explanations on the individual techniques, visit the relevant NgModule pages under the NgModules
section.
## Angular modularity
Modules are a great way to organize an application and extend it with capabilities from external libraries.
@ -48,14 +57,12 @@ You then import these modules into the root module.
## The basic NgModule
The [Angular CLI](cli) generates the following basic `AppModule` when creating a new app.
The [Angular CLI](cli) generates the following basic app module when creating a new app.
<code-example path="ngmodules/src/app/app.module.1.ts" header="src/app/app.module.ts (default AppModule)">
// @NgModule decorator with its metadata
<code-example path="bootstrapping/src/app/app.module.ts" region="whole-ngmodule" header="src/app/app.module.ts" linenums="false">
</code-example>
At the top are the import statements. The next section is where you configure the `@NgModule` by stating what components and directives belong to it (`declarations`) as well as which other modules it uses (`imports`). For more information on the structure of an `@NgModule`, be sure to read [Bootstrapping](guide/bootstrapping).
At the top are the import statements. The next section is where you configure the `@NgModule` by stating what components and directives belong to it (`declarations`) as well as which other modules it uses (`imports`). This page builds on [Bootstrapping](guide/bootstrapping), which covers the structure of an NgModule in detail. If you need more information on the structure of an `@NgModule`, be sure to read [Bootstrapping](guide/bootstrapping).
<hr />

View File

@ -2,17 +2,15 @@
Angular makes use of observables as an interface to handle a variety of common asynchronous operations. For example:
* You can define [custom events](guide/template-syntax#custom-events-with-eventemitter) that send observable output data from a child to a parent component.
* The `EventEmitter` class extends `Observable`.
* The HTTP module uses observables to handle AJAX requests and responses.
* The Router and Forms modules use observables to listen for and respond to user-input events.
## Transmitting data between components
## Event emitter
Angular provides an `EventEmitter` class that is used when publishing values from a component through the [`@Output()` decorator](guide/template-syntax#how-to-use-output).
`EventEmitter` extends [RxJS `Subject`](https://rxjs.dev/api/index/class/Subject), adding an `emit()` method so it can send arbitrary values.
When you call `emit()`, it passes the emitted value to the `next()` method of any subscribed observer.
Angular provides an `EventEmitter` class that is used when publishing values from a component through the `@Output()` decorator. `EventEmitter` extends `Observable`, adding an `emit()` method so it can send arbitrary values. When you call `emit()`, it passes the emitted value to the `next()` method of any subscribed observer.
A good example of usage can be found in the [EventEmitter](https://angular.io/api/core/EventEmitter) documentation. Here is the example component that listens for open and close events:
A good example of usage can be found on the [EventEmitter](https://angular.io/api/core/EventEmitter) documentation. Here is the example component that listens for open and close events:
`<zippy (open)="onOpen($event)" (close)="onClose($event)"></zippy>`

View File

@ -26,14 +26,18 @@ In this page, you'll use pipes to transform a component's birthday property into
a human-friendly date.
<code-example path="pipes/src/app/hero-birthday1.component.ts" header="src/app/hero-birthday1.component.ts"></code-example>
<code-example path="pipes/src/app/hero-birthday1.component.ts" header="src/app/hero-birthday1.component.ts" linenums="false">
</code-example>
Focus on the component's template.
<code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" header="src/app/app.component.html"></code-example>
<code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" header="src/app/app.component.html" linenums="false">
</code-example>
@ -75,7 +79,9 @@ Modify the birthday template to give the date pipe a format parameter.
After formatting the hero's April 15th birthday, it renders as **<samp>04/15/88</samp>**:
<code-example path="pipes/src/app/app.component.html" region="format-birthday" header="src/app/app.component.html"></code-example>
<code-example path="pipes/src/app/app.component.html" region="format-birthday" header="src/app/app.component.html" linenums="false">
</code-example>
@ -89,7 +95,9 @@ Write a second component that *binds* the pipe's format parameter
to the component's `format` property. Here's the template for that component:
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" header="src/app/hero-birthday2.component.ts (template)"></code-example>
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" header="src/app/hero-birthday2.component.ts (template)" linenums="false">
</code-example>
@ -98,7 +106,9 @@ That method toggles the component's `format` property between a short form
(`'shortDate'`) and a longer form (`'fullDate'`).
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" header="src/app/hero-birthday2.component.ts (class)"></code-example>
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" header="src/app/hero-birthday2.component.ts (class)" linenums="false">
</code-example>
@ -133,7 +143,9 @@ the birthday is chained to the `DatePipe` and on to the `UpperCasePipe`.
The birthday displays as **<samp>APR 15, 1988</samp>**.
<code-example path="pipes/src/app/app.component.html" region="chained-birthday" header="src/app/app.component.html"></code-example>
<code-example path="pipes/src/app/app.component.html" region="chained-birthday" header="src/app/app.component.html" linenums="false">
</code-example>
@ -141,7 +153,9 @@ This example&mdash;which displays **<samp>FRIDAY, APRIL 15, 1988</samp>**&mdash;
the same pipes as above, but passes in a parameter to `date` as well.
<code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" header="src/app/app.component.html"></code-example>
<code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" header="src/app/app.component.html" linenums="false">
</code-example>
@ -152,7 +166,9 @@ You can write your own custom pipes.
Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers:
<code-example path="pipes/src/app/exponential-strength.pipe.ts" header="src/app/exponential-strength.pipe.ts"></code-example>
<code-example path="pipes/src/app/exponential-strength.pipe.ts" header="src/app/exponential-strength.pipe.ts" linenums="false">
</code-example>
@ -184,7 +200,8 @@ Technically, it's optional; Angular looks for and executes the `transform` metho
Now you need a component to demonstrate the pipe.
<code-example path="pipes/src/app/power-booster.component.ts" header="src/app/power-booster.component.ts"></code-example>
<code-example path="pipes/src/app/power-booster.component.ts" header="src/app/power-booster.component.ts" linenums="false">
</code-example>
<figure>
<img src='generated/images/guide/pipes/power-booster.png' alt="Power Booster">
@ -254,13 +271,17 @@ In the next example, the component uses the default, aggressive change detection
its display of every hero in the `heroes` array. Here's the template:
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" header="src/app/flying-heroes.component.html (v1)"></code-example>
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" header="src/app/flying-heroes.component.html (v1)" linenums="false">
</code-example>
The companion component class provides heroes, adds heroes into the array, and can reset the array.
<code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" header="src/app/flying-heroes.component.ts (v1)"></code-example>
<code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" header="src/app/flying-heroes.component.ts (v1)" linenums="false">
</code-example>
@ -272,13 +293,17 @@ If you added the ability to remove or change a hero, Angular would detect those
Add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly.
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" header="src/app/flying-heroes.component.html (flyers)"></code-example>
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" header="src/app/flying-heroes.component.html (flyers)" linenums="false">
</code-example>
Here's the `FlyingHeroesPipe` implementation, which follows the pattern for custom pipes described earlier.
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" header="src/app/flying-heroes.pipe.ts"></code-example>
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" header="src/app/flying-heroes.pipe.ts" linenums="false">
</code-example>
@ -290,7 +315,9 @@ It's just using a different change-detection algorithm that ignores changes to t
Notice how a hero is added:
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" header="src/app/flying-heroes.component.ts"></code-example>
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" header="src/app/flying-heroes.component.ts" linenums="false">
</code-example>
@ -338,7 +365,9 @@ You make a pipe impure by setting its pure flag to false. You could make the `Fl
impure like this:
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" header="src/app/flying-heroes.pipe.ts"></code-example>
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" header="src/app/flying-heroes.pipe.ts" linenums="false">
</code-example>
@ -412,14 +441,18 @@ The only difference is the `pure` flag in the pipe metadata.
This is a good candidate for an impure pipe because the `transform` function is trivial and fast.
<code-example path="pipes/src/app/flying-heroes.pipe.ts" header="src/app/flying-heroes.pipe.ts (filter)" region="filter"></code-example>
<code-example path="pipes/src/app/flying-heroes.pipe.ts" linenums="false" header="src/app/flying-heroes.pipe.ts (filter)" region="filter">
</code-example>
You can derive a `FlyingHeroesImpureComponent` from `FlyingHeroesComponent`.
<code-example path="pipes/src/app/flying-heroes-impure.component.html" header="src/app/flying-heroes-impure.component.html (excerpt)" region="template-flying-heroes"></code-example>
<code-example path="pipes/src/app/flying-heroes-impure.component.html" linenums="false" header="src/app/flying-heroes-impure.component.html (excerpt)" region="template-flying-heroes">
</code-example>

View File

@ -1,10 +1,16 @@
# Providers
A provider is an instruction to the DI system on how to obtain a value for a dependency. Most of the time, these dependencies are services that you create and provide.
#### Prerequisites:
* A basic understanding of [Bootstrapping](guide/bootstrapping).
* Familiarity with [Frequently Used Modules](guide/frequent-ngmodules).
For the final sample app using the provider that this page describes,
see the <live-example></live-example>.
<hr>
A provider is an instruction to the DI system on how to obtain a value for a dependency. Most of the time, these dependencies are services that you create and provide.
## Providing a service
If you already have an app that was created with the [Angular CLI](cli), you can create a service using the [`ng generate`](cli/generate) CLI command in the root project directory. Replace _User_ with the name of your service.
@ -15,7 +21,7 @@ ng generate service User
This command creates the following `UserService` skeleton:
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts"></code-example>
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts" linenums="false"> </code-example>
You can now inject `UserService` anywhere in your application.
@ -32,11 +38,11 @@ You should always provide your service in the root injector unless there is a ca
It's also possible to specify that a service should be provided in a particular `@NgModule`. For example, if you don't want `UserService` to be available to applications unless they import a `UserModule` you've created, you can specify that the service should be provided in the module:
<code-example path="providers/src/app/user.service.1.ts" header="src/app/user.service.ts"></code-example>
<code-example path="providers/src/app/user.service.1.ts" header="src/app/user.service.ts" linenums="false"> </code-example>
The example above shows the preferred way to provide a service in a module. This method is preferred because it enables tree-shaking of the service if nothing injects it. If it's not possible to specify in the service which module should provide it, you can also declare a provider for the service within the module:
<code-example path="providers/src/app/user.module.ts" header="src/app/user.module.ts"></code-example>
<code-example path="providers/src/app/user.module.ts" header="src/app/user.module.ts" linenums="false"> </code-example>
## Limiting provider scope by lazy loading modules
@ -61,7 +67,8 @@ method is helpful for when you want to eagerly load a module that needs a servic
Providing a service in the component limits the service only to that component (other components in
the same module cant access it.)
<code-example path="providers/src/app/app.component.ts" region="component-providers" header="src/app/app.component.ts"></code-example>
<code-example path="providers/src/app/app.component.ts" region="component-providers" header="src/app/app.component.ts" linenums="false">
</code-example>
## Providing services in modules vs. components

View File

@ -12,7 +12,7 @@ Try the <live-example title="Reactive Forms in Stackblitz">Reactive Forms live-e
## Introduction to reactive forms
Reactive forms use an explicit and immutable approach to managing the state of a form at a given point in time. Each change to the form state returns a new state, which maintains the integrity of the model between changes. Reactive forms are built around observable streams, where form inputs and values are provided as streams of input values, which can be accessed synchronously.
Reactive forms use an explicit and immutable approach to managing the state of a form at a given point in time. Each change to the form state returns a new state, which maintains the integrity of the model between changes. Reactive forms are built around observable streams, where form inputs and values are provided as streams of input values, which can be accessed synchronously.
Reactive forms also provide a straightforward path to testing because you are assured that your data is consistent and predictable when requested. Any consumers of the streams have access to manipulate that data safely.
@ -26,9 +26,11 @@ This section describes how to add a single form control. In the example, the use
To use reactive forms, import `ReactiveFormsModule` from the `@angular/forms` package and add it to your NgModule's `imports` array.
<code-example path="reactive-forms/src/app/app.module.ts" region="imports" header="src/app/app.module.ts (excerpt)"></code-example>
<code-example path="reactive-forms/src/app/app.module.ts" region="imports" header="src/app/app.module.ts (excerpt)">
### Step 2: Generating and importing a new form control
</code-example>
### Step 2: Generating and importing a new form control
Generate a component for the control.
@ -40,15 +42,19 @@ Generate a component for the control.
The `FormControl` class is the basic building block when using reactive forms. To register a single form control, import the `FormControl` class into your component and create a new instance of the form control to save as a class property.
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.ts" region="create-control" header="src/app/name-editor/name-editor.component.ts"></code-example>
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.ts" region="create-control" header="src/app/name-editor/name-editor.component.ts">
Use the constructor of `FormControl` to set its initial value, which in this case is an empty string. By creating these controls in your component class, you get immediate access to listen for, update, and validate the state of the form input.
</code-example>
Use the constructor of `FormControl` to set its initial value, which in this case is an empty string. By creating these controls in your component class, you get immediate access to listen for, update, and validate the state of the form input.
### Step 3: Registering the control in the template
After you create the control in the component class, you must associate it with a form control element in the template. Update the template with the form control using the `formControl` binding provided by `FormControlDirective` included in `ReactiveFormsModule`.
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="control-binding" header="src/app/name-editor/name-editor.component.html"></code-example>
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="control-binding" linenums="false" header="src/app/name-editor/name-editor.component.html">
</code-example>
<div class="alert is-helpful">
@ -60,9 +66,11 @@ Using the template binding syntax, the form control is now registered to the `na
#### Displaying the component
The form control assigned to `name` is displayed when the component is added to a template.
The form control assigned to `name` is displayed when the component is added to a template.
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-name-editor" header="src/app/app.component.html (name editor)"></code-example>
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-name-editor" linenums="false" header="src/app/app.component.html (name editor)">
</code-example>
<figure>
<img src="generated/images/guide/reactive-forms/name-editor-1.png" alt="Name Editor">
@ -70,21 +78,23 @@ The form control assigned to `name` is displayed when the component is added to
## Managing control values
Reactive forms give you access to the form control state and value at a point in time. You can manipulate
Reactive forms give you access to the form control state and value at a point in time. You can manipulate
the current state and value through the component class or the component template. The following examples display the value of the form control instance and change it.
{@a display-value}
### Displaying a form control value
You can display the value in these ways:
You can display the value in these ways:
* Through the `valueChanges` observable where you can listen for changes in the form's value in the template using `AsyncPipe` or in the component class using the `subscribe()` method.
* With the `value` property. which gives you a snapshot of the current value.
* Through the `valueChanges` observable where you can listen for changes in the form's value in the template using `AsyncPipe` or in the component class using the `subscribe()` method.
* With the `value` property. which gives you a snapshot of the current value.
The following example shows you how to display the current value using interpolation in the template.
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="display-value" header="src/app/name-editor/name-editor.component.html (control value)"></code-example>
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="display-value" linenums="false" header="src/app/name-editor/name-editor.component.html (control value)">
</code-example>
The displayed value changes as you update the form control element.
@ -94,7 +104,7 @@ Read about other `FormControl` properties and methods in the [Reactive forms API
### Replacing a form control value
Reactive forms have methods to change a control's value programmatically, which gives you the flexibility to update the value without user interaction. A form control instance provides a `setValue()` method that updates the value of the form control and validates the structure of the value provided against the control's structure. For example, when retrieving form data from a backend API or service, use the `setValue()` method to update the control to its new value, replacing the old value entirely.
Reactive forms have methods to change a control's value programmatically, which gives you the flexibility to update the value without user interaction. A form control instance provides a `setValue()` method that updates the value of the form control and validates the structure of the value provided against the control's structure. For example, when retrieving form data from a backend API or service, use the `setValue()` method to update the control to its new value, replacing the old value entirely.
The following example adds a method to the component class to update the value of the control to *Nancy* using the `setValue()` method.
@ -102,9 +112,11 @@ The following example adds a method to the component class to update the value o
</code-example>
Update the template with a button to simulate a name update. When you click the **Update Name** button, the value entered in the form control element is reflected as its current value.
Update the template with a button to simulate a name update. When you click the **Update Name** button, the value entered in the form control element is reflected as its current value.
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="update-value" header="src/app/name-editor/name-editor.component.html (update value)"></code-example>
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="update-value" linenums="false" header="src/app/name-editor/name-editor.component.html (update value)">
</code-example>
The form model is the source of truth for the control, so when you click the button, the value of the input is changed within the component class, overriding its current value.
@ -150,17 +162,21 @@ The individual form controls are now collected within a group. A `FormGroup` ins
A form group tracks the status and changes for each of its controls, so if one of the controls changes, the parent control also emits a new status or value change. The model for the group is maintained from its members. After you define the model, you must update the template to reflect the model in the view.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroup" header="src/app/profile-editor/profile-editor.component.html (template form group)"></code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroup" linenums="false" header="src/app/profile-editor/profile-editor.component.html (template form group)">
</code-example>
Note that just as a form group contains a group of controls, the *profile form* `FormGroup` is bound to the `form` element with the `FormGroup` directive, creating a communication layer between the model and the form containing the inputs. The `formControlName` input provided by the `FormControlName` directive binds each individual input to the form control defined in `FormGroup`. The form controls communicate with their respective elements. They also communicate changes to the form group instance, which provides the source of truth for the model value.
### Saving form data
The `ProfileEditor` component accepts input from the user, but in a real scenario you want to capture the form value and make available for further processing outside the component. The `FormGroup` directive listens for the `submit` event emitted by the `form` element and emits an `ngSubmit` event that you can bind to a callback function.
The `ProfileEditor` component accepts input from the user, but in a real scenario you want to capture the form value and make available for further processing outside the component. The `FormGroup` directive listens for the `submit` event emitted by the `form` element and emits an `ngSubmit` event that you can bind to a callback function.
Add an `ngSubmit` event listener to the `form` tag with the `onSubmit()` callback method.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="ng-submit" header="src/app/profile-editor/profile-editor.component.html (submit event)"></code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="ng-submit" linenums="false" header="src/app/profile-editor/profile-editor.component.html (submit event)">
</code-example>
The `onSubmit()` method in the `ProfileEditor` component captures the current value of `profileForm`. Use `EventEmitter` to keep the form encapsulated and to provide the form value outside the component. The following example uses `console.warn` to log a message to the browser console.
@ -168,11 +184,13 @@ The `onSubmit()` method in the `ProfileEditor` component captures the current va
</code-example>
The `submit` event is emitted by the `form` tag using the native DOM event. You trigger the event by clicking a button with `submit` type. This allows the user to press the **Enter** key to submit the completed form.
The `submit` event is emitted by the `form` tag using the native DOM event. You trigger the event by clicking a button with `submit` type. This allows the user to press the **Enter** key to submit the completed form.
Use a `button` element to add a button to the bottom of the form to trigger the form submission.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="submit-button" header="src/app/profile-editor/profile-editor.component.html (submit button)"></code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="submit-button" linenums="false" header="src/app/profile-editor/profile-editor.component.html (submit button)">
</code-example>
<div class="alert is-helpful">
@ -184,7 +202,9 @@ Use a `button` element to add a button to the bottom of the form to trigger the
To display the `ProfileEditor` component that contains the form, add it to a component template.
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-profile-editor" header="src/app/app.component.html (profile editor)"></code-example>
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-profile-editor" linenums="false" header="src/app/app.component.html (profile editor)">
</code-example>
`ProfileEditor` allows you to manage the form control instances for the `firstName` and `lastName` controls within the form group instance.
@ -200,7 +220,9 @@ When building complex forms, managing the different areas of information is easi
An address is a good example of information that can be grouped together. Form groups can accept both form control and form group instances as children. This makes composing complex form models easier to maintain and logically group together. To create a nested group in `profileForm`, add a nested `address` element to the form group instance.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" region="nested-formgroup" header="src/app/profile-editor/profile-editor.component.ts (nested form group)"></code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" region="nested-formgroup" linenums="false" header="src/app/profile-editor/profile-editor.component.ts (nested form group)">
</code-example>
In this example, `address group` combines the current `firstName` and `lastName` controls with the new `street`, `city`, `state`, and `zip` controls. Even though the `address` element in the form group is a child of the overall `profileForm` element in the form group, the same rules apply with value and status changes. Changes in status and value from the nested form group propagate to the parent form group, maintaining consistency with the overall model.
@ -210,7 +232,9 @@ After you update the model in the component class, update the template to connec
Add the `address` form group containing the `street`, `city`, `state`, and `zip` fields to the `ProfileEditor` template.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroupname" header="src/app/profile-editor/profile-editor.component.html (template nested form group)"></code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroupname" linenums="false" header="src/app/profile-editor/profile-editor.component.html (template nested form group)">
</code-example>
The `ProfileEditor` form is displayed as one group, but the model is broken down further to represent the logical grouping areas.
@ -230,11 +254,11 @@ When updating the value for a form group instance that contains multiple control
### Patching the model value
There are two ways to update the model value:
There are two ways to update the model value:
* Use the `setValue()` method to set a new value for an individual control. The `setValue()` method strictly adheres to the structure of the form group and replaces the entire value for the control.
* Use the `setValue()` method to set a new value for an individual control. The `setValue()` method strictly adheres to the structure of the form group and replaces the entire value for the control.
* Use the `patchValue()` method to replace any properties defined in the object that have changed in the form model.
* Use the `patchValue()` method to replace any properties defined in the object that have changed in the form model.
The strict checks of the `setValue()` method help catch nesting errors in complex forms, while `patchValue()` fails silently on those errors.
@ -246,13 +270,15 @@ In `ProfileEditorComponent`, use the `updateProfile` method with the example bel
Simulate an update by adding a button to the template to update the user profile on demand.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="patch-value" header="src/app/profile-editor/profile-editor.component.html (update value)"></code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="patch-value" linenums="false" header="src/app/profile-editor/profile-editor.component.html (update value)">
</code-example>
When a user clicks the button, the `profileForm` model is updated with new values for `firstName` and `street`. Notice that `street` is provided in an object inside the `address` property. This is necessary because the `patchValue()` method applies the update against the model structure. `PatchValue()` only updates properties that the form model defines.
## Generating form controls with FormBuilder
Creating form control instances manually can become repetitive when dealing with multiple forms. The `FormBuilder` service provides convenient methods for generating controls.
Creating form control instances manually can become repetitive when dealing with multiple forms. The `FormBuilder` service provides convenient methods for generating controls.
The following section refactors the `ProfileEditor` component to use the form builder service to create form control and form group instances.
@ -274,7 +300,7 @@ The `FormBuilder` service is an injectable provider that is provided with the re
### Step 3: Generating form controls
The `FormBuilder` service has three methods: `control()`, `group()`, and `array()`. These are factory methods for generating instances in your component classes including form controls, form groups, and form arrays.
The `FormBuilder` service has three methods: `control()`, `group()`, and `array()`. These are factory methods for generating instances in your component classes including form controls, form groups, and form arrays.
Use the `group` method to create the `profileForm` controls.
@ -295,7 +321,7 @@ Compare using the form builder to creating the instances manually.
<code-tabs>
<code-pane path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" region="formgroup-compare" header="src/app/profile-editor/profile-editor.component.ts (instances)">
</code-pane>
<code-pane path="reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" region="formgroup-compare" header="src/app/profile-editor/profile-editor.component.ts (form builder)">
@ -330,7 +356,9 @@ In the `ProfileEditor` component, add the `Validators.required` static method as
HTML5 has a set of built-in attributes that you can use for native validation, including `required`, `minlength`, and `maxlength`. You can take advantage of these optional attributes on your form input elements. Add the `required` attribute to the `firstName` input element.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="required-attribute" header="src/app/profile-editor/profile-editor.component.html (required attribute)"></code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="required-attribute" linenums="false" header="src/app/profile-editor/profile-editor.component.html (required attribute)">
</code-example>
<div class="alert is-important">
@ -344,7 +372,9 @@ When you add a required field to the form control, its initial status is invalid
Display the current status of `profileForm` using interpolation.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="display-status" header="src/app/profile-editor/profile-editor.component.html (display status)"></code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="display-status" linenums="false" header="src/app/profile-editor/profile-editor.component.html (display status)">
</code-example>
<figure>
<img src="generated/images/guide/reactive-forms/profile-editor-3.png" alt="Profile Editor Validation">
@ -380,7 +410,7 @@ The aliases control in the form group instance is now populated with a single co
### Step 3: Accessing the FormArray control
A getter provides easy access to the aliases in the form array instance compared to repeating the `profileForm.get()` method to get each instance. The form array instance represents an undefined number of controls in an array. It's convenient to access a control through a getter, and this approach is easy to repeat for additional controls.
A getter provides easy access to the aliases in the form array instance compared to repeating the `profileForm.get()` method to get each instance. The form array instance represents an undefined number of controls in an array. It's convenient to access a control through a getter, and this approach is easy to repeat for additional controls.
Use the getter syntax to create an `aliases` class property to retrieve the alias's form array control from the parent form group.
@ -404,13 +434,15 @@ In the template, each control is displayed as a separate input field.
### Step 4: Displaying the form array in the template
To attach the aliases from your form model, you must add it to the template. Similar to the `formGroupName` input provided by `FormGroupNameDirective`, `formArrayName` binds communication from the form array instance to the template with `FormArrayNameDirective`.
To attach the aliases from your form model, you must add it to the template. Similar to the `formGroupName` input provided by `FormGroupNameDirective`, `formArrayName` binds communication from the form array instance to the template with `FormArrayNameDirective`.
Add the template HTML below after the `<div>` closing the `formGroupName` element.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="formarrayname" header="src/app/profile-editor/profile-editor.component.html (aliases form array template)"></code-example>
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="formarrayname" linenums="false" header="src/app/profile-editor/profile-editor.component.html (aliases form array template)">
The `*ngFor` directive iterates over each form control instance provided by the aliases form array instance. Because form array elements are unnamed, you assign the index to the `i` variable and pass it to each control to bind it to the `formControlName` input.
</code-example>
The `*ngFor` directive iterates over each form control instance provided by the aliases form array instance. Because form array elements are unnamed, you assign the index to the `i` variable and pass it to each control to bind it to the `formControlName` input.
<figure>
<img src="generated/images/guide/reactive-forms/profile-editor-4.png" alt="Profile Editor Aliases">
@ -522,7 +554,7 @@ Listed below are the base classes and services used to create and manage form co
</td>
</tr>
</tr>
</table>

View File

@ -15,7 +15,8 @@ The [AnimationOptions](https://angular.io/api/animations/AnimationOptions) inter
To create a reusable animation, use the [`animation()`](https://angular.io/api/animations/animation) method to define an animation in a separate `.ts` file and declare this animation definition as a `const` export variable. You can then import and reuse this animation in any of your app components using the [`useAnimation()`](https://angular.io/api/animations/useAnimation) API.
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="reusable" language="typescript"></code-example>
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="reusable" language="typescript" linenums="false">
</code-example>
In the above code snippet, `transAnimation` is made reusable by declaring it as an export variable.
@ -26,7 +27,8 @@ In the above code snippet, `transAnimation` is made reusable by declaring it as
You can import the reusable `transAnimation` variable in your component class and reuse it using the `useAnimation()` method as shown below.
<code-example path="animations/src/app/open-close.component.3.ts" header="src/app/open-close.component.ts" region="reusable" language="typescript"></code-example>
<code-example path="animations/src/app/open-close.component.3.ts" header="src/app/open-close.component.ts" region="reusable" language="typescript" linenums="false">
</code-example>
## More on Angular animations
@ -35,4 +37,4 @@ You may also be interested in the following:
* [Introduction to Angular animations](guide/animations)
* [Transition and triggers](guide/transition-and-triggers)
* [Complex animation Sequences](guide/complex-animation-sequences)
* [Route transition animations](guide/route-animations)
* [Route transition animations](guide/route-animations)

View File

@ -43,7 +43,8 @@ Use the `RouterModule.forRoot` method to define a set of routes. Also, import th
The following configuration defines the possible routes for the application.
<code-example path="animations/src/app/app.module.ts" header="src/app/app.module.ts" region="route-animation-data" language="typescript"></code-example>
<code-example path="animations/src/app/app.module.ts" linenums="false" header="src/app/app.module.ts" region="route-animation-data" language="typescript">
</code-example>
The `home` and `about` paths are associated with the `HomeComponent` and `AboutComponent` views. The route configuration tells the Angular router to instantiate the `HomeComponent` and `AboutComponent` views when the navigation matches the corresponding path.
@ -61,11 +62,13 @@ After configuring the routes, tell the Angular router where to render the views
The `<router-outlet>` container has an attribute directive that contains data about active routes and their states, based on the `data` property that we set in the route configuration.
<code-example path="animations/src/app/app.component.html" header="src/app/app.component.html" region="route-animations-outlet"></code-example>
<code-example path="animations/src/app/app.component.html" header="src/app/app.component.html" region="route-animations-outlet">
</code-example>
`AppComponent` defines a method that can detect when a view changes. The method assigns an animation state value to the animation trigger (`@routeAnimation`) based on the route configuration `data` property value. Here's an example of an `AppComponent` method that detects when a route change happens.
<code-example path="animations/src/app/app.component.ts" header="src/app/app.component.ts" region="prepare-router-outlet" language="typescript"></code-example>
<code-example path="animations/src/app/app.component.ts" linenums="false" header="src/app/app.component.ts" region="prepare-router-outlet" language="typescript">
</code-example>
Here, the `prepareRoute()` method takes the value of the output directive (established through `#outlet="outlet"`) and returns a string value representing the state of the animation based on the custom data of the current active route. You can use this data to control which transition to execute for each route.
@ -76,7 +79,8 @@ Animations can be defined directly inside your components. For this example we a
The following code snippet defines a reusable animation named `slideInAnimation`.
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="route-animations" language="typescript"></code-example>
<code-example path="animations/src/app/animations.ts" linenums="false" header="src/app/animations.ts" region="route-animations" language="typescript">
</code-example>
The animation definition does several things:
@ -93,13 +97,15 @@ A route change activates the animation trigger, and a transition matching the st
Make the animation definition available in your application by adding the reusable animation (`slideInAnimation`) to the `animations` metadata of the `AppComponent`.
<code-example path="animations/src/app/app.component.ts" header="src/app/app.component.ts" region="define" language="typescript"></code-example>
<code-example path="animations/src/app/app.component.ts" linenums="false" header="src/app/app.component.ts" region="define" language="typescript">
</code-example>
### Styling the host and child components
During a transition, a new view is inserted directly after the old one and both elements appear on screen at the same time. To prevent this, apply additional styling to the host view, and to the removed and inserted child views. The host view must use relative positioning, and the child views must use absolute positioning. Adding styling to the views animates the containers in place, without the DOM moving things around.
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="style-view" language="typescript"></code-example>
<code-example path="animations/src/app/animations.ts" linenums="false" header="src/app/animations.ts" region="style-view" language="typescript">
</code-example>
### Querying the view containers
@ -107,7 +113,8 @@ Use the `query()` method to find and animate elements within the current host co
Let's assume that we are routing from the *Home => About*.
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="query" language="typescript"></code-example>
<code-example path="animations/src/app/animations.ts" linenums="false" header="src/app/animations.ts" region="query" language="typescript" linenums="false">
</code-example>
The animation code does the following after styling the views:

View File

@ -53,7 +53,9 @@ If the `app` folder is the application root, as it is for the sample application
set the `href` value *exactly* as shown here.
<code-example path="router/src/index.html" header="src/index.html (base-href)" region="base-href"></code-example>
<code-example path="router/src/index.html" linenums="false" header="src/index.html (base-href)" region="base-href">
</code-example>
@ -67,7 +69,9 @@ It is not part of the Angular core. It is in its own library package, `@angular/
Import what you need from it as you would from any other Angular package.
<code-example path="router/src/app/app.module.1.ts" header="src/app/app.module.ts (import)" region="import-router"></code-example>
<code-example path="router/src/app/app.module.1.ts" linenums="false" header="src/app/app.module.ts (import)" region="import-router">
</code-example>
@ -96,7 +100,9 @@ The following example creates five route definitions, configures the router via
and adds the result to the `AppModule`'s `imports` array.
<code-example path="router/src/app/app.module.0.ts" header="src/app/app.module.ts (excerpt)"></code-example>
<code-example path="router/src/app/app.module.0.ts" linenums="false" header="src/app/app.module.ts (excerpt)">
</code-example>
@ -171,7 +177,9 @@ an anchor tag.
Consider the following template:
<code-example path="router/src/app/app.component.1.html" header="src/app/app.component.html"></code-example>
<code-example path="router/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html">
</code-example>
The `RouterLink` directives on the anchor tags give the router control over those elements.
The navigation paths are fixed, so you can assign a string to the `routerLink` (a "one-time" binding).
@ -909,7 +917,9 @@ In order to use the Router, you must first register the `RouterModule` from the
</div>
<code-example path="router/src/app/app.module.1.ts" header="src/app/app.module.ts (first-config)" region="first-config"></code-example>
<code-example path="router/src/app/app.module.1.ts" linenums="false" header="src/app/app.module.ts (first-config)" region="first-config">
</code-example>
<div class="alert is-helpful">
@ -937,7 +947,9 @@ The router outlet serves as a placeholder when the routed components will be ren
The corresponding component template looks like this:
<code-example path="router/src/app/app.component.1.html" header="src/app/app.component.html"></code-example>
<code-example path="router/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html">
</code-example>
{@a wildcard}
@ -961,13 +973,17 @@ Be sure it is the _last_ route in the configuration.
To test this feature, add a button with a `RouterLink` to the `HeroListComponent` template and set the link to `"/sidekicks"`.
<code-example path="router/src/app/hero-list/hero-list.component.1.html" header="src/app/hero-list/hero-list.component.html (excerpt)"></code-example>
<code-example path="router/src/app/hero-list/hero-list.component.1.html" linenums="false" header="src/app/hero-list/hero-list.component.html (excerpt)">
</code-example>
The application will fail if the user clicks that button because you haven't defined a `"/sidekicks"` route yet.
Instead of adding the `"/sidekicks"` route, define a `wildcard` route instead and have it navigate to a simple `PageNotFoundComponent`.
<code-example path="router/src/app/app.module.1.ts" header="src/app/app.module.ts (wildcard)" region="wildcard"></code-example>
<code-example path="router/src/app/app.module.1.ts" linenums="false" header="src/app/app.module.ts (wildcard)" region="wildcard">
</code-example>
Create the `PageNotFoundComponent` to display when users visit invalid URLs.
@ -975,7 +991,9 @@ Create the `PageNotFoundComponent` to display when users visit invalid URLs.
ng generate component page-not-found
</code-example>
<code-example path="router/src/app/page-not-found/page-not-found.component.html" header="src/app/page-not-found.component.html (404 component)"></code-example>
<code-example path="router/src/app/page-not-found/page-not-found.component.html" linenums="false" header="src/app/page-not-found.component.html (404 component)">
</code-example>
Now when the user visits `/sidekicks`, or any other invalid URL, the browser displays "Page not found".
The browser address bar continues to point to the invalid URL.
@ -1004,7 +1022,8 @@ Add the default route somewhere _above_ the wildcard route.
It's just above the wildcard route in the following excerpt showing the complete `appRoutes` for this milestone.
<code-example path="router/src/app/app-routing.module.1.ts" header="src/app/app-routing.module.ts (appRoutes)" region="appRoutes"></code-example>
<code-example path="router/src/app/app-routing.module.1.ts" linenums="false" header="src/app/app-routing.module.ts (appRoutes)" region="appRoutes">
</code-example>
A redirect route requires a `pathMatch` property to tell the router how to match a URL to the path of a route.
@ -1295,12 +1314,16 @@ By re-exporting the `RouterModule` here the components declared in `AppModule` w
After these steps, the file should look like this.
<code-example path="router/src/app/app-routing.module.1.ts" header="src/app/app-routing.module.ts"></code-example>
<code-example path="router/src/app/app-routing.module.1.ts" header="src/app/app-routing.module.ts">
</code-example>
Next, update the `app.module.ts` file, removing `RouterModule.forRoot` in
the `imports` array.
<code-example path="router/src/app/app.module.2.ts" header="src/app/app.module.ts"></code-example>
<code-example path="router/src/app/app.module.2.ts" header="src/app/app.module.ts">
</code-example>
@ -1419,7 +1442,9 @@ Next, you'll update the `HeroesModule` metadata.
* Import and add the `HeroDetailComponent` and `HeroListComponent` to the `declarations` array in the `HeroesModule`.
<code-example path="router/src/app/heroes/heroes.module.ts" header="src/app/heroes/heroes.module.ts"></code-example>
<code-example path="router/src/app/heroes/heroes.module.ts" header="src/app/heroes/heroes.module.ts">
</code-example>
@ -1543,7 +1568,9 @@ In any other module, you must call the **`RouterModule.forChild`** method to reg
The updated `HeroesRoutingModule` looks like this:
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" header="src/app/heroes/heroes-routing.module.ts"></code-example>
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" header="src/app/heroes/heroes-routing.module.ts">
</code-example>
@ -1575,7 +1602,9 @@ Remove the `HeroListComponent` import and the `/heroes` route from the `app-rout
These are concerns at the top level of the application itself.
<code-example path="router/src/app/app-routing.module.2.ts" header="src/app/app-routing.module.ts (v2)"></code-example>
<code-example path="router/src/app/app-routing.module.2.ts" linenums="false" header="src/app/app-routing.module.ts (v2)">
</code-example>
@ -1589,7 +1618,9 @@ Remove the `HeroListComponent` from the `AppModule`'s `declarations` because it'
After these steps, the `AppModule` should look like this:
<code-example path="router/src/app/app.module.3.ts" header="src/app/app.module.ts"></code-example>
<code-example path="router/src/app/app.module.3.ts" header="src/app/app.module.ts">
</code-example>
@ -1601,7 +1632,9 @@ After these steps, the `AppModule` should look like this:
Look at the module `imports` array. Notice that the `AppRoutingModule` is _last_.
Most importantly, it comes _after_ the `HeroesModule`.
<code-example path="router/src/app/app.module.3.ts" region="module-imports" header="src/app/app.module.ts (module-imports)"></code-example>
<code-example path="router/src/app/app.module.3.ts" region="module-imports" header="src/app/app.module.ts (module-imports)" linenums="false">
</code-example>
@ -1645,7 +1678,9 @@ Return to the `HeroesRoutingModule` and look at the route definitions again.
The route to `HeroDetailComponent` has a twist.
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" header="src/app/heroes/heroes-routing.module.ts (excerpt)" region="hero-detail-route"></code-example>
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" linenums="false" header="src/app/heroes/heroes-routing.module.ts (excerpt)" region="hero-detail-route">
</code-example>
@ -1699,7 +1734,9 @@ Accordingly, the _link parameters array_ has *two* items: the routing _path_ an
`id` of the selected hero.
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array"></code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" linenums="false" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array">
</code-example>
@ -1728,14 +1765,18 @@ the `HeroDetailComponent` via the `ActivatedRoute` service.
Import the `Router`, `ActivatedRoute`, and `ParamMap` tokens from the router package.
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (activated route)" region="imports"></code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (activated route)" region="imports">
</code-example>
Import the `switchMap` operator because you need it later to process the `Observable` route parameters.
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (switchMap operator import)" region="rxjs-operator-import"></code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (switchMap operator import)" region="rxjs-operator-import">
</code-example>
@ -1746,13 +1787,17 @@ As usual, you write a constructor that asks Angular to inject services
that the component requires and reference them as private variables.
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (constructor)" region="ctor"></code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (constructor)" region="ctor">
</code-example>
Later, in the `ngOnInit` method, you use the `ActivatedRoute` service to retrieve the parameters for the route,
pull the hero `id` from the parameters and retrieve the hero to display.
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit)" region="ngOnInit"></code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit)" region="ngOnInit">
</code-example>
The `paramMap` processing is a bit tricky. When the map changes, you `get()`
the `id` parameter from the changed parameters.
@ -1889,7 +1934,9 @@ You can access the parameters directly without subscribing or adding observable
It's much simpler to write and read:
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.2.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit snapshot)" region="snapshot"></code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.2.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit snapshot)" region="snapshot">
</code-example>
@ -1920,7 +1967,9 @@ that you can bind to a `[routerLink]` directive.
It holds the _path to the `HeroListComponent`_:
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (excerpt)" region="gotoHeroes"></code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (excerpt)" region="gotoHeroes">
</code-example>
{@a optional-route-parameters}
@ -1980,7 +2029,9 @@ When navigating to the `HeroDetailComponent` you specified the _required_ `id` o
*route parameter* and made it the second item of the [_link parameters array_](#link-parameters-array).
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array"></code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" linenums="false" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array">
</code-example>
@ -1988,7 +2039,9 @@ The router embedded the `id` value in the navigation URL because you had defined
as a route parameter with an `:id` placeholder token in the route `path`:
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" header="src/app/heroes/heroes-routing.module.ts (hero-detail-route)" region="hero-detail-route"></code-example>
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" linenums="false" header="src/app/heroes/heroes-routing.module.ts (hero-detail-route)" region="hero-detail-route">
</code-example>
@ -1996,7 +2049,9 @@ When the user clicks the back button, the `HeroDetailComponent` constructs anoth
which it uses to navigate back to the `HeroListComponent`.
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (gotoHeroes)" region="gotoHeroes"></code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (gotoHeroes)" region="gotoHeroes">
</code-example>
@ -2011,7 +2066,9 @@ For demonstration purposes, there's an extra junk parameter (`foo`) in the objec
Here's the revised navigation statement:
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (go to heroes)" region="gotoHeroes"></code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (go to heroes)" region="gotoHeroes">
</code-example>
@ -2091,21 +2148,27 @@ This time you'll be navigating in the opposite direction, from the `HeroDetailCo
First you extend the router import statement to include the `ActivatedRoute` service symbol:
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" header="src/app/heroes/hero-list/hero-list.component.ts (import)" region="import-router"></code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" linenums="false" header="src/app/heroes/hero-list/hero-list.component.ts (import)" region="import-router">
</code-example>
Import the `switchMap` operator to perform an operation on the `Observable` of route parameter map.
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" header="src/app/heroes/hero-list/hero-list.component.ts (rxjs imports)" region="rxjs-imports"></code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" linenums="false" header="src/app/heroes/hero-list/hero-list.component.ts (rxjs imports)" region="rxjs-imports">
</code-example>
Then you inject the `ActivatedRoute` in the `HeroListComponent` constructor.
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" header="src/app/heroes/hero-list/hero-list.component.ts (constructor and ngOnInit)" region="ctor"></code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" linenums="false" header="src/app/heroes/hero-list/hero-list.component.ts (constructor and ngOnInit)" region="ctor">
</code-example>
@ -2118,11 +2181,15 @@ The binding adds the `selected` CSS class when the comparison returns `true` and
Look for it within the repeated `<li>` tag as shown here:
<code-example path="router/src/app/heroes/hero-list/hero-list.component.html" header="src/app/heroes/hero-list/hero-list.component.html"></code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.html" linenums="false" header="src/app/heroes/hero-list/hero-list.component.html">
</code-example>
Add some styles to apply when the list item is selected.
<code-example path="router/src/app/heroes/hero-list/hero-list.component.css" region="selected" header="src/app/heroes/hero-list/hero-list.component.css"></code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.css" linenums="false" region="selected" header="src/app/heroes/hero-list/hero-list.component.css">
</code-example>
@ -2148,16 +2215,22 @@ This section shows you how to add some [animations](guide/animations) to the `He
First import the `BrowserAnimationsModule` and add it to the `imports` array:
<code-example path="router/src/app/app.module.ts" header="src/app/app.module.ts (animations-module)" region="animations-module"></code-example>
<code-example path="router/src/app/app.module.ts" linenums="false" header="src/app/app.module.ts (animations-module)" region="animations-module">
</code-example>
Next, add a `data` object to the routes for `HeroListComponent` and `HeroDetailComponent`. Transitions are based on `states` and you'll use the `animation` data from the route to provide a named animation `state` for the transitions.
<code-example path="router/src/app/heroes/heroes-routing.module.2.ts" header="src/app/heroes/heroes-routing.module.ts (animation data)"></code-example>
<code-example path="router/src/app/heroes/heroes-routing.module.2.ts" header="src/app/heroes/heroes-routing.module.ts (animation data)">
</code-example>
Create an `animations.ts` file in the root `src/app/` folder. The contents look like this:
<code-example path="router/src/app/animations.ts" header="src/app/animations.ts (excerpt)"></code-example>
<code-example path="router/src/app/animations.ts" linenums="false" header="src/app/animations.ts (excerpt)">
</code-example>
This file does the following:
@ -2175,18 +2248,24 @@ Back in the `AppComponent`, import the `RouterOutlet` token from the `@angular/r
Add an `animations` array to the `@Component` metadata's that contains the `slideInAnimation`.
<code-example path="router/src/app/app.component.2.ts" header="src/app/app.component.ts (animations)" region="animation-imports"></code-example>
<code-example path="router/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (animations)" region="animation-imports">
</code-example>
In order to use the routable animations, you'll need to wrap the `RouterOutlet` inside an element. You'll
use the `@routeAnimation` trigger and bind it to the element.
For the `@routeAnimation` transitions to key off states, you'll need to provide it with the `data` from the `ActivatedRoute`. The `RouterOutlet` is exposed as an `outlet` template variable, so you bind a reference to the router outlet. A variable of `routerOutlet` is an ideal choice.
<code-example path="router/src/app/app.component.2.html" header="src/app/app.component.html (router outlet)"></code-example>
<code-example path="router/src/app/app.component.2.html" linenums="false" header="src/app/app.component.html (router outlet)">
</code-example>
The `@routeAnimation` property is bound to the `getAnimationData` with the provided `routerOutlet` reference, so you'll need to define that function in the `AppComponent`. The `getAnimationData` function returns the animation property from the `data` provided through the `ActivatedRoute`. The `animation` property matches the `transition` names you used in the `slideInAnimation` defined in `animations.ts`.
<code-example path="router/src/app/app.component.2.ts" header="src/app/app.component.ts (router outlet)" region="function-binding"></code-example>
<code-example path="router/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (router outlet)" region="function-binding">
</code-example>
When switching between the two routes, the `HeroDetailComponent` and `HeroListComponent` will ease in from the left when routed to and will slide to the right when navigating away.
@ -2484,7 +2563,9 @@ Begin by imitating the heroes feature:
You'll use mock crises instead of mock heroes:
<code-example path="router/src/app/crisis-center/mock-crises.ts" header="src/app/crisis-center/mock-crises.ts"></code-example>
<code-example path="router/src/app/crisis-center/mock-crises.ts" header="src/app/crisis-center/mock-crises.ts">
</code-example>
The resulting crisis center is a foundation for introducing a new concept&mdash;**child routing**.
@ -2541,7 +2622,8 @@ Generate a `CrisisCenter` component in the `crisis-center` folder:
Update the component template to look like this:
<code-example path="router/src/app/crisis-center/crisis-center/crisis-center.component.html" header="src/app/crisis-center/crisis-center/crisis-center.component.html"></code-example>
<code-example path="router/src/app/crisis-center/crisis-center/crisis-center.component.html" linenums="false" header="src/app/crisis-center/crisis-center/crisis-center.component.html">
</code-example>
The `CrisisCenterComponent` has the following in common with the `AppComponent`:
@ -2568,12 +2650,14 @@ As a host page for the "Crisis Center" feature, generate a `CrisisCenterHome` co
Update the template with a welcome message to the `Crisis Center`.
<code-example path="router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.html" header="src/app/crisis-center/crisis-center-home/crisis-center-home.component.html"></code-example>
<code-example path="router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.html" linenums="false" header="src/app/crisis-center/crisis-center-home/crisis-center-home.component.html">
</code-example>
Update the `crisis-center-routing.module.ts` you renamed after copying it from `heroes-routing.module.ts` file.
This time, you define **child routes** *within* the parent `crisis-center` route.
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.1.ts" header="src/app/crisis-center/crisis-center-routing.module.ts (Routes)" region="routes"></code-example>
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.1.ts" linenums="false" header="src/app/crisis-center/crisis-center-routing.module.ts (Routes)" region="routes">
</code-example>
Notice that the parent `crisis-center` route has a `children` property
@ -2619,7 +2703,9 @@ The absolute URL for the latter example, including the `localhost` origin, is
Here's the complete `crisis-center-routing.module.ts` file with its imports.
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.1.ts" header="src/app/crisis-center/crisis-center-routing.module.ts (excerpt)"></code-example>
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.1.ts" linenums="false" header="src/app/crisis-center/crisis-center-routing.module.ts (excerpt)">
</code-example>
@ -2637,7 +2723,7 @@ _before_ the `AppRoutingModule`:
</code-pane>
<code-pane path="router/src/app/app.module.4.ts" header="src/app/app.module.ts (import CrisisCenterModule)" region="crisis-center-module">
<code-pane path="router/src/app/app.module.4.ts" linenums="false" header="src/app/app.module.ts (import CrisisCenterModule)" region="crisis-center-module">
</code-pane>
@ -2649,7 +2735,9 @@ The feature routes are now provided by the `HeroesModule` and the `CrisisCenter`
The `app-routing.module.ts` file retains the top-level application routes such as the default and wildcard routes.
<code-example path="router/src/app/app-routing.module.3.ts" header="src/app/app-routing.module.ts (v3)" region="v3"></code-example>
<code-example path="router/src/app/app-routing.module.3.ts" linenums="false" header="src/app/app-routing.module.ts (v3)" region="v3">
</code-example>
@ -2726,7 +2814,9 @@ The `ActivatedRoute` is implicit in a `RouterLink` directive.
Update the `gotoCrises` method of the `CrisisDetailComponent` to navigate back to the *Crisis Center* list using relative path navigation.
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (relative navigation)" region="gotoCrises-navigate"></code-example>
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" linenums="false" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (relative navigation)" region="gotoCrises-navigate">
</code-example>
Notice that the path goes up a level using the `../` syntax.
@ -2757,7 +2847,9 @@ Multiple outlets can be displaying different content, determined by different ro
Add an outlet named "popup" in the `AppComponent`, directly below the unnamed outlet.
<code-example path="router/src/app/app.component.4.html" header="src/app/app.component.html (outlets)" region="outlets"></code-example>
<code-example path="router/src/app/app.component.4.html" linenums="false" header="src/app/app.component.html (outlets)" region="outlets">
</code-example>
@ -2831,7 +2923,9 @@ That's a peculiarity covered [below](#clear-secondary-routes).
Open the `AppRoutingModule` and add a new `compose` route to the `appRoutes`.
<code-example path="router/src/app/app-routing.module.3.ts" header="src/app/app-routing.module.ts (compose route)" region="compose"></code-example>
<code-example path="router/src/app/app-routing.module.3.ts" linenums="false" header="src/app/app-routing.module.ts (compose route)" region="compose">
</code-example>
@ -2842,7 +2936,9 @@ This route now targets the popup outlet and the `ComposeMessageComponent` will d
The user needs a way to open the popup.
Open the `AppComponent` and add a "Contact" link.
<code-example path="router/src/app/app.component.4.html" header="src/app/app.component.html (contact-link)" region="contact-link"></code-example>
<code-example path="router/src/app/app.component.4.html" linenums="false" header="src/app/app.component.html (contact-link)" region="contact-link">
</code-example>
@ -2935,7 +3031,9 @@ That's why the popup stays visible as you navigate among the crises and heroes.
Clicking the "send" or "cancel" buttons _does_ clear the popup view.
To see how, look at the `closePopup()` method again:
<code-example path="router/src/app/compose-message/compose-message.component.ts" header="src/app/compose-message/compose-message.component.ts (closePopup)" region="closePopup"></code-example>
<code-example path="router/src/app/compose-message/compose-message.component.ts" linenums="false" header="src/app/compose-message/compose-message.component.ts (closePopup)" region="closePopup">
</code-example>
@ -3168,11 +3266,11 @@ feature module, a dashboard route and two unfinished components to manage crises
<code-tabs>
<code-pane header="src/app/admin/admin/admin.component.html" path="router/src/app/admin/admin/admin.component.html">
<code-pane header="src/app/admin/admin/admin.component.html" linenums="false" path="router/src/app/admin/admin/admin.component.html">
</code-pane>
<code-pane header="src/app/admin/admin-dashboard/admin-dashboard.component.html" path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.html">
<code-pane header="src/app/admin/admin-dashboard/admin-dashboard.component.html" linenums="false" path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.html">
</code-pane>
@ -3180,11 +3278,11 @@ feature module, a dashboard route and two unfinished components to manage crises
</code-pane>
<code-pane header="src/app/admin/manage-crises/manage-crises.component.html" path="router/src/app/admin/manage-crises/manage-crises.component.html">
<code-pane header="src/app/admin/manage-crises/manage-crises.component.html" linenums="false" path="router/src/app/admin/manage-crises/manage-crises.component.html">
</code-pane>
<code-pane header="src/app/admin/manage-heroes/manage-heroes.component.html" path="router/src/app/admin/manage-heroes/manage-heroes.component.html">
<code-pane header="src/app/admin/manage-heroes/manage-heroes.component.html" linenums="false" path="router/src/app/admin/manage-heroes/manage-heroes.component.html">
</code-pane>
@ -3211,7 +3309,9 @@ is considered a match to any route within the admin feature area. You only want
The initial admin routing configuration:
<code-example path="router/src/app/admin/admin-routing.module.1.ts" header="src/app/admin/admin-routing.module.ts (admin routing)" region="admin-routes"></code-example>
<code-example path="router/src/app/admin/admin-routing.module.1.ts" linenums="false" header="src/app/admin/admin-routing.module.ts (admin routing)" region="admin-routes">
</code-example>
Looking at the child route under the `AdminComponent`, there is a `path` and a `children`
property but it's not using a `component`.
@ -3227,14 +3327,18 @@ Next, import the `AdminModule` into `app.module.ts` and add it to the `imports`
to register the admin routes.
<code-example path="router/src/app/app.module.4.ts" header="src/app/app.module.ts (admin module)" region="admin-module"></code-example>
<code-example path="router/src/app/app.module.4.ts" linenums="false" header="src/app/app.module.ts (admin module)" region="admin-module">
</code-example>
Add an "Admin" link to the `AppComponent` shell so that users can get to this feature.
<code-example path="router/src/app/app.component.5.html" header="src/app/app.component.html (template)"></code-example>
<code-example path="router/src/app/app.component.5.html" linenums="false" header="src/app/app.component.html (template)">
</code-example>
@ -3263,7 +3367,9 @@ At the moment you're interested in seeing how guards work so the first version d
It simply logs to console and `returns` true immediately, allowing navigation to proceed:
<code-example path="router/src/app/auth/auth.guard.1.ts" header="src/app/auth/auth.guard.ts (excerpt)"></code-example>
<code-example path="router/src/app/auth/auth.guard.1.ts" linenums="false" header="src/app/auth/auth.guard.ts (excerpt)">
</code-example>
@ -3271,7 +3377,9 @@ Next, open `admin-routing.module.ts `, import the `AuthGuard` class, and
update the admin route with a `canActivate` guard property that references it:
<code-example path="router/src/app/admin/admin-routing.module.2.ts" header="src/app/admin/admin-routing.module.ts (guarded admin route)" region="admin-route"></code-example>
<code-example path="router/src/app/admin/admin-routing.module.2.ts" linenums="false" header="src/app/admin/admin-routing.module.ts (guarded admin route)" region="admin-route">
</code-example>
@ -3293,7 +3401,9 @@ The `AuthGuard` should call an application service that can login a user and ret
Update the `AuthService` to log in the user:
<code-example path="router/src/app/auth/auth.service.ts" header="src/app/auth/auth.service.ts (excerpt)"></code-example>
<code-example path="router/src/app/auth/auth.service.ts" linenums="false" header="src/app/auth/auth.service.ts (excerpt)">
</code-example>
@ -3306,7 +3416,9 @@ The `redirectUrl` property will store the attempted URL so you can navigate to i
Revise the `AuthGuard` to call it.
<code-example path="router/src/app/auth/auth.guard.2.ts" header="src/app/auth/auth.guard.ts (v2)"></code-example>
<code-example path="router/src/app/auth/auth.guard.2.ts" linenums="false" header="src/app/auth/auth.guard.ts (v2)">
</code-example>
@ -3384,7 +3496,9 @@ async checks and a `boolean` for sync checks.
This one returns a `boolean`:
<code-example path="router/src/app/auth/auth.guard.3.ts" header="src/app/auth/auth.guard.ts (excerpt)" region="can-activate-child"></code-example>
<code-example path="router/src/app/auth/auth.guard.3.ts" linenums="false" header="src/app/auth/auth.guard.ts (excerpt)" region="can-activate-child">
</code-example>
@ -3392,7 +3506,9 @@ Add the same `AuthGuard` to the `component-less` admin route to protect all othe
instead of adding the `AuthGuard` to each route individually.
<code-example path="router/src/app/admin/admin-routing.module.3.ts" header="src/app/admin/admin-routing.module.ts (excerpt)" region="can-activate-child"></code-example>
<code-example path="router/src/app/admin/admin-routing.module.3.ts" linenums="false" header="src/app/admin/admin-routing.module.ts (excerpt)" region="can-activate-child">
</code-example>
@ -3441,7 +3557,9 @@ discards the changes when the user presses the *Cancel* button.
Both buttons navigate back to the crisis list after save or cancel.
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (cancel and save methods)" region="cancel-save"></code-example>
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" linenums="false" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (cancel and save methods)" region="cancel-save">
</code-example>
@ -3475,7 +3593,9 @@ Generate a `Dialog` service to handle user confirmation.
Add a `confirm()` method to the `DialogService` to prompt the user to confirm their intent. The `window.confirm` is a _blocking_ action that displays a modal dialog and waits for user interaction.
<code-example path="router/src/app/dialog.service.ts" header="src/app/dialog.service.ts"></code-example>
<code-example path="router/src/app/dialog.service.ts" header="src/app/dialog.service.ts">
</code-example>
It returns an `Observable` that *resolves* when the user eventually decides what to do: either
to discard changes and navigate away (`true`) or to preserve the pending changes and stay in the crisis editor (`false`).
@ -3497,7 +3617,9 @@ It need only detect that the component has a `canDeactivate()` method and call i
This approach makes the guard reusable.
<code-example path="router/src/app/can-deactivate.guard.ts" header="src/app/can-deactivate.guard.ts"></code-example>
<code-example path="router/src/app/can-deactivate.guard.ts" header="src/app/can-deactivate.guard.ts">
</code-example>
@ -3510,14 +3632,18 @@ wanted to use this guard for this component and needed to get
the component's properties or confirm whether the router should allow navigation away from it.
<code-example path="router/src/app/can-deactivate.guard.1.ts" header="src/app/can-deactivate.guard.ts (component-specific)"></code-example>
<code-example path="router/src/app/can-deactivate.guard.1.ts" linenums="false" header="src/app/can-deactivate.guard.ts (component-specific)">
</code-example>
Looking back at the `CrisisDetailComponent`, it implements the confirmation workflow for unsaved changes.
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (excerpt)" region="canDeactivate"></code-example>
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" linenums="false" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (excerpt)" region="canDeactivate">
</code-example>
@ -3530,7 +3656,9 @@ to resolve to truthy (navigate) or falsy (stay put).
Add the `Guard` to the crisis detail route in `crisis-center-routing.module.ts` using the `canDeactivate` array property.
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.3.ts" header="src/app/crisis-center/crisis-center-routing.module.ts (can deactivate guard)"></code-example>
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.3.ts" linenums="false" header="src/app/crisis-center/crisis-center-routing.module.ts (can deactivate guard)">
</code-example>
Now you have given the user a safeguard against unsaved changes.
@ -3576,7 +3704,9 @@ Generate a `CrisisDetailResolver` service file within the `Crisis Center` featur
</code-example>
<code-example path="router/src/app/crisis-center/crisis-detail-resolver.service.1.ts" header="src/app/crisis-center/crisis-detail-resolver.service.ts (generated)"></code-example>
<code-example path="router/src/app/crisis-center/crisis-detail-resolver.service.1.ts" header="src/app/crisis-center/crisis-detail-resolver.service.ts (generated)">
</code-example>
@ -3598,13 +3728,17 @@ Observable completes after retrieving the first value from the Observable return
If it doesn't return a valid `Crisis`, return an empty `Observable`, canceling the previous in-flight navigation to the `CrisisDetailComponent` and navigate the user back to the `CrisisListComponent`. The update resolver service looks like this:
<code-example path="router/src/app/crisis-center/crisis-detail-resolver.service.ts" header="src/app/crisis-center/crisis-detail-resolver.service.ts"></code-example>
<code-example path="router/src/app/crisis-center/crisis-detail-resolver.service.ts" header="src/app/crisis-center/crisis-detail-resolver.service.ts">
</code-example>
Import this resolver in the `crisis-center-routing.module.ts`
and add a `resolve` object to the `CrisisDetailComponent` route configuration.
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.4.ts" header="src/app/crisis-center/crisis-center-routing.module.ts (resolver)"></code-example>
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.4.ts" linenums="false" header="src/app/crisis-center/crisis-center-routing.module.ts (resolver)">
</code-example>
@ -3614,7 +3748,9 @@ that's where you said it should be when you re-configured the route.
It will be there when the `CrisisDetailComponent` ask for it.
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (ngOnInit v2)" region="ngOnInit"></code-example>
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" linenums="false" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (ngOnInit v2)" region="ngOnInit">
</code-example>
@ -3717,7 +3853,9 @@ Add an `anchor` element so you can jump to a certain point on the page.
Add the `NavigationExtras` object to the `router.navigate()` method that navigates you to the `/login` route.
<code-example path="router/src/app/auth/auth.guard.4.ts" header="src/app/auth/auth.guard.ts (v3)"></code-example>
<code-example path="router/src/app/auth/auth.guard.4.ts" linenums="false" header="src/app/auth/auth.guard.ts (v3)">
</code-example>
@ -3728,7 +3866,9 @@ and provide the `queryParamsHandling` and `preserveFragment` to pass along the c
and fragment to the next route.
<code-example path="router/src/app/auth/login/login.component.ts" header="src/app/auth/login/login.component.ts (preserve)" region="preserve"></code-example>
<code-example path="router/src/app/auth/login/login.component.ts" linenums="false" header="src/app/auth/login/login.component.ts (preserve)" region="preserve">
</code-example>
<div class="alert is-helpful">
@ -3745,7 +3885,9 @@ As you'll be navigating to the *Admin Dashboard* route after logging in, you'll
query parameters and fragment.
<code-example path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (v2)"></code-example>
<code-example path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts" linenums="false" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (v2)">
</code-example>
@ -3817,7 +3959,9 @@ The `loadChildren` property takes a function that returns a promise using the br
The path is the location of the `AdminModule` (relative to the app root).
After the code is requested and loaded, the `Promise` resolves an object that contains the `NgModule`, in this case the `AdminModule`.
<code-example path="router/src/app/app-routing.module.5.ts" region="admin-1" header="app-routing.module.ts (load children)"></code-example>
<code-example path="router/src/app/app-routing.module.5.ts" region="admin-1" header="app-routing.module.ts (load children)">
</code-example>
<div class="alert is-important">
@ -3876,7 +4020,9 @@ Add it to the `AuthGuard` class's `implements` list.
Then implement `canLoad()` as follows:
<code-example path="router/src/app/auth/auth.guard.ts" header="src/app/auth/auth.guard.ts (CanLoad guard)" region="canLoad"></code-example>
<code-example path="router/src/app/auth/auth.guard.ts" linenums="false" header="src/app/auth/auth.guard.ts (CanLoad guard)" region="canLoad">
</code-example>
@ -3888,7 +4034,9 @@ array property for the `admin` route.
The completed admin route looks like this:
<code-example path="router/src/app/app-routing.module.5.ts" region="admin" header="app-routing.module.ts (lazy admin route)"></code-example>
<code-example path="router/src/app/app-routing.module.5.ts" region="admin" header="app-routing.module.ts (lazy admin route)">
</code-example>
@ -3987,7 +4135,9 @@ The second argument in the `RouterModule.forRoot()` method takes an object for a
The `preloadingStrategy` is one of those options.
Add the `PreloadAllModules` token to the `forRoot()` call:
<code-example path="router/src/app/app-routing.module.6.ts" header="src/app/app-routing.module.ts (preload all)" region="forRoot"></code-example>
<code-example path="router/src/app/app-routing.module.6.ts" linenums="false" header="src/app/app-routing.module.ts (preload all)" region="forRoot">
</code-example>
@ -4032,7 +4182,9 @@ Recall that you can add anything to the `data` property of a route.
Set the `data.preload` flag in the `crisis-center` route in the `AppRoutingModule`.
<code-example path="router/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts (route data preload)" region="preload-v2"></code-example>
<code-example path="router/src/app/app-routing.module.ts" linenums="false" header="src/app/app-routing.module.ts (route data preload)" region="preload-v2">
</code-example>
Generate a new `SelectivePreloadingStrategy` service.
@ -4041,7 +4193,9 @@ Generate a new `SelectivePreloadingStrategy` service.
</code-example>
<code-example path="router/src/app/selective-preloading-strategy.service.ts" header="src/app/selective-preloading-strategy.service.ts (excerpt)"></code-example>
<code-example path="router/src/app/selective-preloading-strategy.service.ts" linenums="false" header="src/app/selective-preloading-strategy.service.ts (excerpt)">
</code-example>
@ -4079,7 +4233,9 @@ Now edit the `AdminDashboardComponent` to display the log of preloaded routes.
When you're done it looks like this.
<code-example path="router/src/app/admin/admin-dashboard/admin-dashboard.component.ts" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (preloaded modules)"></code-example>
<code-example path="router/src/app/admin/admin-dashboard/admin-dashboard.component.ts" linenums="false" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (preloaded modules)">
</code-example>
@ -4101,7 +4257,9 @@ You've setup the routes for navigating around your application. You've used navi
Let's take the `Hero` routes and migrate them to new URLs. The `Router` checks for redirects in your configuration before navigating, so each redirect is triggered when needed. To support this change, you'll add redirects from the old routes to the new routes in the `heroes-routing.module`.
<code-example path="router/src/app/heroes/heroes-routing.module.ts" header="src/app/heroes/heroes-routing.module.ts (heroes redirects)"></code-example>
<code-example path="router/src/app/heroes/heroes-routing.module.ts" linenums="false" header="src/app/heroes/heroes-routing.module.ts (heroes redirects)">
</code-example>
You'll notice two different types of redirects. The first change is from `/heroes` to `/superheroes` without any parameters. This is a straightforward redirect, unlike the change from `/hero/:id` to `/superhero/:id`, which includes the `:id` route parameter. Router redirects also use powerful pattern matching, so the `Router` inspects the URL and replaces route parameters in the `path` with their appropriate destination. Previously, you navigated to a URL such as `/hero/15` with a route parameter `id` of `15`.
@ -4119,15 +4277,21 @@ Before updating the `app-routing.module.ts`, you'll need to consider an importan
So instead, you'll update the empty path route in `app-routing.module.ts` to redirect to `/superheroes`.
<code-example path="router/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts (superheroes redirect)"></code-example>
<code-example path="router/src/app/app-routing.module.ts" linenums="false" header="src/app/app-routing.module.ts (superheroes redirect)">
</code-example>
`RouterLink`s aren't tied to route configuration, so you'll need to update the associated router links so they remain active when the new route is active. You'll update the `app.component.ts` template for the `/heroes` routerLink.
<code-example path="router/src/app/app.component.html" header="src/app/app.component.html (superheroes active routerLink)"></code-example>
<code-example path="router/src/app/app.component.html" linenums="false" header="src/app/app.component.html (superheroes active routerLink)">
</code-example>
Update the `goToHeroes()` method in the `hero-detail.component.ts` to navigate back to `/superheroes` with the optional route parameters.
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.ts" region="redirect" header="src/app/heroes/hero-detail/hero-detail.component.ts (goToHeroes)"></code-example>
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.ts" linenums="false" region="redirect" header="src/app/heroes/hero-detail/hero-detail.component.ts (goToHeroes)">
</code-example>
With the redirects setup, all previous routes now point to their new destinations and both URLs still function as intended.
@ -4148,7 +4312,9 @@ examining its `config` property.
For example, update the `AppModule` as follows and look in the browser console window
to see the finished route configuration.
<code-example path="router/src/app/app.module.7.ts" header="src/app/app.module.ts (inspect the router config)" region="inspect-config"></code-example>
<code-example path="router/src/app/app.module.7.ts" linenums="false" header="src/app/app.module.ts (inspect the router config)" region="inspect-config">
</code-example>
{@a final-app}
@ -4187,21 +4353,27 @@ A link parameters array holds the following ingredients for router navigation:
You can bind the `RouterLink` directive to such an array like this:
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (h-anchor)" region="h-anchor"></code-example>
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (h-anchor)" region="h-anchor">
</code-example>
You've written a two element array when specifying a route parameter like this:
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" header="src/app/heroes/hero-list/hero-list.component.html (nav-to-detail)" region="nav-to-detail"></code-example>
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" linenums="false" header="src/app/heroes/hero-list/hero-list.component.html (nav-to-detail)" region="nav-to-detail">
</code-example>
You can provide optional route parameters in an object like this:
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (cc-query-params)" region="cc-query-params"></code-example>
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (cc-query-params)" region="cc-query-params">
</code-example>
@ -4211,7 +4383,9 @@ The moment you add a child router, such as the crisis center, you create new lin
Recall that you specified a default child route for the crisis center so this simple `RouterLink` is fine.
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (cc-anchor-w-default)" region="cc-anchor-w-default"></code-example>
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (cc-anchor-w-default)" region="cc-anchor-w-default">
</code-example>
@ -4227,7 +4401,9 @@ Take it a step further. Consider the following router link that
navigates from the root of the application down to the *Dragon Crisis*:
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (Dragon-anchor)" region="Dragon-anchor"></code-example>
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (Dragon-anchor)" region="Dragon-anchor">
</code-example>
@ -4242,7 +4418,9 @@ navigates from the root of the application down to the *Dragon Crisis*:
If you wanted to, you could redefine the `AppComponent` template with *Crisis Center* routes exclusively:
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
</code-example>
@ -4375,7 +4553,9 @@ If the `app` folder is the application root, as it is for this application,
set the `href` value in **`index.html`** *exactly* as shown here.
<code-example path="router/src/index.html" header="src/index.html (base-href)" region="base-href"></code-example>
<code-example path="router/src/index.html" linenums="false" header="src/index.html (base-href)" region="base-href">
</code-example>
#### HTML5 URLs and the *&lt;base href>*
@ -4388,7 +4568,9 @@ The preferred way to configure the strategy is to add a
tag in the `<head>` of the `index.html`.
<code-example path="router/src/index.html" header="src/index.html (base-href)" region="base-href"></code-example>
<code-example path="router/src/index.html" linenums="false" header="src/index.html (base-href)" region="base-href">
</code-example>
@ -4414,4 +4596,6 @@ providing the `useHash: true` in an object as the second argument of the `Router
in the `AppModule`.
<code-example path="router/src/app/app.module.6.ts" header="src/app/app.module.ts (hash URL strategy)"></code-example>
<code-example path="router/src/app/app.module.6.ts" linenums="false" header="src/app/app.module.ts (hash URL strategy)">
</code-example>

View File

@ -1,6 +1,6 @@
# The RxJS library
Reactive programming is an asynchronous programming paradigm concerned with data streams and the propagation of change ([Wikipedia](https://en.wikipedia.org/wiki/Reactive_programming)). RxJS (Reactive Extensions for JavaScript) is a library for reactive programming using observables that makes it easier to compose asynchronous or callback-based code. See ([RxJS Docs](https://rxjs.dev/guide/overview)).
Reactive programming is an asynchronous programming paradigm concerned with data streams and the propagation of change ([Wikipedia](https://en.wikipedia.org/wiki/Reactive_programming)). RxJS (Reactive Extensions for JavaScript) is a library for reactive programming using observables that makes it easier to compose asynchronous or callback-based code ([RxJS Docs](http://reactivex.io/rxjs/)).
RxJS provides an implementation of the `Observable` type, which is needed until the type becomes part of the language and until browsers support it. The library also provides utility functions for creating and working with observables. These utility functions can be used for:
@ -45,7 +45,7 @@ The `pipe()` function is also a method on the RxJS `Observable`, so you use this
### Common operators
RxJS provides many operators, but only a handful are used frequently. For a list of operators and usage samples, visit the [RxJS API Documentation](https://rxjs.dev/api).
RxJS provides many operators, but only a handful are used frequently. For a list of operators and usage samples, visit the [RxJS API Documentation](https://rxjs-dev.firebaseapp.com/api).
<div class="alert is-helpful">
Note that, for Angular apps, we prefer combining operators with pipes, rather than chaining. Chaining is used in many RxJS examples.

View File

@ -32,7 +32,7 @@ The context also defines a *merge strategy* that determines how changes are merg
When you create a new blank schematic with the [Schematics CLI](#cli), the generated entry function is a *rule factory*.
A `RuleFactory`object defines a higher-order function that creates a `Rule`.
<code-example language="TypeScript">
<code-example language="TypeScript" linenums="false">
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
// You don't have to export the function as default.
@ -49,7 +49,7 @@ You need a rule, for example, to define how a template in the schematic is to be
Rules can make use of utilities provided with the `@schematics/angular` package. Look for helper functions for working with modules, dependencies, TypeScript, AST, JSON, Angular CLI workspaces and projects, and more.
<code-example language="none">
<code-example language="none" linenums="false">
import {
JsonAstObject,
@ -78,7 +78,7 @@ You can see examples of schema files for the Angular CLI command schematics in [
Schematics come with their own command-line tool.
Using Node 6.9 or above, install the Schematics command line tool globally:
<code-example language="bash">
<code-example language="bash" linenums="false">
npm install -g @angular-devkit/schematics-cli
</code-example>
@ -94,7 +94,7 @@ See [Schematics for Libraries](guide/schematics-for-libraries).
The following command creates a new schematic named `hello-world` in a new project folder of the same name.
<code-example language="bash">
<code-example language="bash" linenums="false">
schematics blank --name=hello-world
</code-example>
@ -102,7 +102,7 @@ The `blank` schematic is provided by the Schematics CLI. The command creates a n
Go to the collection folder, install your npm dependencies, and open your new collection in your favorite editor to see the generated files. For example, if you are using VSCode:
<code-example language="bash">
<code-example language="bash" linenums="false">
cd hello-world
npm install
npm run build
@ -118,14 +118,14 @@ Each schematic name must be unique within the collection.
Use the `schematics` command to run a named schematic.
Provide the path to the project folder, the schematic name, and any mandatory options, in the following format.
<code-example language="bash">
<code-example language="bash" linenums="false">
schematics &lt;path-to-schematics-project&gt;:&lt;schematics-name&gt; --&lt;required-option&gt;=&lt;value&gt;
</code-example>
The path can be absolute or relative to the current working directory where the command is executed.
For example, to run the schematic we just generated (which has no required options), use the following command.
<code-example language="bash">
<code-example language="bash" linenums="false">
schematics .:hello-world
</code-example>
@ -133,7 +133,7 @@ schematics .:hello-world
To add a schematic to an existing collection, use the same command you use to start a new schematics project, but run the command inside the project folder.
<code-example language="bash">
<code-example language="bash" linenums="false">
cd hello-world
schematics blank --name=goodbye-world
</code-example>
@ -147,7 +147,7 @@ The top level of the root project folder for a collection contains configuration
The `src/` folder contains subfolders for named schematics in the collection, and a schema, `collection.json`, which describes the collected schematics.
Each schematic is created with a name, description, and factory function.
<code-example language="none">
<code-example language="none" linenums="false">
{
"$schema":
"../node_modules/@angular-devkit/schematics/collection-schema.json",

View File

@ -93,7 +93,7 @@ You can add a named schematic to your collection that lets your users use the `n
We'll assume that your library defines a service, `my-service`, that requires some setup. You want your users to be able to generate it using the following CLI command.
<code-example language="bash">
<code-example language="bash" linenums="false">
ng generate my-lib:my-service
</code-example>
@ -276,7 +276,7 @@ After you build your library and schematics, you can install the schematics coll
From the root of your workspace, run the `ng build` command for your library.
<code-example language="bash">
<code-example language="bash" linenums="false">
ng build my-lib
@ -284,7 +284,7 @@ From the root of your workspace, run the `ng build` command for your library.
Then, you change into your library directory to build the schematic
<code-example language="bash">
<code-example language="bash" linenums="false">
cd projects/my-lib
npm run build
@ -295,7 +295,7 @@ Then, you change into your library directory to build the schematic
Your library and schematics are packaged and placed in the `dist/my-lib` folder at the root of your workspace. For running the schematic, you need to link the library into your `node_modules` folder. From the root of your workspace, run the `npm link` command with the path to your distributable library.
<code-example language="bash">
<code-example language="bash" linenums="false">
npm link dist/my-lib
@ -305,7 +305,7 @@ npm link dist/my-lib
Now that your library is installed, you can run the schematic using the `ng generate` command.
<code-example language="bash">
<code-example language="bash" linenums="false">
ng generate my-lib:my-service --name my-data
@ -313,7 +313,7 @@ ng generate my-lib:my-service --name my-data
In the console, you will see that the schematic was run and the `my-data.service.ts` file was created in your app folder.
<code-example language="bash" hideCopy="true">
<code-example language="bash" linenums="false" hideCopy="true">
CREATE src/app/my-data.service.ts (208 bytes)

View File

@ -17,13 +17,13 @@ Schematics that are included in the `@schematics/angular` collection are run by
The package contains named schematics that configure the options that are available to the CLI for `ng generate` sub-commands, such as `ng generate component` and `ng generate service`.
The subcommands for `ng generate` are shorthand for the corresponding schematic. You can specify a particular schematic (or collection of schematics) to generate, using the long form:
<code-example language="bash">
<code-example language="bash" linenums="false">
ng generate my-schematic-collection:my-schematic-name
</code-example>
&mdash;or&mdash;
<code-example language="bash">
<code-example language="bash" linenums="false">
ng generate my-schematic-name --collection collection-name
</code-example>
@ -72,7 +72,7 @@ The documented sub-commands use the default Angular generation schematics, but y
Angular Material, for example, supplies generation schematics for the UI components that it defines.
The following command uses one of these schematics to render an Angular Material `<mat-table>` that is pre-configured with a datasource for sorting and pagination.
<code-example language="bash">
<code-example language="bash" linenums="false">
ng generate @angular/material:table <component-name>
</code-example>
@ -80,7 +80,7 @@ ng generate @angular/material:table <component-name>
The `ng update` command can be used to update your workspace's library dependencies. If you supply no options or use the help option, the command examines your workspace and suggests libraries to update.
<code-example language="bash">
<code-example language="bash" linenums="false">
ng update
We analyzed your package.json, there are some packages to update:
@ -113,7 +113,7 @@ If you create a new version of your library that introduces potential breaking c
For example, suppose you want to update the Angular Material library.
<code-example language="bash">
<code-example language="bash" linenums="false">
ng update @angular/material
</code-example>

View File

@ -98,7 +98,9 @@ The following template binds the value of `htmlSnippet`, once by interpolating i
content, and once by binding it to the `innerHTML` property of an element:
<code-example path="security/src/app/inner-html-binding.component.html" header="src/app/inner-html-binding.component.html"></code-example>
<code-example path="security/src/app/inner-html-binding.component.html" header="src/app/inner-html-binding.component.html">
</code-example>
@ -110,7 +112,9 @@ a value that an attacker might control into `innerHTML` normally causes an XSS
vulnerability. For example, code contained in a `<script>` tag is executed:
<code-example path="security/src/app/inner-html-binding.component.ts" header="src/app/inner-html-binding.component.ts (class)" region="class"></code-example>
<code-example path="security/src/app/inner-html-binding.component.ts" linenums="false" header="src/app/inner-html-binding.component.ts (class)" region="class">
</code-example>
@ -196,7 +200,9 @@ your intended use of the value. Imagine that the following template needs to bin
`javascript:alert(...)` call:
<code-example path="security/src/app/bypass-security.component.html" header="src/app/bypass-security.component.html (URL)" region="URL"></code-example>
<code-example path="security/src/app/bypass-security.component.html" linenums="false" header="src/app/bypass-security.component.html (URL)" region="URL">
</code-example>
@ -205,7 +211,9 @@ in development mode, logs this action to the console. To prevent
this, mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` call:
<code-example path="security/src/app/bypass-security.component.ts" header="src/app/bypass-security.component.ts (trust-url)" region="trust-url"></code-example>
<code-example path="security/src/app/bypass-security.component.ts" linenums="false" header="src/app/bypass-security.component.ts (trust-url)" region="trust-url">
</code-example>
@ -223,11 +231,15 @@ could execute. So call a method on the controller to construct a trusted video U
Angular to allow binding into `<iframe src>`:
<code-example path="security/src/app/bypass-security.component.html" header="src/app/bypass-security.component.html (iframe)" region="iframe"></code-example>
<code-example path="security/src/app/bypass-security.component.html" linenums="false" header="src/app/bypass-security.component.html (iframe)" region="iframe">
</code-example>
<code-example path="security/src/app/bypass-security.component.ts" header="src/app/bypass-security.component.ts (trust-video-url)" region="trust-video-url"></code-example>
<code-example path="security/src/app/bypass-security.component.ts" linenums="false" header="src/app/bypass-security.component.ts (trust-video-url)" region="trust-video-url">
</code-example>

View File

@ -24,7 +24,7 @@ The `SwUpdate` service supports four separate operations:
The two update events, `available` and `activated`, are `Observable` properties of `SwUpdate`:
<code-example path="service-worker-getting-started/src/app/log-update.service.ts" header="log-update.service.ts" region="sw-update"></code-example>
<code-example path="service-worker-getting-started/src/app/log-update.service.ts" linenums="false" header="log-update.service.ts" region="sw-update"> </code-example>
You can use these events to notify the user of a pending update or to refresh their pages when the code they are running is out of date.
@ -35,7 +35,7 @@ It's possible to ask the service worker to check if any updates have been deploy
Do this with the `checkForUpdate()` method:
<code-example path="service-worker-getting-started/src/app/check-for-update.service.ts" header="check-for-update.service.ts"></code-example>
<code-example path="service-worker-getting-started/src/app/check-for-update.service.ts" linenums="false" header="check-for-update.service.ts"> </code-example>
This method returns a `Promise` which indicates that the update check has completed successfully, though it does not indicate whether an update was discovered as a result of the check. Even if one is found, the service worker must still successfully download the changed files, which can fail. If successful, the `available` event will indicate availability of a new version of the app.
@ -51,7 +51,7 @@ You can avoid that by waiting for the app to stabilize first, before starting to
(as shown in the example above).
Note that this is true for any kind of polling done by your application.
Check the {@link ApplicationRef#isStable isStable} documentation for more information.
Check the {@link ApplicationRef#isStable isStable} documentation for more information.
</div>
@ -59,7 +59,7 @@ Check the {@link ApplicationRef#isStable isStable} documentation for more inform
If the current tab needs to be updated to the latest app version immediately, it can ask to do so with the `activateUpdate()` method:
<code-example path="service-worker-getting-started/src/app/prompt-update.service.ts" header="prompt-update.service.ts" region="sw-activate"></code-example>
<code-example path="service-worker-getting-started/src/app/prompt-update.service.ts" linenums="false" header="prompt-update.service.ts" region="sw-activate"> </code-example>
Doing this could break lazy-loading into currently running apps, especially if the lazy-loaded chunks use filenames with hashes, which change every version.

View File

@ -43,7 +43,7 @@ for getting and setting the current HTML document title:
You can inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it:
<code-example path="set-document-title/src/app/app.component.ts" region="class" header="src/app/app.component.ts (class)"></code-example>
<code-example path="set-document-title/src/app/app.component.ts" region="class" header="src/app/app.component.ts (class)" linenums="false"></code-example>
Bind that method to three anchor tags and voilà!

View File

@ -1,5 +1,18 @@
# Sharing Modules
#### Prerequisites
A basic understanding of the following:
* [Feature Modules](guide/feature-modules).
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
* [Frequently Used Modules](guide/frequent-ngmodules).
* [Routing and Navigation](guide/router).
* [Lazy loading modules](guide/lazy-loading-ngmodules).
<!--* Components (#TBD) We dont have a page just on the concept of components, but I think one would be helpful for beginners.-->
<hr>
Creating shared modules allows you to organize and streamline your code. You can put commonly
used directives, pipes, and components into one module and then import just that module wherever
you need it in other parts of your app.
@ -41,7 +54,7 @@ to import `FormsModule`, `SharedModule` can still export
way, you can give other modules access to `FormsModule` without
having to import it directly into the `@NgModule` decorator.
### Using components vs services from other modules
### Using components vs services from other modules.
There is an important distinction between using another module's component and
using a service from another module. Import modules when you want to use

View File

@ -1,10 +1,15 @@
# Singleton services
A singleton service is a service for which only once instance exists in an app.
#### Prerequisites:
* A basic understanding of [Bootstrapping](guide/bootstrapping).
* Familiarity with [Providers](guide/providers).
For a sample app using the app-wide singleton service that this page describes, see the
<live-example name="ngmodules"></live-example> showcasing all the documented features of NgModules.
<hr />
## Providing a singleton service
There are two ways to make a service a singleton in Angular:
@ -20,7 +25,7 @@ There are two ways to make a service a singleton in Angular:
Beginning with Angular 6.0, the preferred way to create a singleton service is to set `providedIn` to `root` on the service's `@Injectable()` decorator. This tells Angular
to provide the service in the application root.
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts"></code-example>
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts" linenums="false"> </code-example>
For more detailed information on services, see the [Services](tutorial/toh-pt4) chapter of the
[Tour of Heroes tutorial](tutorial).
@ -70,7 +75,7 @@ with `providers` and child modules without `providers`.
1. Create a static method `forRoot()` on the module.
2. Place the providers into the `forRoot()` method.
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="for-root" header="src/app/greeting/greeting.module.ts"></code-example>
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="for-root" header="src/app/greeting/greeting.module.ts" linenums="false"> </code-example>
{@a forRoot-router}
@ -117,16 +122,22 @@ the greeting `UserService`.
In the following example, the optional, injected `UserServiceConfig`
extends the greeting `UserService`. If a `UserServiceConfig` exists, the `UserService` sets the user name from that config.
<code-example path="ngmodules/src/app/greeting/user.service.ts" region="ctor" header="src/app/greeting/user.service.ts (constructor)"></code-example>
<code-example path="ngmodules/src/app/greeting/user.service.ts" region="ctor" header="src/app/greeting/user.service.ts (constructor)" linenums="false">
</code-example>
Here's `forRoot()` that takes a `UserServiceConfig` object:
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="for-root" header="src/app/greeting/greeting.module.ts (forRoot)"></code-example>
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="for-root" header="src/app/greeting/greeting.module.ts (forRoot)" linenums="false">
</code-example>
Lastly, call it within the `imports` list of the `AppModule`. In the following
snippet, other parts of the file are left out. For the complete file, see the <live-example name="ngmodules"></live-example>, or continue to the next section of this document.
<code-example path="ngmodules/src/app/app.module.ts" region="import-for-root" header="src/app/app.module.ts (imports)"></code-example>
<code-example path="ngmodules/src/app/app.module.ts" region="import-for-root" header="src/app/app.module.ts (imports)" linenums="false">
</code-example>
The app displays "Miss Marple" as the user instead of the default "Sherlock Holmes".
@ -140,7 +151,9 @@ lazy-loaded module imports it too, the app can generate
To guard against a lazy loaded module re-importing `GreetingModule`, add the following `GreetingModule` constructor.
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts"></code-example>
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts" linenums="false">
</code-example>
The constructor tells Angular to inject the `GreetingModule` into itself.
The injection would be circular if Angular looked for
@ -164,7 +177,7 @@ Now `parentModule` exists and the constructor throws the error.
Here are the two files in their entirety for reference:
<code-tabs>
<code-tabs linenums="false">
<code-pane header="app.module.ts" path="ngmodules/src/app/app.module.ts">
</code-pane>
<code-pane header="greeting.module.ts" region="whole-greeting-module" path="ngmodules/src/app/greeting/greeting.module.ts">

View File

@ -6,21 +6,21 @@ In version 9, the default setting for `@ViewChild` and `@ContentChild` queries i
In preparation for this change, in version 8, we are migrating all applications and libraries to explicitly specify the resolution strategy for `@ViewChild` and `@ContentChild` queries.
Specifically, this migration adds an explicit "static" flag that dictates when that query's results should be assigned.
Adding this flag will ensure your code works the same way when upgrading to version 9.
Specifically, this migration adds an explicit "static" flag that dictates when that query's results should be assigned.
Adding this flag will ensure your code works the same way when upgrading to version 9.
Before:
```
// query results sometimes available in `ngOnInit`, sometimes in `ngAfterViewInit` (based on template)
@ViewChild('foo') foo: ElementRef;
@ViewChild('foo') foo: ElementRef;
```
After:
```
// query results available in ngOnInit
@ViewChild('foo', {static: true}) foo: ElementRef;
@ViewChild('foo', {static: true}) foo: ElementRef;
OR
@ -28,7 +28,7 @@ OR
@ViewChild('foo', {static: false}) foo: ElementRef;
```
Starting with version 9, the `static` flag will default to false.
Starting with version 9, the `static` flag will default to false.
At that time, any `{static: false}` flags can be safely removed, and we will have a schematic that will update your code for you.
Note: this flag only applies to `@ViewChild` and `@ContentChild` queries specifically, as `@ViewChildren` and `@ContentChildren` queries do not have a concept of static and dynamic (they are always resolved as if they are "dynamic").
@ -38,50 +38,50 @@ Note: this flag only applies to `@ViewChild` and `@ContentChild` queries specifi
{@a what-to-do-with-todo}
### What should I do if I see a `/* TODO: add static flag */` comment printed by the schematic?
If you see this comment, it means that the schematic couldn't statically figure out the correct flag. In this case, you'll have to add the correct flag based on your application's behavior.
If you see this comment, it means that the schematic couldn't statically figure out the correct flag. In this case, you'll have to add the correct flag based on your application's behavior.
For more information on how to choose, see the [next question](#how-do-i-choose).
{@a how-do-i-choose}
### How do I choose which `static` flag value to use: `true` or `false`?
In the official API docs, we have always recommended retrieving query results in [`ngAfterViewInit` for view queries](https://angular.io/api/core/ViewChild#description) and [`ngAfterContentInit` for content queries](https://angular.io/api/core/ContentChild#description).
This is because by the time those lifecycle hooks run, change detection has completed for the relevant nodes and we can guarantee that we have collected all the possible query results.
In the official API docs, we have always recommended retrieving query results in [`ngAfterViewInit` for view queries](https://angular.io/api/core/ViewChild#description) and [`ngAfterContentInit` for content queries](https://angular.io/api/core/ContentChild#description).
This is because by the time those lifecycle hooks run, change detection has completed for the relevant nodes and we can guarantee that we have collected all the possible query results.
Most applications will want to use `{static: false}` for the same reason. This setting will ensure query matches that are dependent on binding resolution (e.g. results inside `*ngIf`s or `*ngFor`s) will be found by the query.
Most applications will want to use `{static: false}` for the same reason. This setting will ensure query matches that are dependent on binding resolution (e.g. results inside `*ngIf`s or `*ngFor`s) will be found by the query.
There are rarer cases where `{static: true}` flag might be necessary (see [answer here](#should-i-use-static-true)).
{@a should-i-use-static-true}
### Is there a case where I should use `{static: true}`?
This option was introduced to support creating embedded views on the fly.
If you need access to a `TemplateRef` in a query to create a view dynamically, you won't be able to do so in `ngAfterViewInit`.
Change detection has already run on that view, so creating a new view with the template will cause an `ExpressionHasChangedAfterChecked` error to be thrown.
In this case, you will want to set the `static` flag to `true` and create your view in `ngOnInit`.
This option was introduced to support creating embedded views on the fly.
If you need access to a `TemplateRef` in a query to create a view dynamically, you won't be able to do so in `ngAfterViewInit`.
Change detection has already run on that view, so creating a new view with the template will cause an `ExpressionHasChangedAfterChecked` error to be thrown.
In this case, you will want to set the `static` flag to `true` and create your view in `ngOnInit`.
In most other cases, the best practice is to use `{static: false}`.
However, to facilitate the migration to version 8, you may also want to set the `static` flag to `true` if your component code already depends on the query results being available some time **before** `ngAfterViewInit` (for view queries) or `ngAfterContentInit` (for content queries).
For example, if your component relies on the query results being populated in the `ngOnInit` hook or in `@Input` setters, you will need to either set the flag to `true` or re-work your component to adjust to later timing.
For example, if your component relies on the query results being populated in the `ngOnInit` hook or in `@Input` setters, you will need to either set the flag to `true` or re-work your component to adjust to later timing.
Note: Selecting the static option means that query results nested in `*ngIf` or `*ngFor` will not be found by the query.
These results are only retrievable after change detection runs.
Note: Selecting the static option means that query results nested in `*ngIf` or `*ngFor` will not be found by the query.
These results are only retrievable after change detection runs.
{@a what-does-this-flag-mean}
### What does this flag mean and why is it necessary?
The default behavior for queries has historically been undocumented and confusing, and has also commonly led to issues that are difficult to debug.
In version 9, we would like to make query behavior more consistent and simple to understand.
The default behavior for queries has historically been undocumented and confusing, and has also commonly led to issues that are difficult to debug.
In version 9, we would like to make query behavior more consistent and simple to understand.
To explain why, first it's important to understand how queries have worked up until now.
Without the `static` flag, the compiler decided when each query would be resolved on a case-by-case basis.
All `@ViewChild`/`@ContentChild` queries were categorized into one of two buckets at compile time: "static" or "dynamic".
Without the `static` flag, the compiler decided when each query would be resolved on a case-by-case basis.
All `@ViewChild`/`@ContentChild` queries were categorized into one of two buckets at compile time: "static" or "dynamic".
This classification determined when query results would become available to users.
- **Static queries** were queries where the result could be determined statically because the result didn't depend on runtime values like bindings.
- **Static queries** were queries where the result could be determined statically because the result didn't depend on runtime values like bindings.
Results from queries classified as static were available before change detection ran for that view (accessible in `ngOnInit`).
- **Dynamic queries** were queries where the result could NOT be determined statically because the result depended on runtime values (aka bindings).
- **Dynamic queries** were queries where the result could NOT be determined statically because the result depended on runtime values (aka bindings).
Results from queries classified as dynamic were not available until after change detection ran for that view (accessible in `ngAfterContentInit` for content queries or `ngAfterViewInit` for view queries).
For example, let's say we have a component, `Comp`. Inside it, we have this query:
@ -96,8 +96,8 @@ and this template:
<div foo></div>
```
This `Foo` query would be categorized as static because at compile-time it's known that the `Foo` instance on the `<div>` is the correct result for the query.
Because the query result is not dependent on runtime values, we don't have to wait for change detection to run on the template before resolving the query.
This `Foo` query would be categorized as static because at compile-time it's known that the `Foo` instance on the `<div>` is the correct result for the query.
Because the query result is not dependent on runtime values, we don't have to wait for change detection to run on the template before resolving the query.
Consequently, results can be made available in `ngOnInit`.
Let's say the query is the same, but the component template looks like this:
@ -106,62 +106,53 @@ Let's say the query is the same, but the component template looks like this:
<div foo *ngIf="showing"></div>
```
With that template, the query would be categorized as a dynamic query.
We would need to know the runtime value of `showing` before determining what the correct results are for the query.
With that template, the query would be categorized as a dynamic query.
We would need to know the runtime value of `showing` before determining what the correct results are for the query.
As a result, change detection must run first, and results can only be made available in `ngAfterViewInit` or a setter for the query property.
The effect of this implementation is that adding an `*ngIf` or `*ngFor` anywhere above a query match can change when that query's results become available.
The effect of this implementation is that adding an `*ngIf` or `*ngFor` anywhere above a query match can change when that query's results become available.
Keep in mind that these categories only applied to `@ViewChild` and `@ContentChild` queries specifically.
Keep in mind that these categories only applied to `@ViewChild` and `@ContentChild` queries specifically.
`@ViewChildren` and `@ContentChildren` queries did not have a concept of static and dynamic, so they were always resolved as if they were "dynamic".
This strategy of resolving queries at different times based on the location of potential query matches has caused a lot of confusion. Namely:
This strategy of resolving queries at different times based on the location of potential query matches has caused a lot of confusion. Namely:
* Sometimes query results are available in `ngOnInit`, but sometimes they aren't and it's not clear why (see [21800](https://github.com/angular/angular/issues/21800) or [19872](https://github.com/angular/angular/issues/19872)).
* `@ViewChild` queries are resolved at a different time from `@ViewChildren` queries, and `@ContentChild` queries are resolved at a different time from `@ContentChildren` queries.
* `@ViewChild` queries are resolved at a different time from `@ViewChildren` queries, and `@ContentChild` queries are resolved at a different time from `@ContentChildren` queries.
If a user turns a `@ViewChild` query into a `@ViewChildren` query, their code can break suddenly because the timing has shifted.
* Code depending on a query result can suddenly stop working as soon as an `*ngIf` or an `*ngFor` is added to a template.
* A `@ContentChild` query for the same component will resolve at different times in the lifecycle for each usage of the component.
* A `@ContentChild` query for the same component will resolve at different times in the lifecycle for each usage of the component.
This leads to buggy behavior where using a component with `*ngIf` is broken in subtle ways that aren't obvious to the component author.
In version 9, we plan to simplify the behavior so all queries resolve after change detection runs by default.
The location of query matches in the template cannot affect when the query result will become available and suddenly break your code, and the default behavior is always the same.
This makes the logic more consistent and predictable for users.
In version 9, we plan to simplify the behavior so all queries resolve after change detection runs by default.
The location of query matches in the template cannot affect when the query result will become available and suddenly break your code, and the default behavior is always the same.
This makes the logic more consistent and predictable for users.
That said, if an application does need query results earlier (for example, the query result is needed to create an embedded view), it's possible to add the `{static: true}` flag to explicitly ask for static resolution.
That said, if an application does need query results earlier (for example, the query result is needed to create an embedded view), it's possible to add the `{static: true}` flag to explicitly ask for static resolution.
With this flag, users can indicate that they only care about results that are statically available and the query results will be populated before `ngOnInit`.
{@a view-children-and-content-children}
### Does this change affect `@ViewChildren` or `@ContentChildren` queries?
No, this change only affects `@ViewChild` and `@ContentChild` queries specifically.
No, this change only affects `@ViewChild` and `@ContentChild` queries specifically.
`@ViewChildren` and `@ContentChildren` queries are already "dynamic" by default and don't support static resolution.
{@a why-specify-static-false}
### Why do I have to specify `{static: false}`? Isn't that the default?
The goal of this migration is to transition apps that aren't yet on version 9 to a query pattern that is compatible with version 9.
However, most applications use libraries, and it's likely that some of these libraries may not be upgraded to version 8 yet (and thus might not have the proper flags).
Since the application's version of Angular will be used for compilation, if we change the default, the behavior of queries in the library's components will change to the version 8 default and possibly break.
This way, an application's dependencies will behave the same way during the transition as they did in the previous version.
The goal of this migration is to transition apps that aren't yet on version 9 to a query pattern that is compatible with version 9.
However, most applications use libraries, and it's likely that some of these libraries may not be upgraded to version 8 yet (and thus might not have the proper flags).
Since the application's version of Angular will be used for compilation, if we change the default, the behavior of queries in the library's components will change to the version 8 default and possibly break.
This way, an application's dependencies will behave the same way during the transition as they did in the previous version.
In Angular version 9 and later, it will be safe to remove any `{static: false}` flags and we will do this cleanup for you in a schematic.
{@a libraries}
### Can I keep on using Angular libraries that havent yet updated to version 8 yet?
Yes, absolutely!
Yes, absolutely!
Because we have not changed the default query behavior in version 8 (i.e. the compiler still chooses a timing if no flag is set), when your application runs with a library that has not updated to version 8, the library will run the same way it did in version 7.
This guarantees your app will work in version 8 even if libraries take longer to update their code.
{@a update-library-to-use-static-flag}
### Can I update my library to version 8 by adding the `static` flag to view queries, while still being compatible with Angular version 7 apps?
Yes, the Angular team's recommendation for libraries is to update to version 8 and add the `static` flag. Angular version 7 apps will continue to work with libraries that have this flag.
However, if you update your library to Angular version 8 and want to take advantage of the new version 8 APIs, or you want more recent dependencies (such as Typescript or RxJS) your library will become incompatible with Angular version 7 apps. If your goal is to make your library compatible with Angular versions 7 and 8, you should not update your lib at all—except for `peerDependencies` in `package.json`.
In general, the most efficient plan is for libraries to adopt a 6 month major version schedule and bump the major version after each Angular update. That way, libraries stay in the same release cadence as Angular.
This guarantees your app will work in version 8 even if libraries take longer to update their code.

View File

@ -31,7 +31,9 @@ Structural directives are easy to recognize.
An asterisk (*) precedes the directive attribute name as in this example.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif)" region="ngif"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngif)" region="ngif">
</code-example>
@ -50,7 +52,9 @@ described in the [_Template Syntax_](guide/template-syntax) guide and seen in sa
Here's an example of them in a template:
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (built-in)" region="built-in"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (built-in)" region="built-in">
</code-example>
@ -117,7 +121,9 @@ You can [only apply one](guide/structural-directives#one-per-element) _structura
It takes a boolean expression and makes an entire chunk of the DOM appear or disappear.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-true)" region="ngif-true"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngif-true)" region="ngif-true">
</code-example>
@ -144,7 +150,9 @@ The component and DOM nodes can be garbage-collected and free up memory.
A directive could hide the unwanted paragraph instead by setting its `display` style to `none`.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (display-none)" region="display-none"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (display-none)" region="display-none">
</code-example>
@ -193,7 +201,9 @@ and wondered why it is necessary and what it does.
Here is `*ngIf` displaying the hero's name if `hero` exists.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (asterisk)" region="asterisk"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (asterisk)" region="asterisk">
</code-example>
@ -201,7 +211,9 @@ The asterisk is "syntactic sugar" for something a bit more complicated.
Internally, Angular translates the `*ngIf` _attribute_ into a `<ng-template>` _element_, wrapped around the host element, like this.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-template)" region="ngif-template"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngif-template)" region="ngif-template">
</code-example>
@ -234,7 +246,9 @@ Angular transforms the `*ngFor` in similar fashion from asterisk (*) syntax to `
Here's a full-featured application of `NgFor`, written both ways:
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (inside-ngfor)" region="inside-ngfor"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (inside-ngfor)" region="inside-ngfor">
</code-example>
@ -261,179 +275,53 @@ In this example, the `[ngClass]="odd"` stays on the `<div>`.
{@a microsyntax}
## Microsyntax
### Microsyntax
The Angular microsyntax lets you configure a directive in a compact, friendly string.
The microsyntax parser translates that string into attributes on the `<ng-template>`:
* The `let` keyword declares a [_template input variable_](guide/structural-directives#template-input-variable)
that you reference within the template. The input variables in this example are `hero`, `i`, and `odd`.
The parser translates `let hero`, `let i`, and `let odd` into variables named
The parser translates `let hero`, `let i`, and `let odd` into variables named,
`let-hero`, `let-i`, and `let-odd`.
* The microsyntax parser title-cases all directives and prefixes them with the directive's
attribute name, such as `ngFor`. For example, the `ngFor` input properties,
`of` and `trackBy`, become `ngForOf` and `ngForTrackBy`, respectively.
* The microsyntax parser takes `of` and `trackBy`, title-cases them (`of` -> `Of`, `trackBy` -> `TrackBy`),
and prefixes them with the directive's attribute name (`ngFor`), yielding the names `ngForOf` and `ngForTrackBy`.
Those are the names of two `NgFor` _input properties_ .
That's how the directive learns that the list is `heroes` and the track-by function is `trackById`.
* As the `NgFor` directive loops through the list, it sets and resets properties of its own _context_ object.
These properties can include, but aren't limited to, `index`, `odd`, and a special property
named `$implicit`.
These properties include `index` and `odd` and a special property named `$implicit`.
* The `let-i` and `let-odd` variables were defined as `let i=index` and `let odd=odd`.
Angular sets them to the current value of the context's `index` and `odd` properties.
* The context property for `let-hero` wasn't specified.
Its intended source is implicit.
Angular sets `let-hero` to the value of the context's `$implicit` property,
Angular sets `let-hero` to the value of the context's `$implicit` property
which `NgFor` has initialized with the hero for the current iteration.
* The [`NgFor` API guide](api/common/NgForOf "API: NgFor")
* The [API guide](api/common/NgForOf "API: NgFor")
describes additional `NgFor` directive properties and context properties.
* The `NgForOf` directive implements `NgFor`. Read more about additional `NgForOf` directive properties and context properties in the [NgForOf API reference](api/common/NgForOf).
### Writing your own structural directives
These microsyntax mechanisms are also available to you when you write your own structural directives.
For example, microsyntax in Angular allows you to write `<div *ngFor="let item of items">{{item}}</div>`
instead of `<ng-template ngFor [ngForOf]="items"><div>{{item}}</div></ng-template`.
The following sections provide detailed information on constraints, grammar,
and translation of microsyntax.
### Constraints
Microsyntax must meet the following requirements:
- It must be known ahead of time so that IDEs can parse it without knowing the underlying semantics of the directive or what directives are present.
- It must translate to key-value attributes in the DOM.
### Grammar
When you write your own structural directives, use the following grammar:
```
*:prefix="( :let | :expression ) (';' | ',')? ( :let | :as | :keyExp )*"
```
The following tables describe each portion of the microsyntax grammar.
<!-- What should I put in the table headers? -->
<table>
<tr>
<th></th>
<th></th>
</tr>
<tr>
<td><code>prefix</code></td>
<td>HTML attribute key</td>
</tr>
<tr>
<td><code>key</code></td>
<td>HTML attribute key</td>
</tr>
<tr>
<td><code>local</code></td>
<td>local variable name used in the template</td>
</tr>
<tr>
<td><code>export</code></td>
<td>value exported by the directive under a given name</td>
</tr>
<tr>
<td><code>expression</code></td>
<td>standard Angular expression</td>
</tr>
</table>
<!-- The items in this table seem different. Is there another name for how we should describe them? -->
<table>
<tr>
<th></th>
</tr>
<tr>
<td colspan="3"><code>keyExp = :key ":"? :expression ("as" :local)? ";"? </code></td>
</tr>
<tr>
<td colspan="3"><code>let = "let" :local "=" :export ";"?</code></td>
</tr>
<tr>
<td colspan="3"><code>as = :export "as" :local ";"?</code></td>
</tr>
</table>
* `NgFor` is implemented by the `NgForOf` directive. Read more about additional `NgForOf` directive properties and context properties [NgForOf API reference](api/common/NgForOf).
### Translation
A microsyntax is translated to the normal binding syntax as follows:
<!-- What to put in the table headers below? Are these correct?-->
<table>
<tr>
<th>Microsyntax</th>
<th>Translation</th>
</tr>
<tr>
<td><code>prefix</code> and naked <code>expression</code></td>
<td><code>[prefix]="expression"</code></td>
</tr>
<tr>
<td><code>keyExp</code></td>
<td><code>[prefixKey] "expression"
(let-prefixKey="export")</code>
<br />
Notice that the <code>prefix</code>
is added to the <code>key</code>
</td>
</tr>
<tr>
<td><code>let</code></td>
<td><code>let-local="export"</code></td>
</tr>
</table>
### Microsyntax examples
The following table demonstrates how Angular desugars microsyntax.
<table>
<tr>
<th>Microsyntax</th>
<th>Desugared</th>
</tr>
<tr>
<td><code>*ngFor="let item of [1,2,3]"</code></td>
<td><code>&lt;ng-template ngFor let-item [ngForOf]="[1,2,3]"&gt;</code></td>
</tr>
<tr>
<td><code>*ngFor="let item of [1,2,3] as items; trackBy: myTrack; index as i"</code></td>
<td><code>&lt;ng-template ngFor let-item [ngForOf]="[1,2,3]" let-items="ngForOf" [ngForTrackBy]="myTrack" let-i="index"&gt;</code>
</td>
</tr>
<tr>
<td><code>*ngIf="exp"</code></td>
<td><code>&lt;ng-template [ngIf]="exp"&gt;</code></td>
</tr>
<tr>
<td><code>*ngIf="exp as value"</code></td>
<td><code>&lt;ng-template [ngIf]="exp" let-value="ngIf"&gt;</code></td>
</tr>
</table>
These microsyntax mechanisms are available to you when you write your own structural directives.
Studying the
[source code for `NgIf`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_if.ts "Source: NgIf")
and [`NgForOf`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts "Source: NgForOf")
is a great way to learn more.
{@a template-input-variable}
{@a template-input-variables}
## Template input variable
### Template input variable
A _template input variable_ is a variable whose value you can reference _within_ a single instance of the template.
There are several such variables in this example: `hero`, `i`, and `odd`.
@ -458,7 +346,7 @@ variable as the `hero` declared as `#hero`.
{@a one-per-element}
## One structural directive per host element
### One structural directive per host element
Someday you'll want to repeat a block of HTML but only when a particular condition is true.
You'll _try_ to put both an `*ngFor` and an `*ngIf` on the same host element.
@ -485,7 +373,9 @@ The Angular _NgSwitch_ is actually a set of cooperating directives: `NgSwitch`,
Here's an example.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngswitch)" region="ngswitch"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngswitch)" region="ngswitch">
</code-example>
@ -519,7 +409,9 @@ As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
can be desugared into the `<ng-template>` element form.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngswitch-template)" region="ngswitch-template"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngswitch-template)" region="ngswitch-template">
</code-example>
@ -552,7 +444,9 @@ those elements disappear.
That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!".
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (template-tag)" region="template-tag"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (template-tag)" region="template-tag">
</code-example>
@ -582,7 +476,9 @@ There's often a _root_ element that can and should host the structural directive
The list element (`<li>`) is a typical host element of an `NgFor` repeater.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngfor-li)" region="ngfor-li"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngfor-li)" region="ngfor-li">
</code-example>
@ -590,7 +486,9 @@ When there isn't a host element, you can usually wrap the content in a native HT
such as a `<div>`, and attach the directive to that wrapper.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif)" region="ngif"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngif)" region="ngif">
</code-example>
@ -603,14 +501,18 @@ neither expect nor accommodate the new layout.
For example, suppose you have the following paragraph layout.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-span)" region="ngif-span"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngif-span)" region="ngif-span">
</code-example>
You also have a CSS style rule that happens to apply to a `<span>` within a `<p>`aragraph.
<code-example path="structural-directives/src/app/app.component.css" header="src/app/app.component.css (p-span)" region="p-span"></code-example>
<code-example path="structural-directives/src/app/app.component.css" linenums="false" header="src/app/app.component.css (p-span)" region="p-span">
</code-example>
@ -632,7 +534,9 @@ You can't wrap the _options_ in a conditional `<div>` or a `<span>`.
When you try this,
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (select-span)" region="select-span"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (select-span)" region="select-span">
</code-example>
@ -655,7 +559,9 @@ because Angular _doesn't put it in the DOM_.
Here's the conditional paragraph again, this time using `<ng-container>`.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer">
</code-example>
@ -671,7 +577,9 @@ It renders properly.
Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer">
</code-example>
@ -722,7 +630,9 @@ that does the opposite of `NgIf`.
`UnlessDirective` displays the content when the condition is ***false***.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (appUnless-1)" region="appUnless-1"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (appUnless-1)" region="appUnless-1">
</code-example>
@ -739,7 +649,9 @@ Creating a directive is similar to creating a component.
Here's how you might begin:
<code-example path="structural-directives/src/app/unless.directive.ts" header="src/app/unless.directive.ts (skeleton)" region="skeleton"></code-example>
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" header="src/app/unless.directive.ts (skeleton)" region="skeleton">
</code-example>
@ -772,7 +684,9 @@ and access the _view container_ through a
You inject both in the directive constructor as private variables of the class.
<code-example path="structural-directives/src/app/unless.directive.ts" header="src/app/unless.directive.ts (ctor)" region="ctor"></code-example>
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" header="src/app/unless.directive.ts (ctor)" region="ctor">
</code-example>
@ -793,7 +707,9 @@ Read about `@Input` in the [_Template Syntax_](guide/template-syntax#inputs-outp
<code-example path="structural-directives/src/app/unless.directive.ts" header="src/app/unless.directive.ts (set)" region="set"></code-example>
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" header="src/app/unless.directive.ts (set)" region="set">
</code-example>
@ -811,7 +727,9 @@ Nobody reads the `appUnless` property so it doesn't need a getter.
The completed directive code looks like this:
<code-example path="structural-directives/src/app/unless.directive.ts" header="src/app/unless.directive.ts (excerpt)" region="no-docs"></code-example>
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" header="src/app/unless.directive.ts (excerpt)" region="no-docs">
</code-example>
@ -820,7 +738,9 @@ Add this directive to the `declarations` array of the AppModule.
Then create some HTML to try it.
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (appUnless)" region="appUnless"></code-example>
<code-example path="structural-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (appUnless)" region="appUnless">
</code-example>

View File

@ -927,7 +927,7 @@ As always, strive for consistency.
<div class="s-rule do">
**Do** use a custom prefix for a component selector.
For example, the prefix `toh` represents **T**our **o**f **H**eroes and the prefix `admin` represents an admin feature area.
For example, the prefix `toh` represents from **T**our **o**f **H**eroes and the prefix `admin` represents an admin feature area.
</div>
@ -1080,10 +1080,6 @@ For example, the prefix `toh` represents **T**our **o**f **H**eroes and the pref
**Do** use consistent names for all pipes, named after their feature.
The pipe class name should use [UpperCamelCase](guide/glossary#case-types)
(the general convention for class names),
and the corresponding `name` string should use *lowerCamelCase*.
The `name` string cannot use hyphens ("dash-case" or "kebab-case").
</div>
@ -1674,7 +1670,7 @@ keep the **F**lattest structure you can, and
**Why?** LIFT provides a consistent structure that scales well, is modular, and makes it easier to increase developer efficiency by finding code quickly.
**Why?** LIFT Provides a consistent structure that scales well, is modular, and makes it easier to increase developer efficiency by finding code quickly.
To confirm your intuition about a particular structure, ask:
_can I quickly open and start work in all of the related files for this feature_?
@ -1694,7 +1690,7 @@ _can I quickly open and start work in all of the related files for this feature_
**Do** make locating code intuitive, simple, and fast.
**Do** make locating code intuitive, simple and fast.
</div>
@ -3674,7 +3670,7 @@ Compare with the less preferred `host` metadata alternative.
</div>
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" header="src/app/treeshaking/service.ts"></code-example>
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" header="src/app/treeshaking/service.ts" linenums="false"> </code-example>

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