Compare commits
258 Commits
Author | SHA1 | Date | |
---|---|---|---|
b667bd2224 | |||
03e8a31bf5 | |||
800e90e4ed | |||
7290053952 | |||
14890e9117 | |||
06c49b4a6b | |||
d269b111dd | |||
46b160e925 | |||
dcbc28f729 | |||
9bdffb1e5c | |||
155f40c175 | |||
8c446b05d0 | |||
2647f708b7 | |||
fa15814d75 | |||
7a62530ed1 | |||
6ece7db37a | |||
3a2b195a58 | |||
489cef6ea2 | |||
985513351b | |||
8f3dd85600 | |||
6b67cd5620 | |||
0cd4c019cf | |||
7e46a6d99d | |||
85d051f8d5 | |||
c7542a1d09 | |||
2172368eae | |||
2c402d5c99 | |||
35a025fbca | |||
a86850e3f2 | |||
1bcd58cee8 | |||
dd0be7feb7 | |||
3d7303efc0 | |||
716af1059c | |||
a182714703 | |||
17b32b5fd4 | |||
29e1c53a31 | |||
87ce4e997b | |||
2bb9a65351 | |||
6b51ed29ef | |||
b696413a79 | |||
b6aeaceb8e | |||
5ae9b76a9b | |||
82055b2fb8 | |||
44039a4b16 | |||
a445826dad | |||
7f7033bbd7 | |||
27997a16c0 | |||
59c3700c8c | |||
4d93d2406f | |||
d39a2beae1 | |||
c038992fae | |||
8a470b9af9 | |||
399935c32b | |||
97ab52c618 | |||
f69e4e6f77 | |||
9f2ae5d6ff | |||
9eefe25e2f | |||
221782a8a1 | |||
24ca582bc5 | |||
b31a292955 | |||
1d7aa0a92c | |||
0aff4a6919 | |||
f50dede8f7 | |||
0e68c7edf9 | |||
54ef63b0f4 | |||
215ef3c5f4 | |||
5a8eb924ba | |||
c845a7b887 | |||
8b26447c4f | |||
ce196105ce | |||
b9a94c6d02 | |||
ebc71f7caa | |||
f65db20c6d | |||
8f084d7214 | |||
cb848b9410 | |||
d52ae7cbab | |||
9c954ebc62 | |||
f14693b9a4 | |||
f10d6c66c9 | |||
4495a46b99 | |||
ef75fb8ecd | |||
7b6ee5e0d9 | |||
e822394075 | |||
1b6e8411bd | |||
7151eae36d | |||
376ad9c3cd | |||
4aecf9253b | |||
60f58bf051 | |||
10a1e1974b | |||
647d7bdd88 | |||
69a612d402 | |||
221cbd0b47 | |||
ce9d0de840 | |||
18f0c2f1d4 | |||
5bd12c5aa1 | |||
0139b11227 | |||
e061e638cb | |||
dda781ecce | |||
a27c5dd740 | |||
012b535147 | |||
2200884e55 | |||
78e7fdd98d | |||
1e9eeafa9e | |||
9ef9bfe76b | |||
a5f9a86520 | |||
dd664f694c | |||
b66d82e308 | |||
518bca0841 | |||
d7ca263cc4 | |||
e8ae3c5f2e | |||
85a7fe8702 | |||
32c07ceca0 | |||
d3744457ab | |||
fc61284dbe | |||
12fd06916b | |||
9e83822679 | |||
54794f9b31 | |||
897bd18fbc | |||
1ac07757dd | |||
0110de2662 | |||
f166b6d8f6 | |||
09576e9683 | |||
40d785f0a0 | |||
46c03bd866 | |||
d5d8657d30 | |||
8853f13f82 | |||
3166cffd28 | |||
40705f3366 | |||
19bc11139d | |||
31ea254a07 | |||
caf8c0a437 | |||
d6c80871f5 | |||
edc51f76c4 | |||
c529be9f24 | |||
b65e11e3c3 | |||
3a09d01c63 | |||
604d9063c5 | |||
c4c340a7c4 | |||
32aa18be78 | |||
80fa84c177 | |||
e44ba0ffa9 | |||
19a28e599b | |||
14ad7562c6 | |||
0c61a35ea3 | |||
fac20bd8d1 | |||
63e458dd3a | |||
d545bbeee4 | |||
4bb283cbb2 | |||
a08b4b3519 | |||
0e86551a63 | |||
5c738417db | |||
3d9ba19ff8 | |||
8b7a4d7550 | |||
76b755e292 | |||
565a58e261 | |||
7014b67e51 | |||
6f50aad5c4 | |||
ef44f51d58 | |||
9204de96a1 | |||
7c62a8f9ca | |||
a55c6df07b | |||
d33204956f | |||
66d6b53fb1 | |||
e85fa5d4ff | |||
0c00c94f34 | |||
9b29ca95a2 | |||
479d926b4b | |||
b9195289a5 | |||
75b6b0e1ba | |||
2d4f507b61 | |||
3246a8553c | |||
db557221bc | |||
7ea6073534 | |||
1cba5d42d1 | |||
103a5b42ec | |||
fe1793844d | |||
68940f05d8 | |||
53212db3ed | |||
dee16a4355 | |||
6da1446afc | |||
76e3b57a12 | |||
524180c271 | |||
c69e552a83 | |||
2844f2779f | |||
2b44be984e | |||
989ebcbb62 | |||
23e0d65471 | |||
e92fb68f3c | |||
7724f7446a | |||
207f9b6017 | |||
a581773887 | |||
7f2330a968 | |||
aaaeb924ac | |||
98a68ad3e7 | |||
36d3062a42 | |||
83b19bf1a2 | |||
e6f1b04cd5 | |||
6aaca21c27 | |||
50c4ec6687 | |||
9a2d1fab84 | |||
8052de07e2 | |||
40aaa42f44 | |||
35f8bfce8b | |||
4b05ebc804 | |||
e30f494a39 | |||
1d3e22766a | |||
c0955975f4 | |||
9515f171b4 | |||
1efaac5cb0 | |||
d27181fcdd | |||
9e34670b25 | |||
5039faff8d | |||
44c6534f3c | |||
09970d52e8 | |||
95a9d67599 | |||
fde016bc38 | |||
11a208f55c | |||
1db3ac457c | |||
02491a6ce8 | |||
dca713c087 | |||
eb6281f5b4 | |||
a4a423a083 | |||
ba5b3af077 | |||
64e7af4e43 | |||
d2d84c4460 | |||
29f5582af5 | |||
63bdfca580 | |||
361109d80f | |||
b7a099d27e | |||
f83dfd6f5a | |||
dd36f3ac99 | |||
0d6fd134d4 | |||
bbb27b5517 | |||
7ca611cd12 | |||
c12b6fa028 | |||
a29dc961a2 | |||
0de5d79bf6 | |||
f57e77eeb4 | |||
2b5d52fbdc | |||
81332150aa | |||
c6b29f4c6d | |||
261dc04d8e | |||
15e397816f | |||
f96a81a818 | |||
4f38419e33 | |||
119004c7d4 | |||
d171006083 | |||
19e8570ac0 | |||
93abc35213 | |||
d7be38f84b | |||
2d38623974 | |||
5306330d85 | |||
c150354464 | |||
7217525da4 | |||
b6e8d19313 | |||
6c0cca093a | |||
f2360aab9d | |||
d7b4172678 |
@ -1,3 +1,4 @@
|
||||
.git
|
||||
node_modules
|
||||
dist
|
||||
aio/content
|
||||
|
42
.bazelrc
42
.bazelrc
@ -1,14 +1,3 @@
|
||||
###############################
|
||||
# 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
|
||||
# TODO(alexeagle): re-enable after fixing worker instability with rxjs typings
|
||||
# build --strategy=TypeScriptCompile=worker
|
||||
build --strategy=TypeScriptCompile=standalone
|
||||
|
||||
# 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
|
||||
|
||||
@ -85,12 +74,6 @@ 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 #
|
||||
################################
|
||||
@ -154,6 +137,31 @@ build:remote --bes_results_url="https://source.cloud.google.com/results/invocati
|
||||
# This allows us to avoid installing a second copy of node_modules
|
||||
common --experimental_allow_incremental_repository_updates
|
||||
|
||||
# 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",
|
||||
# },
|
||||
# ```
|
||||
build --incompatible_list_based_execution_strategy_selection=false
|
||||
test --incompatible_list_based_execution_strategy_selection=false
|
||||
run --incompatible_list_based_execution_strategy_selection=false
|
||||
|
||||
####################################################
|
||||
# User bazel configuration
|
||||
# NOTE: This needs to be the *last* entry in the config.
|
||||
|
@ -28,3 +28,14 @@ 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
|
||||
|
@ -58,17 +58,7 @@ 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
|
||||
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
|
||||
./.circleci/setup-rbe.sh .bazelrc.user
|
||||
|
||||
# Settings common to each job
|
||||
var_6: &job_defaults
|
||||
@ -138,7 +128,7 @@ var_13: ¬ify_caretaker_on_fail
|
||||
# `$SLACK_CARETAKER_WEBHOOK_URL` is a secret env var defined in CircleCI project settings.
|
||||
# The URL comes from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
|
||||
command: |
|
||||
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
|
||||
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_CARETAKER_WEBHOOK_URL
|
||||
|
||||
var_14: ¬ify_dev_infra_on_fail
|
||||
@ -148,9 +138,14 @@ var_14: ¬ify_dev_infra_on_fail
|
||||
# `$SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL` is a secret env var defined in CircleCI project settings.
|
||||
# The URL comes from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
|
||||
command: |
|
||||
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
|
||||
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:
|
||||
@ -245,6 +240,11 @@ 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,6 +293,8 @@ 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
|
||||
@ -618,40 +620,36 @@ 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
|
||||
- run:
|
||||
name: "Cloning Material repository"
|
||||
command: ./scripts/ci/clone_angular_material_repo.sh
|
||||
# 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:
|
||||
# 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-
|
||||
- *material_unit_tests_cache_key
|
||||
- *material_unit_tests_cache_key_short
|
||||
- run:
|
||||
name: "Fetching Material repository"
|
||||
command: ./scripts/ci/clone_angular_material_repo.sh
|
||||
- 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:
|
||||
# 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" }}
|
||||
key: *material_unit_tests_cache_key
|
||||
paths:
|
||||
- "/tmp/material2/node_modules"
|
||||
# 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"
|
||||
- run:
|
||||
name: "Running Material unit tests"
|
||||
command: ./scripts/ci/run_angular_material_unit_tests.sh
|
||||
@ -666,6 +664,10 @@ 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
|
||||
@ -674,31 +676,41 @@ 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
|
||||
- test_aio:
|
||||
requires:
|
||||
- setup
|
||||
- legacy-unit-tests-saucelabs:
|
||||
requires:
|
||||
- setup
|
||||
- deploy_aio:
|
||||
requires:
|
||||
- test_aio
|
||||
- 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
|
||||
- test_aio:
|
||||
requires:
|
||||
- setup
|
||||
- deploy_aio:
|
||||
requires:
|
||||
- test_aio
|
||||
- test_aio_local:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
@ -755,22 +767,6 @@ 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
|
||||
|
@ -77,7 +77,9 @@ 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 "ivy-2019"
|
||||
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"
|
||||
|
||||
# Source `$BASH_ENV` to make the variables available immediately.
|
||||
source $BASH_ENV;
|
||||
|
Binary file not shown.
20
.circleci/setup-rbe.sh
Executable file
20
.circleci/setup-rbe.sh
Executable file
@ -0,0 +1,20 @@
|
||||
#!/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
|
@ -82,6 +82,9 @@ FROM baseimage
|
||||
|
||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
||||
|
||||
# Install Bazel prereqs on Windows (https://docs.bazel.build/versions/master/install-windows.html)
|
||||
|
||||
# Install MSYS2
|
||||
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; `
|
||||
@ -94,7 +97,10 @@ RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe'
|
||||
[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
|
||||
# Install MSYS2 packages
|
||||
RUN C:\msys64\usr\bin\bash.exe -l -c \"pacman --needed --noconfirm -S zip unzip patch diffutils git\"
|
||||
|
||||
# Install VS Build Tools (required to build C++ targets)
|
||||
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; `
|
||||
@ -112,7 +118,7 @@ RUN Invoke-WebRequest -UseBasicParsing https://download.visualstudio.microsoft.c
|
||||
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
|
||||
# Install Python (required to build Python targets)
|
||||
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
|
||||
|
30
BUILD.bazel
30
BUILD.bazel
@ -18,15 +18,15 @@ filegroup(
|
||||
name = "web_test_bootstrap_scripts",
|
||||
# do not sort
|
||||
srcs = [
|
||||
"@npm//node_modules/core-js:client/core.js",
|
||||
"@npm//node_modules/zone.js:dist/zone.js",
|
||||
"@npm//node_modules/zone.js:dist/zone-testing.js",
|
||||
"@npm//node_modules/zone.js:dist/task-tracking.js",
|
||||
"@npm//:node_modules/core-js/client/core.js",
|
||||
"@npm//:node_modules/zone.js/dist/zone.js",
|
||||
"@npm//:node_modules/zone.js/dist/zone-testing.js",
|
||||
"@npm//:node_modules/zone.js/dist/task-tracking.js",
|
||||
"//:test-events.js",
|
||||
"//:shims_for_IE.js",
|
||||
# Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
"@npm//node_modules/systemjs:dist/system.src.js",
|
||||
"@npm//node_modules/reflect-metadata:Reflect.js",
|
||||
"@npm//:node_modules/systemjs/dist/system.src.js",
|
||||
"@npm//:node_modules/reflect-metadata/Reflect.js",
|
||||
],
|
||||
)
|
||||
|
||||
@ -35,15 +35,15 @@ filegroup(
|
||||
srcs = [
|
||||
# We also declare the unminfied AngularJS files since these can be used for
|
||||
# local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts)
|
||||
"@npm//node_modules/angular:angular.js",
|
||||
"@npm//node_modules/angular:angular.min.js",
|
||||
"@npm//node_modules/angular-1.5:angular.js",
|
||||
"@npm//node_modules/angular-1.5:angular.min.js",
|
||||
"@npm//node_modules/angular-1.6:angular.js",
|
||||
"@npm//node_modules/angular-1.6:angular.min.js",
|
||||
"@npm//node_modules/angular-mocks:angular-mocks.js",
|
||||
"@npm//node_modules/angular-mocks-1.5:angular-mocks.js",
|
||||
"@npm//node_modules/angular-mocks-1.6:angular-mocks.js",
|
||||
"@npm//:node_modules/angular/angular.js",
|
||||
"@npm//:node_modules/angular/angular.min.js",
|
||||
"@npm//:node_modules/angular-1.5/angular.js",
|
||||
"@npm//:node_modules/angular-1.5/angular.min.js",
|
||||
"@npm//:node_modules/angular-1.6/angular.js",
|
||||
"@npm//:node_modules/angular-1.6/angular.min.js",
|
||||
"@npm//:node_modules/angular-mocks/angular-mocks.js",
|
||||
"@npm//:node_modules/angular-mocks-1.5/angular-mocks.js",
|
||||
"@npm//:node_modules/angular-mocks-1.6/angular-mocks.js",
|
||||
],
|
||||
)
|
||||
|
||||
|
128
CHANGELOG.md
128
CHANGELOG.md
@ -1,3 +1,131 @@
|
||||
<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)
|
||||
|
||||
|
||||
### 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)) ([b0c3453](https://github.com/angular/angular/commit/b0c3453)), 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)) ([a9038ef](https://github.com/angular/angular/commit/a9038ef))
|
||||
|
||||
|
||||
|
||||
<a name="8.1.0-rc.0"></a>
|
||||
# [8.1.0-rc.0](https://github.com/angular/angular/compare/8.1.0-next.3...8.1.0-rc.0) (2019-06-26)
|
||||
|
||||
|
33
WORKSPACE
33
WORKSPACE
@ -18,8 +18,11 @@ 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",
|
||||
sha256 = "e04a82a72146bfbca2d0575947daa60fda1878c8d3a3afe868a8ec39a6b968bb",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.31.1/rules_nodejs-0.31.1.tar.gz"],
|
||||
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"],
|
||||
)
|
||||
|
||||
# Check the bazel version and download npm dependencies
|
||||
@ -27,6 +30,7 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "check_rules
|
||||
|
||||
# Bazel version must be at least the following version because:
|
||||
# - 0.26.0 managed_directories feature added which is required for nodejs rules 0.30.0
|
||||
# - 0.27.0 has a fix for managed_directories after `rm -rf node_modules`
|
||||
check_bazel_version(
|
||||
message = """
|
||||
You no longer need to install Bazel on your machine.
|
||||
@ -35,7 +39,7 @@ Try running `yarn bazel` instead.
|
||||
(If you did run that, check that you've got a fresh `yarn install`)
|
||||
|
||||
""",
|
||||
minimum_bazel_version = "0.26.0",
|
||||
minimum_bazel_version = "0.27.0",
|
||||
)
|
||||
|
||||
# The NodeJS rules version must be at least the following version because:
|
||||
@ -46,7 +50,11 @@ Try running `yarn bazel` instead.
|
||||
# - 0.27.12 Adds NodeModuleSources provider for transtive npm deps support
|
||||
# - 0.30.0 yarn_install now uses symlinked node_modules with new managed directories Bazel 0.26.0 feature
|
||||
# - 0.31.1 entry_point attribute of nodejs_binary & rollup_bundle is now a label
|
||||
check_rules_nodejs_version(minimum_version_string = "0.31.1")
|
||||
# - 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")
|
||||
|
||||
# Setup the Node.js toolchain
|
||||
node_repositories(
|
||||
@ -70,19 +78,7 @@ node_repositories(
|
||||
|
||||
yarn_install(
|
||||
name = "npm",
|
||||
data = [
|
||||
"//:tools/npm/@angular_bazel/index.js",
|
||||
"//:tools/npm/@angular_bazel/package.json",
|
||||
"//:tools/postinstall-patches.js",
|
||||
"//:tools/yarn/check-yarn.js",
|
||||
],
|
||||
package_json = "//:package.json",
|
||||
# Don't install devDependencies, they are large and not used under Bazel
|
||||
prod_only = True,
|
||||
# Temporarily disable node_modules symlinking until the fix for
|
||||
# https://github.com/bazelbuild/bazel/issues/8487 makes it into a
|
||||
# future Bazel release
|
||||
symlink_node_modules = False,
|
||||
yarn_lock = "//:yarn.lock",
|
||||
)
|
||||
|
||||
@ -96,6 +92,11 @@ 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")
|
||||
|
||||
|
@ -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 format="." language="bash">
|
||||
<code-example 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 format="." language="bash">
|
||||
<code-example 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 format="." language="bash">
|
||||
<code-example language="bash">
|
||||
ng new my-first-project
|
||||
cd my-first-project
|
||||
ng serve
|
||||
@ -36,6 +36,14 @@ 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.
|
||||
@ -75,7 +83,7 @@ Command syntax is shown as follows:
|
||||
Option aliases are prefixed with a single dash (-).
|
||||
Arguments are not prefixed.
|
||||
For example:
|
||||
<code-example format="." language="bash">
|
||||
<code-example language="bash">
|
||||
ng build my-app -c production
|
||||
</code-example>
|
||||
|
||||
|
@ -25,8 +25,8 @@ describe('Attribute directives', () => {
|
||||
greenRb.click();
|
||||
browser.actions().mouseMove(highlightedEle).perform();
|
||||
|
||||
// Wait for up to 2s for the background color to be updated,
|
||||
// Wait for up to 4s 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), 2000);
|
||||
browser.wait(() => highlightedEle.getCssValue('background-color').then(c => c === lightGreen), 4000);
|
||||
});
|
||||
});
|
||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 32 KiB |
@ -18,7 +18,7 @@ export class AppComponent {
|
||||
}
|
||||
|
||||
deleteItem(item: Item) {
|
||||
alert(`Delete the ${item}.`);
|
||||
alert(`Delete the ${item.name}.`);
|
||||
}
|
||||
|
||||
onClickMe(event?: KeyboardEvent) {
|
||||
|
@ -16,13 +16,17 @@
|
||||
<!-- #enddocregion checkout-form-1 -->
|
||||
|
||||
<div>
|
||||
<label>Name</label>
|
||||
<input type="text" formControlName="name">
|
||||
<label for="name">
|
||||
Name
|
||||
</label>
|
||||
<input id="name" type="text" formControlName="name">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Address</label>
|
||||
<input type="text" formControlName="address">
|
||||
<label for="address">
|
||||
Address
|
||||
</label>
|
||||
<input id="address" type="text" formControlName="address">
|
||||
</div>
|
||||
|
||||
<button class="button" type="submit">Purchase</button>
|
||||
|
@ -10,7 +10,6 @@ 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));
|
||||
}
|
||||
|
14
aio/content/examples/ngmodules/src/app/app.module.1.ts
Normal file
14
aio/content/examples/ngmodules/src/app/app.module.1.ts
Normal file
@ -0,0 +1,14 @@
|
||||
// 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 {}
|
@ -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
|
||||
|
@ -2,42 +2,49 @@
|
||||
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
// Not yet complete
|
||||
describe('Template Syntax', function () {
|
||||
// TODO Not yet complete
|
||||
describe('Template Syntax', () => {
|
||||
|
||||
beforeAll(function () {
|
||||
beforeAll(() => {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
it('should be able to use interpolation with a hero', function () {
|
||||
let heroInterEle = element.all(by.css('h2+p')).get(0);
|
||||
it('should be able to use interpolation with a hero', () => {
|
||||
const heroInterEle = element.all(by.css('h2+p')).get(0);
|
||||
expect(heroInterEle.getText()).toEqual('My current hero is Hercules');
|
||||
});
|
||||
|
||||
it('should be able to use interpolation with a calculation', function () {
|
||||
let theSumEles = element.all(by.cssContainingText('h3~p', 'The sum of'));
|
||||
it('should be able to use interpolation with a calculation', () => {
|
||||
const theSumEles = element.all(by.cssContainingText('h3~p', 'The sum of'));
|
||||
expect(theSumEles.count()).toBe(2);
|
||||
expect(theSumEles.get(0).getText()).toEqual('The sum of 1 + 1 is 2');
|
||||
expect(theSumEles.get(1).getText()).toEqual('The sum of 1 + 1 is not 4');
|
||||
});
|
||||
|
||||
it('should be able to use class binding syntax', function () {
|
||||
let specialEle = element(by.cssContainingText('div', 'Special'));
|
||||
it('should be able to use class binding syntax', () => {
|
||||
const specialEle = element(by.cssContainingText('div', 'Special'));
|
||||
expect(specialEle.getAttribute('class')).toMatch('special');
|
||||
});
|
||||
|
||||
it('should be able to use style binding syntax', function () {
|
||||
let specialButtonEle = element(by.cssContainingText('div.special~button', 'button'));
|
||||
it('should be able to use style binding syntax', () => {
|
||||
const specialButtonEle = element(by.cssContainingText('div.special~button', 'button'));
|
||||
expect(specialButtonEle.getAttribute('style')).toMatch('color: red');
|
||||
});
|
||||
|
||||
it('should two-way bind to sizer', async () => {
|
||||
let div = element(by.css('div#two-way-1'));
|
||||
let incButton = div.element(by.buttonText('+'));
|
||||
let input = div.element(by.css('input'));
|
||||
let initSize = await input.getAttribute('value');
|
||||
const div = element(by.css('div#two-way-1'));
|
||||
const incButton = div.element(by.buttonText('+'));
|
||||
const input = div.element(by.css('input'));
|
||||
const initSize = await input.getAttribute('value');
|
||||
incButton.click();
|
||||
expect(input.getAttribute('value')).toEqual((+initSize + 1).toString());
|
||||
});
|
||||
});
|
||||
|
||||
it('should change SVG rectangle\'s fill color on click', async () => {
|
||||
const div = element(by.css('app-svg'));
|
||||
const colorSquare = div.element(by.css('rect'));
|
||||
const initialColor = await colorSquare.getAttribute('fill');
|
||||
colorSquare.click();
|
||||
expect(colorSquare.getAttribute('fill')).not.toEqual(initialColor);
|
||||
});
|
||||
});
|
||||
|
@ -38,6 +38,7 @@
|
||||
<a href="#safe-navigation-operator">Safe navigation operator <i>?.</i></a><br>
|
||||
<a href="#non-null-assertion-operator">Non-null assertion operator <i>!.</i></a><br>
|
||||
<a href="#enums">Enums</a><br>
|
||||
<a href="#svg-templates">SVG Templates</a><br>
|
||||
|
||||
<!-- Interpolation and expressions -->
|
||||
<hr><h2 id="interpolation">Interpolation</h2>
|
||||
@ -442,7 +443,7 @@ button</button>
|
||||
|
||||
<!-- #docregion without-NgModel -->
|
||||
<input [value]="currentHero.name"
|
||||
(input)="currentHero.name=$event.target.value" >
|
||||
(input)="updateCurrentHeroName($event)">
|
||||
<!-- #enddocregion without-NgModel -->
|
||||
without NgModel
|
||||
<br>
|
||||
@ -752,7 +753,7 @@ bindon-ngModel
|
||||
|
||||
<div>
|
||||
<!-- pipe price to USD and display the $ symbol -->
|
||||
<label>Price: </label>{{product.price | currency:'USD':true}}
|
||||
<label>Price: </label>{{product.price | currency:'USD':'symbol'}}
|
||||
</div>
|
||||
|
||||
<a class="to-toc" href="#toc">top</a>
|
||||
@ -857,3 +858,9 @@ The null hero's name is {{nullHero && nullHero.name}}
|
||||
</p>
|
||||
|
||||
<a class="to-toc" href="#toc">top</a>
|
||||
|
||||
<hr><h2 id="svg-templates">SVG Templates</h2>
|
||||
<!-- #docregion svg-templates -->
|
||||
<app-svg></app-svg>
|
||||
<!-- #enddocregion svg-templates -->
|
||||
<a class="to-toc" href="#toc">top</a>
|
||||
|
@ -5,7 +5,7 @@ import { AfterViewInit, Component, ElementRef, OnInit, QueryList, ViewChildren }
|
||||
|
||||
import { Hero } from './hero';
|
||||
|
||||
export enum Color {Red, Green, Blue};
|
||||
export enum Color {Red, Green, Blue}
|
||||
|
||||
/**
|
||||
* Giant grab bag of stuff to drive the chapter
|
||||
@ -29,7 +29,7 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
trackChanges(this.heroesWithTrackBy, () => this.heroesWithTrackByCount++);
|
||||
}
|
||||
|
||||
@ViewChildren('noTrackBy') heroesNoTrackBy: QueryList<ElementRef>;
|
||||
@ViewChildren('noTrackBy') heroesNoTrackBy: QueryList<ElementRef>;
|
||||
@ViewChildren('withTrackBy') heroesWithTrackBy: QueryList<ElementRef>;
|
||||
|
||||
actionName = 'Go for it';
|
||||
@ -66,6 +66,10 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
|
||||
currentHero: Hero;
|
||||
|
||||
updateCurrentHeroName(event: Event) {
|
||||
this.currentHero.name = (event.target as any).value;
|
||||
}
|
||||
|
||||
deleteHero(hero?: Hero) {
|
||||
this.alert(`Delete ${hero ? hero.name : 'the hero'}.`);
|
||||
}
|
||||
@ -105,13 +109,13 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
|
||||
get nullHero(): Hero { return null; }
|
||||
|
||||
onClickMe(event?: KeyboardEvent) {
|
||||
let evtMsg = event ? ' Event target class is ' + (<HTMLElement>event.target).className : '';
|
||||
onClickMe(event?: MouseEvent) {
|
||||
const evtMsg = event ? ' Event target class is ' + (event.target as HTMLElement).className : '';
|
||||
this.alert('Click me.' + evtMsg);
|
||||
}
|
||||
|
||||
onSave(event?: KeyboardEvent) {
|
||||
let evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).textContent : '';
|
||||
onSave(event?: MouseEvent) {
|
||||
const evtMsg = event ? ' Event target is ' + (event.target as HTMLElement).textContent : '';
|
||||
this.alert('Saved.' + evtMsg);
|
||||
if (event) { event.stopPropagation(); }
|
||||
}
|
||||
@ -140,9 +144,9 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
setCurrentClasses() {
|
||||
// CSS classes: added/removed per current state of component properties
|
||||
this.currentClasses = {
|
||||
'saveable': this.canSave,
|
||||
'modified': !this.isUnchanged,
|
||||
'special': this.isSpecial
|
||||
saveable: this.canSave,
|
||||
modified: !this.isUnchanged,
|
||||
special: this.isSpecial
|
||||
};
|
||||
}
|
||||
// #enddocregion setClasses
|
||||
@ -164,7 +168,7 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
// #enddocregion trackByHeroes
|
||||
|
||||
// #docregion trackById
|
||||
trackById(index: number, item: any): number { return item['id']; }
|
||||
trackById(index: number, item: any): number { return item.id; }
|
||||
// #enddocregion trackById
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { BigHeroDetailComponent, HeroDetailComponent } from './hero-detail.component';
|
||||
import { ClickDirective, ClickDirective2 } from './click.directive';
|
||||
import { HeroFormComponent } from './hero-form.component';
|
||||
import { heroSwitchComponents } from './hero-switch.components';
|
||||
import { SizerComponent } from './sizer.component';
|
||||
import { HeroFormComponent } from './hero-form.component';
|
||||
import { heroSwitchComponents } from './hero-switch.components';
|
||||
import { SizerComponent } from './sizer.component';
|
||||
import { SvgComponent } from './svg.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -22,7 +23,8 @@ import { SizerComponent } from './sizer.component';
|
||||
heroSwitchComponents,
|
||||
ClickDirective,
|
||||
ClickDirective2,
|
||||
SizerComponent
|
||||
SizerComponent,
|
||||
SvgComponent
|
||||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* tslint:disable use-output-property-decorator directive-class-suffix */
|
||||
/* tslint:disable directive-selector directive-class-suffix */
|
||||
// #docplaster
|
||||
import { Directive, ElementRef, EventEmitter, Output } from '@angular/core';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Component, Input, ViewChild } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { NgForm } from '@angular/forms';
|
||||
|
||||
import { Hero } from './hero';
|
||||
|
||||
@ -15,10 +15,11 @@ export class HeroFormComponent {
|
||||
@Input() hero: Hero;
|
||||
@ViewChild('heroForm', {static: false}) form: NgForm;
|
||||
|
||||
// tslint:disable-next-line:variable-name
|
||||
private _submitMessage = '';
|
||||
|
||||
get submitMessage() {
|
||||
if (!this.form.valid) {
|
||||
if (this.form && !this.form.valid) {
|
||||
this._submitMessage = '';
|
||||
}
|
||||
return this._submitMessage;
|
||||
|
@ -0,0 +1,4 @@
|
||||
svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<svg>
|
||||
<g>
|
||||
<rect x="0" y="0" width="100" height="100" [attr.fill]="fillColor" (click)="changeColor()" />
|
||||
<text x="120" y="50">click the rectangle to change the fill color</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 201 B |
@ -0,0 +1,17 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-svg',
|
||||
templateUrl: './svg.component.svg',
|
||||
styleUrls: ['./svg.component.css']
|
||||
})
|
||||
export class SvgComponent {
|
||||
fillColor = 'rgb(255, 0, 0)';
|
||||
|
||||
changeColor() {
|
||||
const r = Math.floor(Math.random() * 256);
|
||||
const g = Math.floor(Math.random() * 256);
|
||||
const b = Math.floor(Math.random() * 256);
|
||||
this.fillColor = `rgb(${r}, ${g}, ${b})`;
|
||||
}
|
||||
}
|
@ -1,8 +1,4 @@
|
||||
/* HeroesComponent's private CSS styles */
|
||||
.selected {
|
||||
background-color: #CFD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
.heroes {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
@ -19,18 +15,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 .text {
|
||||
position: relative;
|
||||
top: -3px;
|
||||
.heroes li.selected {
|
||||
background-color: #CFD8DC;
|
||||
color: white;
|
||||
}
|
||||
.heroes li.selected:hover {
|
||||
background-color: #BBD8DC;
|
||||
color: white;
|
||||
}
|
||||
.heroes .badge {
|
||||
display: inline-block;
|
||||
|
@ -34,4 +34,7 @@ export class HeroesComponent implements OnInit {
|
||||
this.selectedHero = hero;
|
||||
}
|
||||
// #enddocregion on-select
|
||||
// #docregion component
|
||||
}
|
||||
// #enddocregion component
|
||||
|
||||
|
@ -1,8 +1,4 @@
|
||||
/* HeroesComponent's private CSS styles */
|
||||
.selected {
|
||||
background-color: #CFD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
.heroes {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
@ -19,18 +15,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 .text {
|
||||
position: relative;
|
||||
top: -3px;
|
||||
.heroes li.selected {
|
||||
background-color: #CFD8DC;
|
||||
color: white;
|
||||
}
|
||||
.heroes li.selected:hover {
|
||||
background-color: #BBD8DC;
|
||||
color: white;
|
||||
}
|
||||
.heroes .badge {
|
||||
display: inline-block;
|
||||
|
@ -1,8 +1,4 @@
|
||||
/* HeroesComponent's private CSS styles */
|
||||
.selected {
|
||||
background-color: #CFD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
.heroes {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
@ -19,18 +15,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 .text {
|
||||
position: relative;
|
||||
top: -3px;
|
||||
.heroes li.selected {
|
||||
background-color: #CFD8DC;
|
||||
color: white;
|
||||
}
|
||||
.heroes li.selected:hover {
|
||||
background-color: #BBD8DC;
|
||||
color: white;
|
||||
}
|
||||
.heroes .badge {
|
||||
display: inline-block;
|
||||
@ -43,8 +39,6 @@
|
||||
left: -1px;
|
||||
top: -4px;
|
||||
height: 1.8em;
|
||||
min-width: 16px;
|
||||
text-align: right;
|
||||
margin-right: .8em;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
13
aio/content/examples/toh-pt5/src/app/app-routing.module.1.ts
Normal file
13
aio/content/examples/toh-pt5/src/app/app-routing.module.1.ts
Normal file
@ -0,0 +1,13 @@
|
||||
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 { }
|
@ -7,9 +7,7 @@ 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
|
||||
@ -39,7 +37,9 @@ 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
|
||||
|
@ -23,13 +23,17 @@ 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.
|
||||
@ -38,7 +42,9 @@ import { MessagesComponent } from './messages/messages.component';
|
||||
InMemoryDataService, { dataEncapsulation: false }
|
||||
)
|
||||
// #enddocregion in-mem-web-api-imports
|
||||
// #docregion import-httpclientmodule
|
||||
],
|
||||
// #enddocregion import-httpclientmodule
|
||||
declarations: [
|
||||
AppComponent,
|
||||
DashboardComponent,
|
||||
@ -50,6 +56,9 @@ import { MessagesComponent } from './messages/messages.component';
|
||||
// #docregion v1
|
||||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
// #docregion import-httpclientmodule
|
||||
})
|
||||
// #enddocregion import-httpclientmodule
|
||||
|
||||
export class AppModule { }
|
||||
// #enddocregion , v1
|
||||
|
@ -13,11 +13,6 @@ 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 {
|
||||
@ -26,6 +21,12 @@ 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,
|
||||
@ -96,7 +97,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, httpOptions).pipe(
|
||||
return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe(
|
||||
tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),
|
||||
catchError(this.handleError<Hero>('addHero'))
|
||||
);
|
||||
@ -109,7 +110,7 @@ export class HeroService {
|
||||
const id = typeof hero === 'number' ? hero : hero.id;
|
||||
const url = `${this.heroesUrl}/${id}`;
|
||||
|
||||
return this.http.delete<Hero>(url, httpOptions).pipe(
|
||||
return this.http.delete<Hero>(url, this.httpOptions).pipe(
|
||||
tap(_ => this.log(`deleted hero id=${id}`)),
|
||||
catchError(this.handleError<Hero>('deleteHero'))
|
||||
);
|
||||
@ -119,7 +120,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, httpOptions).pipe(
|
||||
return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(
|
||||
tap(_ => this.log(`updated hero id=${hero.id}`)),
|
||||
catchError(this.handleError<any>('updateHero'))
|
||||
);
|
||||
|
@ -0,0 +1,79 @@
|
||||
import { browser, element, by, ExpectedConditions } from 'protractor';
|
||||
|
||||
describe('Lazy Loading AngularJS Tests', function () {
|
||||
const pageElements = {
|
||||
homePageHref: element(by.cssContainingText('app-root nav a', 'Home')),
|
||||
homePageParagraph: element(by.css('app-root app-home p')),
|
||||
ajsUsersPageHref: element(by.cssContainingText('app-root nav a', 'Users')),
|
||||
ajsUsersPageParagraph: element(by.css('app-root app-angular-js div p')),
|
||||
notFoundPageHref: element(by.cssContainingText('app-root nav a', '404 Page')),
|
||||
notFoundPageParagraph: element(by.css('app-root app-app404 p')),
|
||||
};
|
||||
|
||||
beforeAll(async() => {
|
||||
await browser.get('/');
|
||||
});
|
||||
|
||||
it('should display \'Angular Home\' when visiting the home page', async() => {
|
||||
await pageElements.homePageHref.click();
|
||||
|
||||
const paragraphText = await pageElements.homePageParagraph.getText();
|
||||
|
||||
expect(paragraphText).toEqual('Angular Home');
|
||||
});
|
||||
|
||||
it('should display \'Users Page\' page when visiting the AngularJS page at /users', async() => {
|
||||
await pageElements.ajsUsersPageHref.click();
|
||||
await loadAngularJS();
|
||||
|
||||
const paragraphText = await pageElements.ajsUsersPageParagraph.getText();
|
||||
|
||||
expect(paragraphText).toEqual('Users Page');
|
||||
});
|
||||
|
||||
it('should display \'Angular 404\' when visiting an invalid URL', async() => {
|
||||
await pageElements.notFoundPageHref.click();
|
||||
|
||||
const paragraphText = await pageElements.notFoundPageParagraph.getText();
|
||||
|
||||
expect(paragraphText).toEqual('Angular 404');
|
||||
});
|
||||
|
||||
// Workaround for https://github.com/angular/protractor/issues/4724
|
||||
async function loadAngularJS() {
|
||||
// Abort if `resumeBootstrap` has already occured
|
||||
if (await browser.executeScript(`return '__TESTABILITY__NG1_APP_ROOT_INJECTOR__' in window;`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Might have to re-insert the 'NG_DEFER_BOOTSTRAP!' if the name has been changed since protractor loaded the page
|
||||
if (!await browser.executeScript('window.name.includes(\'NG_DEFER_BOOTSTRAP!\')')) {
|
||||
await browser.executeScript('window.name = \'NG_DEFER_BOOTSTRAP!\' + name');
|
||||
}
|
||||
|
||||
// Wait for the AngularJS bundle to download and initialize
|
||||
await browser.wait(ExpectedConditions.presenceOf(element(by.css('app-root app-angular-js'))), 5000, 'AngularJS app');
|
||||
|
||||
// Run the protractor pre-bootstrap logic and resumeBootstrap
|
||||
// Based on https://github.com/angular/protractor/blob/5.3.0/lib/browser.ts#L950-L969
|
||||
{
|
||||
let moduleNames = [];
|
||||
for (const {name, script, args} of browser.mockModules_) {
|
||||
moduleNames.push(name);
|
||||
await browser.executeScriptWithDescription(script, 'add mock module ' + name, ...args);
|
||||
}
|
||||
|
||||
await browser.executeScriptWithDescription(
|
||||
// TODO: must manually assign __TESTABILITY__NG1_APP_ROOT_INJECTOR__ (https://github.com/angular/angular/issues/22723)
|
||||
`window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__ = angular.resumeBootstrap(arguments[0]) `
|
||||
+ `|| angular.element('app-angular-js').injector();`,
|
||||
'resume bootstrap',
|
||||
moduleNames
|
||||
);
|
||||
}
|
||||
|
||||
// Wait for the initial AngularJS page to finish loading
|
||||
await browser.waitForAngular();
|
||||
}
|
||||
});
|
||||
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"projectType": "cli-ajs"
|
||||
}
|
@ -1,14 +1,22 @@
|
||||
import { Component, OnInit, ElementRef } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, ElementRef } from '@angular/core';
|
||||
import { LazyLoaderService } from '../lazy-loader.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-angular-js',
|
||||
template: '<div ng-view></div>'
|
||||
})
|
||||
export class AngularJSComponent implements OnInit {
|
||||
constructor(private lazyLoader: LazyLoaderService, private elRef: ElementRef) {}
|
||||
export class AngularJSComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private lazyLoader: LazyLoaderService,
|
||||
private elRef: ElementRef
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.lazyLoader.load(this.elRef.nativeElement);
|
||||
}
|
||||
|
||||
|
||||
ngOnDestroy() {
|
||||
this.lazyLoader.destroy();
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import { App404Component } from './app404/app404.component';
|
||||
BrowserModule,
|
||||
AppRoutingModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
@ -1,23 +1,25 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import * as angular from 'angular';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LazyLoaderService {
|
||||
bootstrapped = false;
|
||||
private app: angular.auto.IInjectorService;
|
||||
|
||||
load(el: HTMLElement): void {
|
||||
if (this.bootstrapped) {
|
||||
return;
|
||||
}
|
||||
|
||||
import('./angularjs-app').then(app => {
|
||||
try {
|
||||
app.bootstrap(el);
|
||||
this.bootstrapped = true;
|
||||
this.app = app.bootstrap(el);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.app) {
|
||||
this.app.get('$rootScope').$destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
181
aio/content/guide/accessibility.md
Normal file
181
aio/content/guide/accessibility.md
Normal file
@ -0,0 +1,181 @@
|
||||
# Accessibility in Angular
|
||||
|
||||
The web is used by a wide variety of people, including those who have visual or motor impairments.
|
||||
A variety of assistive technologies are available that make it much easier for these groups to
|
||||
interact with web-based software applications.
|
||||
In addition, designing an application to be more accessible generally improves the user experience for all users.
|
||||
|
||||
For an in-depth introduction to issues and techniques for designing accessible applications, see the [Accessibility](https://developers.google.com/web/fundamentals/accessibility/#what_is_accessibility) section of the Google's [Web Fundamentals](https://developers.google.com/web/fundamentals/).
|
||||
|
||||
This page discusses best practices for designing Angular applications that
|
||||
work well for all users, including those who rely on assistive technologies.
|
||||
|
||||
## Accessibility attributes
|
||||
|
||||
Building accessible web experience often involves setting [ARIA attributes](https://developers.google.com/web/fundamentals/accessibility/semantics-aria)
|
||||
to provide semantic meaning where it might otherwise be missing.
|
||||
Use [attribute binding](guide/template-syntax#attribute-binding) template syntax to control the values of accessibility-related attributes.
|
||||
|
||||
When binding to ARIA attributes in Angular, you must use the `attr.` prefix, as the ARIA
|
||||
specification depends specifically on HTML attributes rather than properties on DOM elements.
|
||||
|
||||
```html
|
||||
<!-- Use attr. when binding to an ARIA attribute -->
|
||||
<button [attr.aria-label]="myActionLabel">...</button>
|
||||
```
|
||||
|
||||
Note that this syntax is only necessary for attribute _bindings_.
|
||||
Static ARIA attributes require no extra syntax.
|
||||
|
||||
```html
|
||||
<!-- Static ARIA attributes require no extra syntax -->
|
||||
<button aria-label="Save document">...</button>
|
||||
```
|
||||
|
||||
NOTE:
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
By convention, HTML attributes use lowercase names (`tabindex`), while properties use camelCase names (`tabIndex`).
|
||||
|
||||
See the [Template Syntax](https://angular.io/guide/template-syntax#html-attribute-vs-dom-property) guide for more background on the difference between attributes and properties.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## Angular UI components
|
||||
|
||||
The [Angular Material](https://material.angular.io/) library, which is maintained by the Angular team, is a suite of reusable UI components that aims to be fully accessible.
|
||||
The [Component Development Kit (CDK)](https://material.angular.io/cdk/categories) includes the `a11y` package that provides tools to support various areas of accessibility.
|
||||
For example:
|
||||
|
||||
* `LiveAnnouncer` is used to announce messages for screen-reader users using an `aria-live` region. See the W3C documentation for more information on [aria-live regions](https://www.w3.org/WAI/PF/aria-1.1/states_and_properties#aria-live).
|
||||
|
||||
* The `cdkTrapFocus` directive traps Tab-key focus within an element. Use it to create accessible experience for components like modal dialogs, where focus must be constrained.
|
||||
|
||||
For full details of these and other tools, see the [Angular CDK accessibility overview](https://material.angular.io/cdk/a11y/overview).
|
||||
|
||||
|
||||
### Augmenting native elements
|
||||
|
||||
Native HTML elements capture a number of standard interaction patterns that are important to accessibility.
|
||||
When authoring Angular components, you should re-use these native elements directly when possible, rather than re-implementing well-supported behaviors.
|
||||
|
||||
For example, instead of creating a custom element for a new variety of button, you can create a component that uses an attribute selector with a native `<button>` element.
|
||||
This most commonly applies to `<button>` and `<a>`, but can be used with many other types of element.
|
||||
|
||||
You can see examples of this pattern in Angular Material: [`MatButton`](https://github.com/angular/components/blob/master/src/material/button/button.ts#L66-L68), [`MatTabNav`](https://github.com/angular/components/blob/master/src/material/tabs/tab-nav-bar/tab-nav-bar.ts#L67), [`MatTable`](https://github.com/angular/components/blob/master/src/material/table/table.ts#L17).
|
||||
|
||||
### Using containers for native elements
|
||||
|
||||
Sometimes using the appropriate native element requires a container element.
|
||||
For example, the native `<input>` element cannot have children, so any custom text entry components need
|
||||
to wrap an `<input>` with additional elements.
|
||||
While you might just include the `<input>` in your custom component's template,
|
||||
this makes it impossible for users of the component to set arbitrary properties and attributes to the input element.
|
||||
Instead, you can create a container component that uses content projection to include the native control in the
|
||||
component's API.
|
||||
|
||||
You can see [`MatFormField`](https://material.angular.io/components/form-field/overview) as an example of this pattern.
|
||||
|
||||
## Case study: Building a custom progress bar
|
||||
|
||||
The following example shows how to make a simple progress bar accessible by using host binding to control accessibility-related attributes.
|
||||
|
||||
* The component defines an accessibility-enabled element with both the standard HTML attribute `role`, and ARIA attributes. The ARIA attribute `aria-valuenow` is bound to the user's input.
|
||||
|
||||
```ts
|
||||
import { Component, Input } from '@angular/core';
|
||||
/**
|
||||
* Example progressbar component.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'example-progressbar',
|
||||
template: `<div class="bar" [style.width.%]="value"></div>`,
|
||||
styleUrls: ['./progress-bar.css'],
|
||||
host: {
|
||||
// Sets the role for this component to "progressbar"
|
||||
role: 'progressbar',
|
||||
|
||||
// Sets the minimum and maximum values for the progressbar role.
|
||||
'aria-valuemin': '0',
|
||||
'aria-valuemax': '0',
|
||||
|
||||
// Binding that updates the current value of the progressbar.
|
||||
'[attr.aria-valuenow]': 'value',
|
||||
}
|
||||
})
|
||||
export class ExampleProgressbar {
|
||||
/** Current value of the progressbar. */
|
||||
@Input() value: number = 0;
|
||||
}
|
||||
```
|
||||
|
||||
* In the template, the `aria-label` attribute ensures that the control is accessible to screen readers.
|
||||
|
||||
```html
|
||||
<label>
|
||||
Enter an example progress value
|
||||
<input type="number" min="0" max="100"
|
||||
[value]="progress" (input)="progress = $event.target.value">
|
||||
</label>
|
||||
|
||||
<!-- The user of the progressbar sets an aria-label to communicate what the progress means. -->
|
||||
<example-progressbar [value]="progress" aria-label="Example of a progress bar">
|
||||
</example-progressbar>
|
||||
```
|
||||
|
||||
[See the full example in StackBlitz](https://stackblitz.com/edit/angular-kn5jdi?file=src%2Fapp%2Fapp.component.html).
|
||||
|
||||
## Routing and focus management
|
||||
|
||||
Tracking and controlling [focus](https://developers.google.com/web/fundamentals/accessibility/focus/) in a UI is an important consideration in designing for accessibility.
|
||||
When using Angular routing, you should decide where page focus goes upon navigation.
|
||||
|
||||
To avoid relying solely on visual cues, you need to make sure your routing code updates focus after page navigation.
|
||||
Use the `NavigationEnd` event from the `Router` service to know when to update
|
||||
focus.
|
||||
|
||||
The following example shows how to find and focus the main content header in the DOM after navigation.
|
||||
|
||||
```ts
|
||||
|
||||
router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe(() => {
|
||||
const mainHeader = document.querySelector('#main-content-header')
|
||||
if (mainHeader) {
|
||||
mainHeader.focus();
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
In a real application, the element that receives focus will depend on your specific
|
||||
application structure and layout.
|
||||
The focused element should put users in a position to immediately move into the main content that has just been routed into view.
|
||||
You should avoid situations where focus returns to the `body` element after a route change.
|
||||
|
||||
|
||||
## Additional resources
|
||||
|
||||
* [Accessibility - Google Web Fundamentals](https://developers.google.com/web/fundamentals/accessibility)
|
||||
|
||||
* [ARIA specification and authoring practices](https://www.w3.org/TR/wai-aria/)
|
||||
|
||||
* [Material Design - Accessibility](https://material.io/design/usability/accessibility.html)
|
||||
|
||||
* [Smashing Magazine](https://www.smashingmagazine.com/search/?q=accessibility)
|
||||
|
||||
* [Inclusive Components](https://inclusive-components.design/)
|
||||
|
||||
* [Accessibility Resources and Code Examples](https://dequeuniversity.com/resources/)
|
||||
|
||||
* [W3C - Web Accessibility Initiative](https://www.w3.org/WAI/people-use-web/)
|
||||
|
||||
* [Rob Dodson A11ycasts](https://www.youtube.com/watch?v=HtTyRajRuyY)
|
||||
|
||||
* [Codelyzer](http://codelyzer.com/rules/) provides linting rules that can help you make sure your code meets accessibility standards.
|
||||
|
||||
Books
|
||||
|
||||
* "A Web for Everyone: Designing Accessible User Experiences", Sarah Horton and Whitney Quesenbery
|
||||
|
||||
* "Inclusive Design Patterns", Heydon Pickering
|
@ -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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="interpolation"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="uppercase"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="local"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/main.ts" header="main.ts"></code-example>
|
||||
<br>
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts" header="app.module.ts" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts" header="app.module.ts"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngClass"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="event-binding"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="href"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="router-link"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngIf"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngModel"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngFor"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="hidden"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="src"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngStyle"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngSwitch"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="currency"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="date"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="json"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="slice"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="lowercase"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="number"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="class"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="di"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/.angular-cli.1.json" region="styles"></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" linenums="false"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="style-url"></code-example>
|
||||
|
||||
|
||||
This allows you to set appropriate styles for individual components that won’t leak into
|
||||
|
@ -37,8 +37,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="animations/src/app/app.module.1.ts" header="src/app/app.module.ts" language="typescript"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -129,7 +128,7 @@ The third argument, `easing`, controls how the animation [accelerates and decele
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
**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.
|
||||
**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.
|
||||
</div>
|
||||
|
||||
This example provides a state transition from `open` to `closed` with a one second transition between states.
|
||||
@ -180,9 +179,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" language="typescript" region="component"></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.
|
||||
|
||||
|
@ -945,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 linenums="false">
|
||||
<code-example>
|
||||
// CORRECTED
|
||||
import { calculateValue } from './utilities';
|
||||
|
||||
@ -978,7 +978,7 @@ The compiler does not support references to variables assigned by [destructuring
|
||||
|
||||
For example, you cannot write something like this:
|
||||
|
||||
<code-example linenums="false">
|
||||
<code-example>
|
||||
// ERROR
|
||||
import { configuration } from './configuration';
|
||||
|
||||
@ -994,7 +994,7 @@ const {foo, bar} = configuration;
|
||||
|
||||
To correct this error, refer to non-destructured values.
|
||||
|
||||
<code-example linenums="false">
|
||||
<code-example>
|
||||
// CORRECTED
|
||||
import { configuration } from './configuration';
|
||||
...
|
||||
@ -1041,7 +1041,7 @@ you can finesse the problem in four steps:
|
||||
|
||||
Here's an illustrative example.
|
||||
|
||||
<code-example linenums="false">
|
||||
<code-example>
|
||||
// CORRECTED
|
||||
import { Inject } from '@angular/core';
|
||||
|
||||
@ -1064,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 linenums="false">
|
||||
<code-example>
|
||||
import { Inject } from '@angular/core';
|
||||
import { DOCUMENT } from '@angular/platform-browser';
|
||||
|
||||
@ -1101,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 linenums="false">
|
||||
<code-example>
|
||||
// ERROR
|
||||
enum Colors {
|
||||
Red = 1,
|
||||
|
@ -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 format="." language="bash" linenums="false">
|
||||
<code-example language="bash">
|
||||
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 format="." language="bash" linenums="false">
|
||||
<code-example language="bash">
|
||||
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 format="." language="none" linenums="false">
|
||||
<code-example language="json">
|
||||
"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 format="." language="bash" linenums="false">
|
||||
<code-example language="bash">
|
||||
ng run my-app:app-shell
|
||||
</code-example>
|
||||
|
||||
|
@ -15,7 +15,7 @@ Its `selectHero()` method sets a `selectedHero` property when the user clicks to
|
||||
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" linenums="false" header="src/app/hero-list.component.ts (class)" region="class"></code-example>
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" 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" linenums="false" header="src/app/hero-list.component.ts (metadata)" region="metadata"></code-example>
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (metadata)" region="metadata"></code-example>
|
||||
|
||||
This example shows some of the most useful `@Component` configuration options:
|
||||
|
||||
@ -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" linenums="false" header="src/app/hero-list.component.html (binding)" region="binding"></code-example>
|
||||
<code-example path="architecture/src/app/hero-list.component.1.html" 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.
|
||||
@ -101,7 +101,7 @@ 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" linenums="false" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
|
||||
<code-example path="architecture/src/app/hero-detail.component.html" 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,
|
||||
@ -164,7 +164,7 @@ Just as for components, the metadata for a directive associates the decorated cl
|
||||
*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" linenums="false" header="src/app/hero-list.component.html (structural)" region="structural"></code-example>
|
||||
<code-example path="architecture/src/app/hero-list.component.1.html" 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" linenums="false" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
|
||||
<code-example path="architecture/src/app/hero-detail.component.html" 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))
|
||||
|
@ -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" linenums="false"></code-example>
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="module" header="src/app/app.module.ts"></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" linenums="false"></code-example>
|
||||
<code-example path="architecture/src/app/app.module.ts" region="imports"></code-example>
|
||||
|
||||
<code-example path="architecture/src/app/app.module.ts" region="export" linenums="false"></code-example>
|
||||
<code-example path="architecture/src/app/app.module.ts" region="export"></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" linenums="false"></code-example>
|
||||
<code-example path="architecture/src/app/app.component.ts" region="import"></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" linenums="false"></code-example>
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module"></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" linenums="false"></code-example>
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports"></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.
|
||||
|
||||
|
@ -28,11 +28,11 @@ 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" linenums="false" header="src/app/logger.service.ts (class)" region="class"></code-example>
|
||||
<code-example path="architecture/src/app/logger.service.ts" 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" linenums="false" header="src/app/hero.service.ts (class)" region="class"></code-example>
|
||||
<code-example path="architecture/src/app/hero.service.ts" header="src/app/hero.service.ts (class)" region="class"></code-example>
|
||||
|
||||
## Dependency injection (DI)
|
||||
|
||||
@ -62,7 +62,7 @@ A dependency doesn't have to be a service—it could be a function, for exam
|
||||
|
||||
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" linenums="false" header="src/app/hero-list.component.ts (constructor)" region="ctor"></code-example>
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" 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.
|
||||
|
||||
@ -111,6 +111,6 @@ or in the `@NgModule()` or `@Component()` metadata
|
||||
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" linenums="false" header="src/app/hero-list.component.ts (component providers)" region="providers"></code-example>
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" 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.
|
||||
|
@ -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" linenums="false" header="src/app/app.component.html (applied)" region="applied"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" 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" linenums="false" header="src/app/app.component.avoid.html (unsupported)" region="unsupported"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.avoid.html" header="src/app/app.component.avoid.html (unsupported)" region="unsupported"></code-example>
|
||||
|
||||
### Write the directive code
|
||||
|
||||
@ -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" linenums="false" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" 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" linenums="false" header="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" 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" linenums="false" header="src/app/highlight.directive.ts (constructor)" region="ctor"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" 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" linenums="false" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" 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" linenums="false" header="src/app/highlight.directive.ts (highlightColor)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" 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" linenums="false" header="src/app/app.component.html (excerpt)" region="color-1"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" 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" linenums="false" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.1.ts" 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" linenums="false" header="src/app/app.component.html (excerpt)" region="color-2"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" 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" linenums="false" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.html" 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" linenums="false" 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" 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" linenums="false" header="src/app/highlight.directive.ts (color property with alias)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" 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" linenums="false" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.html" 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" linenums="false" header="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" 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" linenums="false" header="src/app/highlight.directive.ts (excerpt)"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" 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" linenums="false" header="src/app/app.component.html (v2)" region="v2"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.html" 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" linenums="false" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.ts" 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" linenums="false" header="src/app/highlight.directive.ts (defaultColor)" region="defaultColor"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" 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" linenums="false" header="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" 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" linenums="false" header="src/app/app.component.html (defaultColor)" region="defaultColor"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.html" 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" linenums="false" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" 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" linenums="false" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" 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" linenums="false" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.html" 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.
|
||||
|
@ -106,19 +106,16 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="bootstrapping/src/app/item.directive.ts" region="directive" header="src/app/item.directive.ts"></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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="bootstrapping/src/app/app.module.ts" region="directive-import" header="src/app/app.module.ts"></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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="bootstrapping/src/app/app.module.ts" region="declarations" header="src/app/app.module.ts"></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.
|
||||
|
@ -53,7 +53,7 @@ Angular supports most recent browsers. This includes the following specific vers
|
||||
IE
|
||||
</td>
|
||||
<td>
|
||||
11<br>10<br>9
|
||||
11, 10, 9
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -89,7 +89,7 @@ Angular supports most recent browsers. This includes the following specific vers
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Nougat (7.0)<br>Marshmallow (6.0)<br>Lollipop (5.0, 5.1)<br>KitKat (4.4)
|
||||
Nougat (7.0), Marshmallow (6.0), Lollipop (5.0, 5.1), KitKat (4.4)
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -127,25 +127,27 @@ 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
|
||||
## Enabling polyfills with CLI projects
|
||||
|
||||
[Angular CLI](cli) users enable polyfills through the `src/polyfills.ts` file that
|
||||
the CLI created with your project.
|
||||
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).
|
||||
|
||||
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 (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.
|
||||
* 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.
|
||||
|
||||
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):
|
||||
* 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):
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
# note that the web-animations-js polyfill is only here as an example
|
||||
# it isn't a strict requirement of Angular anymore (more below)
|
||||
# install the optional web animations polyfill
|
||||
npm install --save web-animations-js
|
||||
</code-example>
|
||||
|
||||
Then open the `polyfills.ts` file and un-comment the corresponding `import` statement as in the following 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.
|
||||
|
||||
<code-example header="src/polyfills.ts">
|
||||
/**
|
||||
@ -155,23 +157,14 @@ Then open the `polyfills.ts` file and un-comment the corresponding `import` stat
|
||||
import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
</code-example>
|
||||
|
||||
If you can't find the polyfill you want in `polyfills.ts`,
|
||||
add it yourself, following the same pattern:
|
||||
If the polyfill you want is not already in `polyfills.ts` file, add the `import` statement by hand.
|
||||
|
||||
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">
|
||||
@ -189,26 +182,13 @@ These are the polyfills required to run an Angular application on each supported
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
<td>
|
||||
Chrome, Firefox, Edge, Safari 9+
|
||||
Chrome, Firefox, Edge, <br>
|
||||
Safari, Android, IE10+
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
[ES7/reflect](guide/browser-support#core-es7-reflect) (JIT only)
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
<td>
|
||||
Safari 7 & 8, IE10 & 11, Android 4.1+
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
[ES6](guide/browser-support#core-es6)
|
||||
[ES2015](guide/browser-support#core-es6)
|
||||
|
||||
</td>
|
||||
|
||||
@ -222,7 +202,7 @@ These are the polyfills required to run an Angular application on each supported
|
||||
|
||||
<td>
|
||||
|
||||
[ES6<br>classList](guide/browser-support#classlist)
|
||||
ES2015<br>[classList](guide/browser-support#classlist)
|
||||
|
||||
</td>
|
||||
|
||||
@ -235,12 +215,6 @@ 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">
|
||||
@ -263,31 +237,8 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
<td>
|
||||
|
||||
[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).
|
||||
[AnimationBuilder](api/animations/AnimationBuilder).
|
||||
(Standard animation support does not require polyfills.)
|
||||
|
||||
</td>
|
||||
|
||||
@ -298,8 +249,9 @@ Here are the features which may require additional polyfills:
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<p>If AnimationBuilder is used then the polyfill will enable scrubbing
|
||||
support for IE/Edge and Safari (Chrome and Firefox support this natively).</p>
|
||||
<p>If AnimationBuilder is used, enables scrubbing
|
||||
support for IE/Edge and Safari.
|
||||
(Chrome and Firefox support this natively).</p>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@ -308,15 +260,10 @@ Here are the features which 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>
|
||||
@ -337,9 +284,7 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
<td>
|
||||
|
||||
[NgClass](api/common/NgClass)
|
||||
|
||||
on SVG elements
|
||||
[NgClass](api/common/NgClass) on SVG elements
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -358,9 +303,7 @@ Here are the features which 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>
|
||||
@ -383,9 +326,8 @@ Here are the features which 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>
|
||||
@ -404,8 +346,9 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
|
||||
|
||||
### Suggested polyfills ##
|
||||
Below are the polyfills which are used to test the framework itself. They are a good starting point for an application.
|
||||
### Suggested polyfills
|
||||
|
||||
The following polyfills are used to test the framework itself. They are a good starting point for an application.
|
||||
|
||||
|
||||
<table>
|
||||
@ -426,24 +369,6 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
|
||||
</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>
|
||||
@ -466,7 +391,7 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
|
||||
<td>
|
||||
|
||||
<a id='core-es6' href="https://github.com/zloirock/core-js">ES6</a>
|
||||
<a id='core-es6' href="https://github.com/zloirock/core-js">ES2015</a>
|
||||
|
||||
</td>
|
||||
|
||||
@ -595,11 +520,14 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
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, you should add your polyfill scripts directly to the host web page (`index.html`), perhaps like this.
|
||||
If you are not using the CLI, add your polyfill scripts directly to the host web page (`index.html`).
|
||||
|
||||
<code-example header="src/index.html">
|
||||
For example:
|
||||
|
||||
<code-example header="src/index.html" language="html">
|
||||
<!-- pre-zone polyfills -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
<script src="node_modules/web-animations-js/web-animations.min.js"></script>
|
||||
|
@ -185,8 +185,7 @@ 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 has dependencies that should be injected into the constructor when the dependency injector is creating an instance of this class.
|
||||
</p>
|
||||
<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>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
@ -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 format="." language="json" linenums="false">
|
||||
<code-example language="json">
|
||||
|
||||
{
|
||||
"$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 format="." language="json" linenums="false">
|
||||
<code-example language="json">
|
||||
|
||||
{
|
||||
"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 format="." language="json" linenums="false">
|
||||
<code-example language="json">
|
||||
|
||||
{
|
||||
"name": "@example/command-runner",
|
||||
@ -257,7 +257,7 @@ The first part of this is the package name (resolved using node resolution), an
|
||||
|
||||
Using one of our `options` is very straightforward, we did this in the previous section when we accessed `options.command`.
|
||||
|
||||
<code-example format="." language="typescript" linenums="false">
|
||||
<code-example language="typescript">
|
||||
context.reportStatus(`Executing "${options.command}"...`);
|
||||
const child = childProcess.spawn(options.command, options.args, { stdio: 'pipe' });
|
||||
|
||||
@ -274,7 +274,7 @@ 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 format="." language="json" linenums="false">
|
||||
<code-example language="json">
|
||||
{
|
||||
"myApp": {
|
||||
...
|
||||
@ -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 format="." language="json" linenums="false">
|
||||
<code-example language="json">
|
||||
|
||||
{
|
||||
// ...
|
||||
@ -413,7 +413,7 @@ 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 format="." language="json" linenums="false">
|
||||
<code-example language="json">
|
||||
|
||||
{
|
||||
"projects": {
|
||||
@ -495,7 +495,7 @@ Use integration testing for your builder, so that you can use the Architect sche
|
||||
Here’s 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 format="." language="typescript" linenums="false">
|
||||
<code-example language="typescript">
|
||||
|
||||
import { Architect, ArchitectHost } from '@angular-devkit/architect';
|
||||
import { TestingArchitectHost } from '@angular-devkit/architect/testing';
|
||||
|
@ -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" linenums="false"></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"></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" linenums="false"></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"></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" linenums="false"></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"></code-example>
|
||||
|
||||
The animation does the following:
|
||||
|
||||
|
@ -21,8 +21,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="component-styles/src/app/hero-app.component.ts" header="src/app/hero-app.component.ts"></code-example>
|
||||
|
||||
## Style scope
|
||||
|
||||
@ -71,8 +70,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="host" header="src/app/hero-details.component.css"></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
|
||||
@ -83,8 +81,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" header="src/app/hero-details.component.css"></code-example>
|
||||
|
||||
### :host-context
|
||||
|
||||
@ -99,8 +96,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" header="src/app/hero-details.component.css"></code-example>
|
||||
|
||||
### (deprecated) `/deep/`, `>>>`, and `::ng-deep`
|
||||
|
||||
@ -115,9 +111,7 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" header="src/app/hero-details.component.css"></code-example>
|
||||
|
||||
The `/deep/` combinator also has the aliases `>>>`, and `::ng-deep`.
|
||||
|
||||
@ -304,8 +298,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" header="src/app/quest-summary.component.ts"></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
|
||||
|
@ -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 format="." language="bash">
|
||||
<code-example 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 format="." language="bash">
|
||||
<code-example 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 [Schematics for
|
||||
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 format="." language="bash">
|
||||
<code-example 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 format="." language="bash">
|
||||
<code-example 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 format="." language="bash">
|
||||
<code-example language="bash">
|
||||
ng build my-lib --watch
|
||||
</code-example>
|
||||
|
@ -21,18 +21,14 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/app.component.ts" region="ctor" header="src/app/app.component.ts"></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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/user-context.service.ts" region="injectables" header="user-context.service.ts (injection)"></code-example>
|
||||
|
||||
|
||||
When Angular creates `AppComponent`, the DI framework creates an instance of `LoggerService` and starts to create `UserContextService`.
|
||||
@ -185,17 +181,13 @@ 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" linenums="false">
|
||||
|
||||
</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"></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)" linenums="false">
|
||||
|
||||
</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)"></code-example>
|
||||
|
||||
The result is shown below, with the hero's telephone number from `HeroContactComponent` projected above the hero description.
|
||||
|
||||
@ -212,9 +204,7 @@ 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" linenums="false">
|
||||
|
||||
</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"></code-example>
|
||||
|
||||
The `@Host()` function decorating the `heroCache` constructor property ensures that
|
||||
you get a reference to the cache service from the parent `HeroBioComponent`.
|
||||
@ -299,9 +289,7 @@ 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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/app.component.html" region="highlight" header="src/app/app.component.html (highlight)"></code-example>
|
||||
|
||||
|
||||
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
|
||||
@ -325,9 +313,7 @@ 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)" linenums="false">
|
||||
|
||||
</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)"></code-example>
|
||||
|
||||
|
||||
Angular asks the injector for the service associated with `LoggerService`
|
||||
@ -386,9 +372,7 @@ 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" linenums="false">
|
||||
|
||||
</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"></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.
|
||||
@ -427,9 +411,7 @@ 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" linenums="false">
|
||||
|
||||
</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"></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.
|
||||
@ -448,9 +430,7 @@ 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" linenums="false">
|
||||
|
||||
</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"></code-example>
|
||||
|
||||
{@a useexisting}
|
||||
|
||||
@ -472,15 +452,11 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="src/app/minimal-logger.service.ts"></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)" linenums="false">
|
||||
|
||||
</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)"></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.
|
||||
|
||||
@ -532,9 +508,7 @@ 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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/runners-up.ts" region="factory-synopsis" header="runners-up.ts (excerpt)"></code-example>
|
||||
|
||||
The provider factory function (returned by `runnersUpFactory()`) returns the actual dependency object,
|
||||
the string of names.
|
||||
@ -578,9 +552,7 @@ 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" linenums="false">
|
||||
|
||||
</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"></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`.
|
||||
@ -606,9 +578,7 @@ 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" linenums="false">
|
||||
|
||||
</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"></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.
|
||||
@ -635,15 +605,11 @@ 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" linenums="false">
|
||||
|
||||
</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"></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" linenums="false">
|
||||
|
||||
</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"></code-example>
|
||||
|
||||
The type parameter, while optional, conveys the dependency's type to developers and tooling.
|
||||
The token description is another developer aid.
|
||||
@ -733,9 +699,7 @@ 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)" linenums="false">
|
||||
|
||||
</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)"></code-example>
|
||||
|
||||
|
||||
<!--- Waiting for good examples
|
||||
|
@ -40,18 +40,14 @@ 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)" linenums="false">
|
||||
|
||||
</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)"></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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="cathy" header="parent-finder.component.ts (CathyComponent)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -98,17 +94,13 @@ 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)" linenums="false">
|
||||
|
||||
</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)"></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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="craig" header="parent-finder.component.ts (CraigComponent)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -138,9 +130,7 @@ 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)" linenums="false">
|
||||
|
||||
</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)"></code-example>
|
||||
|
||||
|
||||
[Parent](#parent-token) is the provider's class interface token.
|
||||
@ -149,9 +139,7 @@ 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)" linenums="false">
|
||||
|
||||
</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)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -177,9 +165,7 @@ 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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="barry" header="parent-finder.component.ts (BarryComponent)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -229,9 +215,7 @@ 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)" linenums="false">
|
||||
|
||||
</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)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -241,9 +225,7 @@ 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)" linenums="false">
|
||||
|
||||
</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)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -251,9 +233,7 @@ 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)" linenums="false">
|
||||
|
||||
</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)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -277,21 +257,15 @@ 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" linenums="false">
|
||||
|
||||
</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"></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" linenums="false">
|
||||
|
||||
</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"></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" linenums="false">
|
||||
|
||||
</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"></code-example>
|
||||
|
||||
|
||||
You can do better. The current version of the helper function can only alias the `Parent` class interface.
|
||||
@ -299,14 +273,10 @@ 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" linenums="false">
|
||||
|
||||
</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"></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" linenums="false">
|
||||
|
||||
</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"></code-example>
|
||||
|
||||
|
@ -68,13 +68,11 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger"></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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5"></code-example>
|
||||
|
||||
{@a aliased-class-providers}
|
||||
|
||||
@ -92,13 +90,11 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a"></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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b"></code-example>
|
||||
|
||||
{@a value-provider}
|
||||
|
||||
@ -110,13 +106,11 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger"></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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7"></code-example>
|
||||
|
||||
{@a non-class-dependencies}
|
||||
|
||||
@ -130,8 +124,7 @@ 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)" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/app.config.ts" region="config" header="src/app/app.config.ts (excerpt)"></code-example>
|
||||
|
||||
{@a interface-not-valid-token}
|
||||
|
||||
@ -141,11 +134,9 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<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="provider-9-ctor-interface" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -163,22 +154,19 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/app.config.ts" region="token" header="src/app/app.config.ts"></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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9"></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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -215,22 +203,19 @@ 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)" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" header="src/app/heroes/hero.service.ts (excerpt)"></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)" linenums="false">
|
||||
</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)"></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)" linenums="false">
|
||||
</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)"></code-example>
|
||||
|
||||
* The `useFactory` field tells Angular that the provider is a factory function whose implementation is `heroServiceFactory`.
|
||||
|
||||
@ -322,13 +307,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" linenums="false"> </code-example>
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/service-and-module.ts" header="src/app/tree-shaking/service-and-modules.ts"></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" linenums="false"> </code-example>
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/app.module.ts" header="src/app/tree-shaking/app.modules.ts"></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.
|
||||
|
||||
@ -340,11 +325,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" linenums="false"> </code-example>
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" header="src/app/tree-shaking/service.ts"></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" linenums="false"> </code-example>
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/service.0.ts" header="src/app/tree-shaking/service.0.ts"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
@ -196,8 +196,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" header="src/app/test.component.ts"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -259,8 +258,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" header="src/app/injector.component.ts"></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,
|
||||
@ -287,8 +285,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor"></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
|
||||
|
@ -152,17 +152,14 @@ 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 format=".">
|
||||
|
||||
<code-example>
|
||||
RewriteEngine On
|
||||
# If an existing asset or directory is requested go to it as it is
|
||||
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
|
||||
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
|
||||
RewriteRule ^ - [L]
|
||||
|
||||
RewriteRule ^ - [L]<br>
|
||||
# If the requested resource doesn't exist, use index.html
|
||||
RewriteRule ^ /index.html
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
@ -170,18 +167,15 @@ The list is by no means exhaustive, but should provide you with a good starting
|
||||
[Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps),
|
||||
modified to serve `index.html`:
|
||||
|
||||
<code-example format=".">
|
||||
|
||||
try_files $uri $uri/ /index.html;
|
||||
|
||||
</code-example>
|
||||
```
|
||||
try_files $uri $uri/ /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='.' linenums="false">
|
||||
|
||||
<code-example format='.' language="xml">
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
@ -196,7 +190,6 @@ modified to serve `index.html`:
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
@ -214,13 +207,11 @@ 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 format=".">
|
||||
|
||||
<code-example language="json">
|
||||
"rewrites": [ {
|
||||
"source": "**",
|
||||
"destination": "/index.html"
|
||||
} ]
|
||||
|
||||
</code-example>
|
||||
|
||||
{@a cors}
|
||||
@ -412,7 +403,7 @@ Differential loading, which is supported by default in Angular CLI version 8 and
|
||||
|
||||
Differential loading is a strategy where the CLI builds two separate bundles as part of your deployed application.
|
||||
|
||||
* The first bundle contains modern ES1015 syntax, takes advantage of built-in support in modern browsers, ships less polyfills, and results in a smaller bundle size.
|
||||
* The first bundle contains modern ES2015 syntax, takes advantage of built-in support in modern browsers, ships less polyfills, and results in a smaller bundle size.
|
||||
|
||||
* The second bundle contains code in the old ES5 syntax, along with all necessary polyfills. This results in a larger bundle size, but supports older browsers.
|
||||
|
||||
@ -445,24 +436,20 @@ 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" format="." linenums="false">
|
||||
|
||||
<!-- ... -->
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
<script src="runtime-es2015.js" type="module"></script>
|
||||
<script src="runtime-es5.js" nomodule></script>
|
||||
<script src="polyfills-es2015.js" type="module"></script>
|
||||
<script src="polyfills-es5.js" nomodule></script>
|
||||
<script src="styles-es2015.js" type="module"></script>
|
||||
<script src="styles-es5.js" nomodule></script>
|
||||
<script src="vendor-es2015.js" type="module"></script>
|
||||
<script src="vendor-es5.js" nomodule></script>
|
||||
<script src="main-es2015.js" type="module"></script>
|
||||
<script src="main-es5.js" nomodule></script>
|
||||
</body>
|
||||
<!-- ... -->
|
||||
|
||||
<code-example language="html">
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
<script src="runtime-es2015.js" type="module"></script>
|
||||
<script src="runtime-es5.js" nomodule></script>
|
||||
<script src="polyfills-es2015.js" type="module"></script>
|
||||
<script src="polyfills-es5.js" nomodule></script>
|
||||
<script src="styles-es2015.js" type="module"></script>
|
||||
<script src="styles-es5.js" nomodule></script>
|
||||
<script src="vendor-es2015.js" type="module"></script>
|
||||
<script src="vendor-es5.js" nomodule></script>
|
||||
<script src="main-es2015.js" type="module"></script>
|
||||
<script src="main-es5.js" nomodule></script>
|
||||
</body>
|
||||
</code-example>
|
||||
|
||||
Each script tag has a `type="module"` or `nomodule` attribute. Browsers with native support for ES modules only load the scripts with the `module` type attribute and ignore scripts with the `nomodule` attribute. Legacy browsers only load the scripts with the `nomodule` attribute, and ignore the script tags with the `module` type that load ES modules.
|
||||
@ -492,7 +479,7 @@ not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||
|
||||
The `tsconfig.json` looks like this:
|
||||
|
||||
<code-example language="json" format="." linenums="false">
|
||||
<code-example language="json">
|
||||
|
||||
{
|
||||
"compileOnSave": false,
|
||||
@ -523,7 +510,7 @@ By default, legacy browsers such as IE 9-11 are ignored, and the compilation tar
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
To see which browsers are supported with the above configuration, see which settings meet to your browser support requirements, see the [Browserslist compatibility page](https://browserl.ist/?q=%3E+0.5%25%2C+last+2+versions%2C+Firefox+ESR%2C+Chrome+41%2C+not+dead%2C+not+IE+9-11).
|
||||
To see which browsers are supported with the above configuration, see which settings meet to your browser support requirements, see the [Browserslist compatibility page](https://browserl.ist/?q=%3E+0.5%25%2C+last+2+versions%2C+Firefox+ESR%2C+not+dead%2C+not+IE+9-11).
|
||||
|
||||
</div>
|
||||
|
||||
@ -563,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" format="." linenums="false">
|
||||
<code-example language="json">
|
||||
|
||||
{
|
||||
"extends": "./tsconfig.app.json",
|
||||
@ -576,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" format="." linenums="false">
|
||||
<code-example language="json">
|
||||
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
@ -623,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" format="." linenums="false">
|
||||
<code-example language="json">
|
||||
|
||||
{
|
||||
"extends": "./tsconfig.spec.json",
|
||||
@ -634,7 +621,7 @@ Create a new file, `tsconfig-es5.spec.json` next to `tsconfig.spec.json` with th
|
||||
|
||||
</code-example>
|
||||
|
||||
<code-example language="json" format="." linenums="false">
|
||||
<code-example language="json">
|
||||
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
@ -660,9 +647,9 @@ ng test --configuration es5
|
||||
|
||||
### Configuring the e2e command
|
||||
|
||||
Create an ES5 serve configuration as explained above (link to the above serve section), and configuration an ES5 configuration for the E2E target.
|
||||
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" format="." linenums="false">
|
||||
<code-example language="json">
|
||||
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
|
@ -465,3 +465,99 @@ 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 aren’t 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.
|
||||
|
||||
### I’m 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()`)|
|
||||
|
@ -41,9 +41,7 @@ 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>
|
||||
|
||||
|
||||
|
||||
@ -53,9 +51,7 @@ The template displays the two component properties using double curly brace
|
||||
interpolation:
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.1.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
|
||||
|
||||
</code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -97,9 +93,7 @@ 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" linenums="false" header="src/index.html (body)" region="body">
|
||||
|
||||
</code-example>
|
||||
<code-example path="displaying-data/src/index.html" header="src/index.html (body)" region="body"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -148,9 +142,7 @@ 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" linenums="false" region="class">
|
||||
|
||||
</code-example>
|
||||
<code-example path="displaying-data/src/app/app-ctor.component.1.ts" region="class"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -163,9 +155,7 @@ 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" linenums="false" header="src/app/app.component.ts (class)" region="class">
|
||||
|
||||
</code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -173,9 +163,7 @@ 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" linenums="false" header="src/app/app.component.ts (template)" region="template">
|
||||
|
||||
</code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -184,9 +172,7 @@ 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" linenums="false" header="src/app/app.component.ts (li)" region="li">
|
||||
|
||||
</code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (li)" region="li"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -251,9 +237,7 @@ 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" linenums="false" header="src/app/hero.ts">
|
||||
|
||||
</code-example>
|
||||
<code-example path="displaying-data/src/app/hero.ts" header="src/app/hero.ts"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -265,9 +249,7 @@ 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" linenums="false" header="src/app/hero.ts (id)" region="id">
|
||||
|
||||
</code-example>
|
||||
<code-example path="displaying-data/src/app/hero.ts" header="src/app/hero.ts (id)" region="id"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -285,9 +267,7 @@ 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" linenums="false" header="src/app/app.component.ts (heroes)" region="heroes">
|
||||
|
||||
</code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.3.ts" header="src/app/app.component.ts (heroes)" region="heroes"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -296,9 +276,7 @@ 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" linenums="false" header="src/app/app.component.ts (template)" region="template">
|
||||
|
||||
</code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.3.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -316,9 +294,7 @@ 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" linenums="false" header="src/app/app.component.ts (message)" region="message">
|
||||
|
||||
</code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.ts" header="src/app/app.component.ts (message)" region="message"></code-example>
|
||||
|
||||
|
||||
|
||||
|
@ -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 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.
|
||||
* `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.
|
||||
|
||||
* `class`- code snippets can be styled with the CSS classes `no-box`, `code-shell`, and `avoid`.
|
||||
|
||||
@ -465,8 +465,6 @@ 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.
|
||||
@ -496,18 +494,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, line numbers are enabled only when code for a tab pane has greater than 10 lines of code.
|
||||
* `linenums`: The value can be `true`, `false` or a number indicating the starting line number. If not specified, it defaults to `false`.
|
||||
|
||||
#### 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, line numbers are enabled only when the number of lines of code are greater than 10.
|
||||
* `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`.
|
||||
|
||||
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="false">
|
||||
<code-tabs linenums="true">
|
||||
<code-pane
|
||||
header="app.component.html"
|
||||
path="docs-style-guide/src/app/app.component.html">
|
||||
@ -515,7 +513,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="true">
|
||||
linenums="false">
|
||||
</code-pane>
|
||||
<code-pane
|
||||
header="app.component.css (heroes)"
|
||||
@ -530,11 +528,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 disables numbering for all panes.
|
||||
The `linenums` attribute in the second pane restores line numbering for _itself only_.
|
||||
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_.
|
||||
|
||||
```html
|
||||
<code-tabs linenums="false">
|
||||
<code-tabs linenums="true">
|
||||
<code-pane
|
||||
header="app.component.html"
|
||||
path="docs-style-guide/src/app/app.component.html">
|
||||
@ -542,7 +540,7 @@ The `linenums` attribute in the second pane restores line numbering for _itself
|
||||
<code-pane
|
||||
header="app.component.ts"
|
||||
path="docs-style-guide/src/app/app.component.ts"
|
||||
linenums="true">
|
||||
linenums="false">
|
||||
</code-pane>
|
||||
<code-pane
|
||||
header="app.component.css (heroes)"
|
||||
@ -667,7 +665,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 linenums="false">
|
||||
<code-example>
|
||||
// #docplaster
|
||||
...
|
||||
// #docregion class, class-skeleton
|
||||
|
@ -35,9 +35,7 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dynamic-component-loader/src/app/ad.directive.ts" header="src/app/ad.directive.ts"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -62,9 +60,7 @@ 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)" linenums="false">
|
||||
|
||||
</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)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -91,9 +87,7 @@ 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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="class" header="src/app/ad-banner.component.ts (excerpt)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -150,9 +144,7 @@ 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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dynamic-component-loader/src/app/app.module.ts" region="entry-components" header="src/app/app.module.ts (entry components)"></code-example>
|
||||
|
||||
|
||||
|
||||
|
@ -77,18 +77,14 @@ appropriate controls dynamically.
|
||||
via the `type` property.
|
||||
|
||||
|
||||
<code-example path="dynamic-form/src/app/question-textbox.ts" header="src/app/question-textbox.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dynamic-form/src/app/question-textbox.ts" header="src/app/question-textbox.ts"></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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dynamic-form/src/app/question-dropdown.ts" header="src/app/question-dropdown.ts"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -97,9 +93,7 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dynamic-form/src/app/question-control.service.ts" header="src/app/question-control.service.ts"></code-example>
|
||||
|
||||
{@a form-component}
|
||||
|
||||
|
@ -1,12 +1,5 @@
|
||||
# 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 you’re 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">
|
||||
|
@ -1,12 +1,6 @@
|
||||
# Feature Modules
|
||||
|
||||
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).
|
||||
Feature modules are NgModules for the purpose of organizing code.
|
||||
|
||||
For the final sample app with a feature module that this page describes,
|
||||
see the <live-example></live-example>.
|
||||
@ -72,8 +66,7 @@ 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" linenums="false">
|
||||
</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"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -83,8 +76,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="feature-modules/src/app/app.module.ts" region="app-module" header="src/app/app.module.ts"></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 don’t expose their components.
|
||||
@ -94,21 +86,18 @@ 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" linenums="false">
|
||||
</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"></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" linenums="false">
|
||||
</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"></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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="feature-modules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html"></code-example>
|
||||
|
||||
|
||||
Now, in addition to the title that renders by default, the `CustomerDashboardComponent` template renders too:
|
||||
|
@ -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" linenums="false">
|
||||
<code-example language="bash">
|
||||
ng new <my-project>
|
||||
</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" linenums="false">
|
||||
<code-example language="none">
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
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" linenums="false">
|
||||
<code-example language="none">
|
||||
my-workspace/
|
||||
... (workspace-wide config files)
|
||||
projects/ (generated applications and libraries)
|
||||
|
@ -29,9 +29,7 @@ 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)" linenums="false">
|
||||
|
||||
</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)"></code-example>
|
||||
|
||||
|
||||
Note the following:
|
||||
@ -92,8 +90,7 @@ built-in validators—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)" linenums="false">
|
||||
</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)"></code-example>
|
||||
|
||||
Note that:
|
||||
|
||||
@ -106,8 +103,7 @@ 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)" linenums="false">
|
||||
</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)"></code-example>
|
||||
|
||||
Key takeaways:
|
||||
|
||||
@ -125,8 +121,7 @@ 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)" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" header="shared/forbidden-name.directive.ts (forbiddenNameValidator)"></code-example>
|
||||
|
||||
The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name and returns a validator function.
|
||||
|
||||
@ -148,8 +143,7 @@ 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)" linenums="false">
|
||||
</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)"></code-example>
|
||||
|
||||
### Adding to template-driven forms
|
||||
|
||||
@ -161,8 +155,7 @@ 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)" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" header="shared/forbidden-name.directive.ts (providers)"></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
|
||||
@ -173,9 +166,7 @@ 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)" linenums="false">
|
||||
|
||||
</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)"></code-example>
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -245,8 +236,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-validator" header="shared/identity-revealed.directive.ts"></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.
|
||||
|
||||
@ -255,8 +245,7 @@ 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" linenums="false">
|
||||
</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"></code-example>
|
||||
|
||||
Note that we check if:
|
||||
- the `FormGroup` has the cross validation error returned by the `identityRevealed` validator,
|
||||
@ -265,16 +254,13 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-directive" header="shared/identity-revealed.directive.ts"></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" linenums="false">
|
||||
</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"></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" linenums="false">
|
||||
</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"></code-example>
|
||||
|
||||
Note that we check if:
|
||||
- the form has the cross validation error returned by the `identityRevealed` validator,
|
||||
@ -313,7 +299,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" linenums="false"></code-example>
|
||||
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator"></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:
|
||||
|
||||
|
@ -109,9 +109,7 @@ 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.
|
||||
|
||||
@ -122,9 +120,7 @@ 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" linenums="false" region="SkyDog">
|
||||
|
||||
</code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="SkyDog"></code-example>
|
||||
|
||||
## Create a form component
|
||||
|
||||
@ -142,9 +138,7 @@ 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" linenums="false" 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" header="src/app/hero-form/hero-form.component.ts (v1)" region="v1"></code-example>
|
||||
|
||||
There’s nothing special about this component, nothing form-specific,
|
||||
nothing to distinguish it from any component you've written before.
|
||||
@ -176,9 +170,7 @@ 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">
|
||||
|
||||
@ -204,9 +196,7 @@ 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">
|
||||
|
||||
@ -221,9 +211,7 @@ 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.
|
||||
@ -259,9 +247,7 @@ 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" linenums="false" header="src/styles.css">
|
||||
|
||||
</code-example>
|
||||
<code-example path="forms/src/styles.1.css" header="src/styles.css"></code-example>
|
||||
|
||||
## Add powers with _*ngFor_
|
||||
|
||||
@ -274,9 +260,7 @@ 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" linenums="false" 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" 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;
|
||||
@ -307,9 +291,7 @@ 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" linenums="false" 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" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-1"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -325,9 +307,7 @@ 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" linenums="false" 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" 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.
|
||||
|
||||
@ -391,9 +371,7 @@ 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" linenums="false" 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" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModel-2"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -493,9 +471,7 @@ 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" linenums="false" 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" 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*:
|
||||
@ -535,15 +511,11 @@ 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" linenums="false" header="src/index.html (styles)" region="styles">
|
||||
|
||||
</code-example>
|
||||
<code-example path="forms/src/index.html" header="src/index.html (styles)" region="styles"></code-example>
|
||||
|
||||
## Show and hide validation error messages
|
||||
|
||||
@ -564,9 +536,7 @@ 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" linenums="false" 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" 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".
|
||||
@ -583,9 +553,7 @@ 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" linenums="false" 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" 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.
|
||||
@ -609,13 +577,9 @@ 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 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>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
|
||||
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.
|
||||
@ -634,9 +598,7 @@ 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.
|
||||
|
||||
@ -651,9 +613,7 @@ 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" linenums="false" 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" 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".
|
||||
@ -664,9 +624,7 @@ 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" linenums="false" 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" 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—although
|
||||
it doesn't do anything useful yet.
|
||||
@ -703,17 +661,13 @@ 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" linenums="false" 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" 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" linenums="false" 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" 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.
|
||||
@ -721,9 +675,7 @@ 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" linenums="false" 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" 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.
|
||||
|
@ -1,12 +1,5 @@
|
||||
# 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
|
||||
|
@ -454,10 +454,9 @@ A form of property [data binding](#data-binding) in which a [template expression
|
||||
That text can be concatenated with neighboring text before it is assigned to an element property
|
||||
or displayed between element tags, as in this example.
|
||||
|
||||
<code-example language="html" escape="html">
|
||||
<label>My current hero is {{hero.name}}</label>
|
||||
|
||||
</code-example>
|
||||
```html
|
||||
<label>My current hero is {{hero.name}}</label>
|
||||
```
|
||||
|
||||
|
||||
Read more about [interpolation](guide/template-syntax#interpolation) in [Template Syntax](guide/template-syntax).
|
||||
@ -749,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 format="." language="bash">
|
||||
<code-example language="bash">
|
||||
npm install -g @angular-devkit/schematics-cli
|
||||
</code-example>
|
||||
|
||||
@ -764,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" linenums="false" header="architecture/src/app/app.component.ts (import)" region="import">
|
||||
<code-example path="architecture/src/app/app.component.ts" header="architecture/src/app/app.component.ts (import)" region="import">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
@ -75,7 +75,7 @@ When an injectable class provides its own service to the `root` injector, the se
|
||||
|
||||
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" linenums="false"> </code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.0.ts" header="src/app/heroes/heroes.service.ts"></code-example>
|
||||
|
||||
This configuration tells Angular that the app's root injector is responsible for creating an
|
||||
instance of `HeroService` by invoking its constructor,
|
||||
@ -92,7 +92,7 @@ Instead of specifying the `root` injector, you can set `providedIn` to a specifi
|
||||
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" linenums="false"> </code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.4.ts" header="src/app/heroes/hero.service.ts"></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.
|
||||
@ -117,9 +117,7 @@ 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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/app.module.ts" region="providers" header="src/app/app.module.ts (providers)"></code-example>
|
||||
|
||||
|
||||
{@a register-providers-component}
|
||||
@ -132,8 +130,7 @@ by configuring the provider at the component level using the `@Component` metada
|
||||
|
||||
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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" header="src/app/heroes/heroes.component.ts"></code-example>
|
||||
|
||||
### Element injectors
|
||||
|
||||
@ -207,9 +204,7 @@ 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" linenums="false" header="src/app/villains-list.component.ts (metadata)" region="metadata">
|
||||
|
||||
</code-example>
|
||||
<code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" 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.
|
||||
@ -273,9 +268,7 @@ 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" linenums="false" 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" 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.
|
||||
|
@ -28,7 +28,7 @@ Most apps do so in the root `AppModule`.
|
||||
<code-example
|
||||
path="http/src/app/app.module.ts"
|
||||
region="sketch"
|
||||
header="app/app.module.ts (excerpt)" linenums="false">
|
||||
header="app/app.module.ts (excerpt)">
|
||||
</code-example>
|
||||
|
||||
Having imported `HttpClientModule` into the `AppModule`, you can inject the `HttpClient`
|
||||
@ -37,7 +37,7 @@ into an application class as shown in the following `ConfigService` example.
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="proto"
|
||||
header="app/config/config.service.ts (excerpt)" linenums="false">
|
||||
header="app/config/config.service.ts (excerpt)">
|
||||
</code-example>
|
||||
|
||||
## Getting JSON data
|
||||
@ -48,7 +48,7 @@ that specifies resource URLs.
|
||||
|
||||
<code-example
|
||||
path="http/src/assets/config.json"
|
||||
header="assets/config.json" linenums="false">
|
||||
header="assets/config.json">
|
||||
</code-example>
|
||||
|
||||
The `ConfigService` fetches this file with a `get()` method on `HttpClient`.
|
||||
@ -56,7 +56,7 @@ The `ConfigService` fetches this file with a `get()` method on `HttpClient`.
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="getConfig_1"
|
||||
header="app/config/config.service.ts (getConfig v.1)" linenums="false">
|
||||
header="app/config/config.service.ts (getConfig v.1)">
|
||||
</code-example>
|
||||
|
||||
A component, such as `ConfigComponent`, injects the `ConfigService` and calls
|
||||
@ -65,7 +65,7 @@ the `getConfig` service method.
|
||||
<code-example
|
||||
path="http/src/app/config/config.component.ts"
|
||||
region="v1"
|
||||
header="app/config/config.component.ts (showConfig v.1)" linenums="false">
|
||||
header="app/config/config.component.ts (showConfig v.1)">
|
||||
</code-example>
|
||||
|
||||
Because the service method returns an `Observable` of configuration data,
|
||||
@ -95,7 +95,7 @@ The subscribe callback above requires bracket notation to extract the data value
|
||||
|
||||
<code-example
|
||||
path="http/src/app/config/config.component.ts"
|
||||
region="v1_callback" linenums="false">
|
||||
region="v1_callback">
|
||||
</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.
|
||||
@ -108,7 +108,7 @@ First, define an interface with the correct shape:
|
||||
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="config-interface" linenums="false">
|
||||
region="config-interface">
|
||||
</code-example>
|
||||
|
||||
Then, specify that interface as the `HttpClient.get()` call's type parameter in the service:
|
||||
@ -116,7 +116,7 @@ Then, specify that interface as the `HttpClient.get()` call's type parameter in
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="getConfig_2"
|
||||
header="app/config/config.service.ts (getConfig v.2)" linenums="false">
|
||||
header="app/config/config.service.ts (getConfig v.2)">
|
||||
</code-example>
|
||||
|
||||
The callback in the updated component method receives a typed data object, which is
|
||||
@ -125,7 +125,7 @@ easier and safer to consume:
|
||||
<code-example
|
||||
path="http/src/app/config/config.component.ts"
|
||||
region="v2"
|
||||
header="app/config/config.component.ts (showConfig v.2)" linenums="false">
|
||||
header="app/config/config.component.ts (showConfig v.2)">
|
||||
</code-example>
|
||||
|
||||
### Reading the full response
|
||||
@ -136,7 +136,7 @@ Tell `HttpClient` that you want the full response with the `observe` option:
|
||||
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="getConfigResponse" linenums="false">
|
||||
region="getConfigResponse">
|
||||
</code-example>
|
||||
|
||||
Now `HttpClient.get()` returns an `Observable` of typed `HttpResponse` rather than just the JSON data.
|
||||
@ -147,7 +147,7 @@ The component's `showConfigResponse()` method displays the response headers as w
|
||||
path="http/src/app/config/config.component.ts"
|
||||
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.
|
||||
@ -162,7 +162,7 @@ You _could_ handle in the component by adding a second callback to the `.subscri
|
||||
path="http/src/app/config/config.component.ts"
|
||||
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.
|
||||
@ -188,7 +188,7 @@ You might first devise an error handler like this one:
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="handleError"
|
||||
header="app/config/config.service.ts (handleError)" linenums="false">
|
||||
header="app/config/config.service.ts (handleError)">
|
||||
</code-example>
|
||||
|
||||
Notice that this handler returns an RxJS [`ErrorObservable`](#rxjs) with a user-friendly error message.
|
||||
@ -201,7 +201,7 @@ and _pipe them through_ to the error handler.
|
||||
<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)" linenums="false">
|
||||
header="app/config/config.service.ts (getConfig v.3 with error handler)">
|
||||
</code-example>
|
||||
|
||||
### `retry()`
|
||||
@ -218,7 +218,7 @@ _Pipe_ it onto the `HttpClient` method result just before the error handler.
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="getConfig"
|
||||
header="app/config/config.service.ts (getConfig with retry)" linenums="false">
|
||||
header="app/config/config.service.ts (getConfig with retry)">
|
||||
</code-example>
|
||||
|
||||
{@a rxjs}
|
||||
@ -239,7 +239,7 @@ If you're following along with these code snippets, note that you must import th
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="rxjs-imports"
|
||||
header="app/config/config.service.ts (RxJS imports)" linenums="false">
|
||||
header="app/config/config.service.ts (RxJS imports)">
|
||||
</code-example>
|
||||
|
||||
## Requesting non-JSON data
|
||||
@ -252,7 +252,7 @@ as an `Observable<string>`.
|
||||
<code-example
|
||||
path="http/src/app/downloader/downloader.service.ts"
|
||||
region="getTextFile"
|
||||
header="app/downloader/downloader.service.ts (getTextFile)" linenums="false">
|
||||
header="app/downloader/downloader.service.ts (getTextFile)">
|
||||
</code-example>
|
||||
|
||||
`HttpClient.get()` returns a string rather than the default JSON because of the `responseType` option.
|
||||
@ -264,7 +264,7 @@ A `download()` method in the `DownloaderComponent` initiates the request by subs
|
||||
<code-example
|
||||
path="http/src/app/downloader/downloader.component.ts"
|
||||
region="download"
|
||||
header="app/downloader/downloader.component.ts (download)" linenums="false">
|
||||
header="app/downloader/downloader.component.ts (download)">
|
||||
</code-example>
|
||||
|
||||
## Sending data to the server
|
||||
@ -289,7 +289,7 @@ to every `HttpClient` save method.
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.service.ts"
|
||||
region="http-options"
|
||||
header="app/heroes/heroes.service.ts (httpOptions)" linenums="false">
|
||||
header="app/heroes/heroes.service.ts (httpOptions)">
|
||||
</code-example>
|
||||
|
||||
### Making a POST request
|
||||
@ -300,7 +300,7 @@ In the following example, the `HeroesService` posts when adding a hero to the da
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.service.ts"
|
||||
region="addHero"
|
||||
header="app/heroes/heroes.service.ts (addHero)" linenums="false">
|
||||
header="app/heroes/heroes.service.ts (addHero)">
|
||||
</code-example>
|
||||
|
||||
The `HttpClient.post()` method is similar to `get()` in that it has a type parameter
|
||||
@ -320,7 +320,7 @@ the `Observable` returned by this service method.
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.component.ts"
|
||||
region="add-hero-subscribe"
|
||||
header="app/heroes/heroes.component.ts (addHero)" linenums="false">
|
||||
header="app/heroes/heroes.component.ts (addHero)">
|
||||
</code-example>
|
||||
|
||||
When the server responds successfully with the newly added hero, the component adds
|
||||
@ -334,7 +334,7 @@ in the request URL.
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.service.ts"
|
||||
region="deleteHero"
|
||||
header="app/heroes/heroes.service.ts (deleteHero)" linenums="false">
|
||||
header="app/heroes/heroes.service.ts (deleteHero)">
|
||||
</code-example>
|
||||
|
||||
The `HeroesComponent` initiates the actual DELETE operation by subscribing to
|
||||
@ -343,7 +343,7 @@ the `Observable` returned by this service method.
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.component.ts"
|
||||
region="delete-hero-subscribe"
|
||||
header="app/heroes/heroes.component.ts (deleteHero)" linenums="false">
|
||||
header="app/heroes/heroes.component.ts (deleteHero)">
|
||||
</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.
|
||||
@ -357,7 +357,7 @@ You must call _subscribe()_ or nothing happens. Just calling `HeroesService.dele
|
||||
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.component.ts"
|
||||
region="delete-hero-no-subscribe" linenums="false">
|
||||
region="delete-hero-no-subscribe">
|
||||
</code-example>
|
||||
|
||||
{@a always-subscribe}
|
||||
@ -403,7 +403,7 @@ The following `HeroesService` example is just like the POST example.
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.service.ts"
|
||||
region="updateHero"
|
||||
header="app/heroes/heroes.service.ts (updateHero)" linenums="false">
|
||||
header="app/heroes/heroes.service.ts (updateHero)">
|
||||
</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()`
|
||||
@ -435,7 +435,7 @@ before making the next request.
|
||||
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.service.ts"
|
||||
region="update-headers" linenums="false">
|
||||
region="update-headers">
|
||||
</code-example>
|
||||
|
||||
#### URL Parameters
|
||||
@ -445,7 +445,7 @@ Here is a `searchHeroes` method that queries for heroes whose names contain the
|
||||
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.service.ts"
|
||||
region="searchHeroes" linenums="false">
|
||||
region="searchHeroes">
|
||||
</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`.
|
||||
@ -543,8 +543,7 @@ To implement an interceptor, declare a class that implements the `intercept()` m
|
||||
Here is a do-nothing _noop_ interceptor that simply passes the request through without touching it:
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/noop-interceptor.ts"
|
||||
header="app/http-interceptors/noop-interceptor.ts"
|
||||
linenums="false">
|
||||
header="app/http-interceptors/noop-interceptor.ts">
|
||||
</code-example>
|
||||
|
||||
The `intercept` method transforms a request into an `Observable` that eventually returns the HTTP response.
|
||||
@ -590,7 +589,7 @@ write the `NoopInterceptor` provider like this:
|
||||
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/index.ts"
|
||||
region="noop-provider" linenums="false">
|
||||
region="noop-provider">
|
||||
</code-example>
|
||||
|
||||
Note the `multi: true` option.
|
||||
@ -608,7 +607,7 @@ Consider creating a "barrel" file that gathers all the interceptor providers int
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/index.ts"
|
||||
region="interceptor-providers"
|
||||
header="app/http-interceptors/index.ts" linenums="false">
|
||||
header="app/http-interceptors/index.ts">
|
||||
</code-example>
|
||||
|
||||
Then import and add it to the `AppModule` _providers array_ like this:
|
||||
@ -616,7 +615,7 @@ Then import and add it to the `AppModule` _providers array_ like this:
|
||||
<code-example
|
||||
path="http/src/app/app.module.ts"
|
||||
region="interceptor-providers"
|
||||
header="app/app.module.ts (interceptor providers)" linenums="false">
|
||||
header="app/app.module.ts (interceptor providers)">
|
||||
</code-example>
|
||||
|
||||
As you create new interceptors, add them to the `httpInterceptorProviders` array and
|
||||
@ -672,7 +671,7 @@ You can clone and modify the request in a single step as in this example.
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/ensure-https-interceptor.ts"
|
||||
region="excerpt"
|
||||
header="app/http-interceptors/ensure-https-interceptor.ts (excerpt)" linenums="false">
|
||||
header="app/http-interceptors/ensure-https-interceptor.ts (excerpt)">
|
||||
</code-example>
|
||||
|
||||
The `clone()` method's hash argument allows you to mutate specific properties of the request while copying the others.
|
||||
@ -692,7 +691,7 @@ If you must mutate the request body, copy it first, change the copy,
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/trim-name-interceptor.ts"
|
||||
region="excerpt"
|
||||
header="app/http-interceptors/trim-name-interceptor.ts (excerpt)" linenums="false">
|
||||
header="app/http-interceptors/trim-name-interceptor.ts (excerpt)">
|
||||
</code-example>
|
||||
|
||||
##### Clearing the request body
|
||||
@ -768,7 +767,7 @@ The `CachingInterceptor` demonstrates this approach.
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/caching-interceptor.ts"
|
||||
region="v1"
|
||||
header="app/http-interceptors/caching-interceptor.ts)" linenums="false">
|
||||
header="app/http-interceptors/caching-interceptor.ts)">
|
||||
</code-example>
|
||||
|
||||
The `isCachable()` function determines if the request is cachable.
|
||||
@ -876,7 +875,7 @@ returns an `Observable` of `HttpEvents`, the same events processed by intercepto
|
||||
<code-example
|
||||
path="http/src/app/uploader/uploader.service.ts"
|
||||
region="upload-body"
|
||||
header="app/uploader/uploader.service.ts (upload body)" linenums="false">
|
||||
header="app/uploader/uploader.service.ts (upload body)">
|
||||
</code-example>
|
||||
|
||||
The `getEventMessage` method interprets each type of `HttpEvent` in the event stream.
|
||||
@ -884,7 +883,7 @@ The `getEventMessage` method interprets each type of `HttpEvent` in the event st
|
||||
<code-example
|
||||
path="http/src/app/uploader/uploader.service.ts"
|
||||
region="getEventMessage"
|
||||
header="app/uploader/uploader.service.ts (getEventMessage)" linenums="false">
|
||||
header="app/uploader/uploader.service.ts (getEventMessage)">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -925,8 +924,7 @@ use `HttpClientXsrfModule.withOptions()` to override the defaults.
|
||||
|
||||
<code-example
|
||||
path="http/src/app/app.module.ts"
|
||||
region="xsrf"
|
||||
linenums="false">
|
||||
region="xsrf">
|
||||
</code-example>
|
||||
|
||||
## Testing HTTP requests
|
||||
@ -967,7 +965,7 @@ along with the other symbols your tests require.
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="imports"
|
||||
header="app/testing/http-client.spec.ts (imports)" linenums="false">
|
||||
header="app/testing/http-client.spec.ts (imports)">
|
||||
</code-example>
|
||||
|
||||
Then add the `HttpClientTestingModule` to the `TestBed` and continue with
|
||||
@ -976,7 +974,7 @@ the setup of the _service-under-test_.
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="setup"
|
||||
header="app/testing/http-client.spec.ts(setup)" linenums="false">
|
||||
header="app/testing/http-client.spec.ts(setup)">
|
||||
</code-example>
|
||||
|
||||
Now requests made in the course of your tests will hit the testing backend instead of the normal backend.
|
||||
@ -991,15 +989,14 @@ Now you can write a test that expects a GET Request to occur and provides a mock
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="get-test"
|
||||
header="app/testing/http-client.spec.ts(httpClient.get)" linenums="false">
|
||||
header="app/testing/http-client.spec.ts(httpClient.get)">
|
||||
</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
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="afterEach"
|
||||
linenums="false">
|
||||
region="afterEach">
|
||||
</code-example>
|
||||
|
||||
#### Custom request expectations
|
||||
@ -1009,8 +1006,7 @@ For example, you could look for an outgoing request that has an authorization he
|
||||
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="predicate"
|
||||
linenums="false">
|
||||
region="predicate">
|
||||
</code-example>
|
||||
|
||||
As with the previous `expectOne()`,
|
||||
@ -1025,8 +1021,7 @@ you are responsible for flushing and verifying them.
|
||||
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="multi-request"
|
||||
linenums="false">
|
||||
region="multi-request">
|
||||
</code-example>
|
||||
|
||||
### Testing for errors
|
||||
@ -1037,14 +1032,12 @@ Call `request.flush()` with an error message, as seen in the following example.
|
||||
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="404"
|
||||
linenums="false">
|
||||
region="404">
|
||||
</code-example>
|
||||
|
||||
Alternatively, you can call `request.error()` with an `ErrorEvent`.
|
||||
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="network-error"
|
||||
linenums="false">
|
||||
region="network-error">
|
||||
</code-example>
|
||||
|
@ -85,8 +85,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/app.locale_data.ts" region="import-locale" header="src/app/app.module.ts"></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
|
||||
@ -100,8 +99,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/app.locale_data_extra.ts" region="import-locale-extra" header="src/app/app.module.ts"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -146,13 +144,11 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/app.component.html" region="greeting" header="src/app/app.component.html"></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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute" header="src/app/app.component.html"></code-example>
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -171,8 +167,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-desc" header="src/app/app.component.html"></code-example>
|
||||
|
||||
The translator may also need to know the meaning or intent of the text message within this particular
|
||||
app context.
|
||||
@ -180,8 +175,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-meaning" header="src/app/app.component.html"></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.
|
||||
@ -199,8 +193,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="generated-id"></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.
|
||||
@ -208,14 +201,12 @@ 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' linenums="false">
|
||||
</code-example>
|
||||
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-solo-id' header='app/app.component.html'></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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="custom-id"></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.
|
||||
@ -226,13 +217,11 @@ 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' linenums="false">
|
||||
</code-example>
|
||||
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-id' header='app/app.component.html'></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' linenums="false">
|
||||
</code-example>
|
||||
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-meaning-and-id' header='app/app.component.html'></code-example>
|
||||
|
||||
#### Define unique custom ids
|
||||
|
||||
@ -275,8 +264,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-ng-container" header="src/app/app.component.html"></code-example>
|
||||
|
||||
{@a translate-attributes}
|
||||
### Translate attributes
|
||||
@ -284,15 +272,13 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-title" header="src/app/app.component.html"></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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-title-translate" header="src/app/app.component.html"></code-example>
|
||||
|
||||
This technique works for any attribute of any element.
|
||||
|
||||
@ -316,8 +302,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-plural" header="src/app/app.component.html"></code-example>
|
||||
|
||||
* The first parameter is the key. It is bound to the component property (`minutes`), which determines
|
||||
the number of minutes.
|
||||
@ -372,8 +357,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html"></code-example>
|
||||
|
||||
{@a nesting-ICUS}
|
||||
### Nesting plural and select ICU expressions
|
||||
@ -518,8 +502,7 @@ 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 (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello-before" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
|
||||
> This XML element represents the translation of the `<h1>` greeting tag that you marked with the
|
||||
`i18n` attribute earlier in this guide.
|
||||
@ -534,13 +517,11 @@ 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 (<trans-unit>, after translation)" linenums="false">
|
||||
</code-example>
|
||||
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello" header="src/locale/messages.fr.xlf (<trans-unit>, after translation)"></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 (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-other-nodes" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
@ -566,8 +547,7 @@ 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 (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-plural" header="src/locale/messages.fr.xlf (<trans-unit>)"></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).)
|
||||
@ -577,8 +557,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html"></code-example>
|
||||
|
||||
The extraction tool broke that into two translation units because ICU expressions are extracted
|
||||
separately.
|
||||
@ -588,19 +567,16 @@ 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 (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-1" header="src/locale/messages.fr.xlf (<trans-unit>)"></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 (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-2" header="src/locale/messages.fr.xlf (<trans-unit>)"></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 (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-select" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
|
||||
{@a translate-nested}
|
||||
### Translate a nested expression
|
||||
@ -608,18 +584,15 @@ 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 (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-1" header="src/locale/messages.fr.xlf (<trans-unit>)"></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 (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-2" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
|
||||
And both together:
|
||||
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested" header="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
|
||||
The entire template translation is complete. The next section describes how to load that translation
|
||||
into the app.
|
||||
@ -783,8 +756,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="i18n/doc-files/app.module.ts" header="src/app/app.module.ts"></code-example>
|
||||
|
||||
|
||||
{@a missing-translation}
|
||||
@ -837,4 +809,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 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).
|
||||
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).
|
||||
|
@ -1,23 +1,13 @@
|
||||
# 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—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.
|
||||
@ -98,9 +88,7 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="lazy-loading-ngmodules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -138,9 +126,7 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="lazy-loading-ngmodules/src/app/app-routing.module.ts" region="const-routes" header="src/app/app-routing.module.ts"></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.
|
||||
@ -150,9 +136,7 @@ The import statements stay the same. The first two paths are the routes to the `
|
||||
Next, take a look at `customers.module.ts`. If you’re using the CLI and following the steps outlined in this page, you don’t 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="lazy-loading-ngmodules/src/app/customers/customers.module.ts" region="customers-module" header="src/app/customers/customers.module.ts"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -163,18 +147,14 @@ 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" linenums="false">
|
||||
|
||||
</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"></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)" linenums="false">
|
||||
|
||||
</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)"></code-example>
|
||||
|
||||
Now, if you view the app in the browser, the three buttons take you to each module.
|
||||
|
||||
|
@ -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)" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" header="peek-a-boo.component.ts (excerpt)"></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" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" header="src/app/spy.directive.ts"></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" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" header="src/app/spy.component.html"></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)" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" header="on-changes.component.ts (excerpt)"></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" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" header="src/app/on-changes.component.ts"></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)" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" header="DoCheckComponent (ngDoCheck)"></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" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" header="ChildComponent"></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)" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" header="AfterViewComponent (template)"></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)" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" header="AfterViewComponent (class excerpts)"></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)" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" header="AfterViewComponent (doSomething)"></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)" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" header="AfterContentParentComponent (template excerpt)"></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)" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" header="AfterContentComponent (template)"></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)" linenums="false"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" header="AfterContentComponent (class excerpts)"></code-example>
|
||||
|
||||
{@a no-unidirectional-flow-worries}
|
||||
|
||||
|
@ -1,16 +1,5 @@
|
||||
|
||||
|
||||
# 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:
|
||||
|
||||
|
@ -1,15 +1,5 @@
|
||||
# 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.
|
||||
|
@ -1,13 +1,5 @@
|
||||
# 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.
|
||||
@ -478,8 +470,7 @@ 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)" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts (Constructor)"></code-example>
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -1,13 +1,9 @@
|
||||
# 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 what’s in them available, you write an export statement, usually after the relevant code, like this:
|
||||
@ -24,6 +20,8 @@ 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.-->
|
||||
|
@ -1,13 +1,5 @@
|
||||
# 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.
|
||||
@ -20,7 +12,6 @@ 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.
|
||||
@ -57,12 +48,14 @@ You then import these modules into the root module.
|
||||
|
||||
## The basic NgModule
|
||||
|
||||
The [Angular CLI](cli) generates the following basic app module when creating a new app.
|
||||
The [Angular CLI](cli) generates the following basic `AppModule` when creating a new app.
|
||||
|
||||
<code-example path="bootstrapping/src/app/app.module.ts" region="whole-ngmodule" header="src/app/app.module.ts" linenums="false">
|
||||
|
||||
<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>
|
||||
|
||||
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).
|
||||
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).
|
||||
|
||||
<hr />
|
||||
|
||||
|
@ -26,18 +26,14 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/hero-birthday1.component.ts" header="src/app/hero-birthday1.component.ts"></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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" header="src/app/app.component.html"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -79,9 +75,7 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/app.component.html" region="format-birthday" header="src/app/app.component.html"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -95,9 +89,7 @@ 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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" header="src/app/hero-birthday2.component.ts (template)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -106,9 +98,7 @@ 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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" header="src/app/hero-birthday2.component.ts (class)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -143,9 +133,7 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/app.component.html" region="chained-birthday" header="src/app/app.component.html"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -153,9 +141,7 @@ This example—which displays **<samp>FRIDAY, APRIL 15, 1988</samp>**—
|
||||
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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" header="src/app/app.component.html"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -166,9 +152,7 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/exponential-strength.pipe.ts" header="src/app/exponential-strength.pipe.ts"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -200,8 +184,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/power-booster.component.ts" header="src/app/power-booster.component.ts"></code-example>
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/pipes/power-booster.png' alt="Power Booster">
|
||||
@ -271,17 +254,13 @@ 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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" header="src/app/flying-heroes.component.html (v1)"></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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" header="src/app/flying-heroes.component.ts (v1)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -293,17 +272,13 @@ 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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" header="src/app/flying-heroes.component.html (flyers)"></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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" header="src/app/flying-heroes.pipe.ts"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -315,9 +290,7 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" header="src/app/flying-heroes.component.ts"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -365,9 +338,7 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" header="src/app/flying-heroes.pipe.ts"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -441,18 +412,14 @@ 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" linenums="false" header="src/app/flying-heroes.pipe.ts (filter)" region="filter">
|
||||
|
||||
</code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" 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" linenums="false" 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" header="src/app/flying-heroes-impure.component.html (excerpt)" region="template-flying-heroes"></code-example>
|
||||
|
||||
|
||||
|
||||
|
@ -1,16 +1,10 @@
|
||||
# Providers
|
||||
|
||||
#### Prerequisites:
|
||||
* A basic understanding of [Bootstrapping](guide/bootstrapping).
|
||||
* Familiarity with [Frequently Used Modules](guide/frequent-ngmodules).
|
||||
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.
|
||||
|
||||
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.
|
||||
@ -21,7 +15,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" linenums="false"> </code-example>
|
||||
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts"></code-example>
|
||||
|
||||
You can now inject `UserService` anywhere in your application.
|
||||
|
||||
@ -38,11 +32,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" linenums="false"> </code-example>
|
||||
<code-example path="providers/src/app/user.service.1.ts" header="src/app/user.service.ts"></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" linenums="false"> </code-example>
|
||||
<code-example path="providers/src/app/user.module.ts" header="src/app/user.module.ts"></code-example>
|
||||
|
||||
## Limiting provider scope by lazy loading modules
|
||||
|
||||
@ -67,8 +61,7 @@ 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 can’t access it.)
|
||||
|
||||
<code-example path="providers/src/app/app.component.ts" region="component-providers" header="src/app/app.component.ts" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="providers/src/app/app.component.ts" region="component-providers" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
|
||||
## Providing services in modules vs. components
|
||||
|
@ -26,9 +26,7 @@ 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)"></code-example>
|
||||
|
||||
### Step 2: Generating and importing a new form control
|
||||
|
||||
@ -42,9 +40,7 @@ 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"></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.
|
||||
|
||||
@ -52,9 +48,7 @@ Use the constructor of `FormControl` to set its initial value, which in this cas
|
||||
|
||||
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" linenums="false" 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" header="src/app/name-editor/name-editor.component.html"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -68,9 +62,7 @@ Using the template binding syntax, the form control is now registered to the `na
|
||||
|
||||
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" linenums="false" 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" 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">
|
||||
@ -92,9 +84,7 @@ You can display the value in these ways:
|
||||
|
||||
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" linenums="false" 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" header="src/app/name-editor/name-editor.component.html (control value)"></code-example>
|
||||
|
||||
The displayed value changes as you update the form control element.
|
||||
|
||||
@ -114,9 +104,7 @@ The following example adds a method to the component class to update the value o
|
||||
|
||||
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" linenums="false" 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" 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.
|
||||
|
||||
@ -162,9 +150,7 @@ 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" linenums="false" 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" 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.
|
||||
|
||||
@ -174,9 +160,7 @@ The `ProfileEditor` component accepts input from the user, but in a real scenari
|
||||
|
||||
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" linenums="false" 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" 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.
|
||||
|
||||
@ -188,9 +172,7 @@ The `submit` event is emitted by the `form` tag using the native DOM event. You
|
||||
|
||||
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" linenums="false" 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" header="src/app/profile-editor/profile-editor.component.html (submit button)"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -202,9 +184,7 @@ 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" linenums="false" 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" 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.
|
||||
|
||||
@ -220,9 +200,7 @@ 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" linenums="false" 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" 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.
|
||||
|
||||
@ -232,9 +210,7 @@ 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" linenums="false" 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" 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.
|
||||
|
||||
@ -270,9 +246,7 @@ 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" linenums="false" 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" 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.
|
||||
|
||||
@ -356,9 +330,7 @@ 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" linenums="false" 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" header="src/app/profile-editor/profile-editor.component.html (required attribute)"></code-example>
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
@ -372,9 +344,7 @@ 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" linenums="false" 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" 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">
|
||||
@ -438,9 +408,7 @@ To attach the aliases from your form model, you must add it to the template. Sim
|
||||
|
||||
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" linenums="false" 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" header="src/app/profile-editor/profile-editor.component.html (aliases form array template)"></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.
|
||||
|
||||
|
@ -15,8 +15,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="reusable" language="typescript"></code-example>
|
||||
|
||||
In the above code snippet, `transAnimation` is made reusable by declaring it as an export variable.
|
||||
|
||||
@ -27,8 +26,7 @@ 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" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="animations/src/app/open-close.component.3.ts" header="src/app/open-close.component.ts" region="reusable" language="typescript"></code-example>
|
||||
|
||||
## More on Angular animations
|
||||
|
||||
|
@ -43,8 +43,7 @@ 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" linenums="false" header="src/app/app.module.ts" region="route-animation-data" language="typescript">
|
||||
</code-example>
|
||||
<code-example path="animations/src/app/app.module.ts" 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.
|
||||
|
||||
@ -62,13 +61,11 @@ 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" linenums="false" header="src/app/app.component.ts" region="prepare-router-outlet" language="typescript">
|
||||
</code-example>
|
||||
<code-example path="animations/src/app/app.component.ts" 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.
|
||||
|
||||
@ -79,8 +76,7 @@ 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" linenums="false" header="src/app/animations.ts" region="route-animations" language="typescript">
|
||||
</code-example>
|
||||
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="route-animations" language="typescript"></code-example>
|
||||
|
||||
The animation definition does several things:
|
||||
|
||||
@ -97,15 +93,13 @@ 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" linenums="false" header="src/app/app.component.ts" region="define" language="typescript">
|
||||
</code-example>
|
||||
<code-example path="animations/src/app/app.component.ts" 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" linenums="false" header="src/app/animations.ts" region="style-view" language="typescript">
|
||||
</code-example>
|
||||
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="style-view" language="typescript"></code-example>
|
||||
|
||||
### Querying the view containers
|
||||
|
||||
@ -113,8 +107,7 @@ 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" linenums="false" header="src/app/animations.ts" region="query" language="typescript" linenums="false">
|
||||
</code-example>
|
||||
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="query" language="typescript"></code-example>
|
||||
|
||||
The animation code does the following after styling the views:
|
||||
|
||||
|
@ -53,9 +53,7 @@ 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" linenums="false" header="src/index.html (base-href)" region="base-href">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/index.html" header="src/index.html (base-href)" region="base-href"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -69,9 +67,7 @@ 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" linenums="false" header="src/app/app.module.ts (import)" region="import-router">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.module.1.ts" header="src/app/app.module.ts (import)" region="import-router"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -100,9 +96,7 @@ 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" linenums="false" header="src/app/app.module.ts (excerpt)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.module.0.ts" header="src/app/app.module.ts (excerpt)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -177,9 +171,7 @@ an anchor tag.
|
||||
Consider the following template:
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.component.1.html" 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).
|
||||
@ -917,9 +909,7 @@ 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" linenums="false" header="src/app/app.module.ts (first-config)" region="first-config">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.module.1.ts" header="src/app/app.module.ts (first-config)" region="first-config"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -947,9 +937,7 @@ 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" linenums="false" header="src/app/app.component.html">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.component.1.html" header="src/app/app.component.html"></code-example>
|
||||
|
||||
{@a wildcard}
|
||||
|
||||
@ -973,17 +961,13 @@ 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" linenums="false" 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" 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" linenums="false" header="src/app/app.module.ts (wildcard)" region="wildcard">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.module.1.ts" header="src/app/app.module.ts (wildcard)" region="wildcard"></code-example>
|
||||
|
||||
Create the `PageNotFoundComponent` to display when users visit invalid URLs.
|
||||
|
||||
@ -991,9 +975,7 @@ 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" linenums="false" 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" 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.
|
||||
@ -1022,8 +1004,7 @@ 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" linenums="false" header="src/app/app-routing.module.ts (appRoutes)" region="appRoutes">
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app-routing.module.1.ts" 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.
|
||||
@ -1314,16 +1295,12 @@ 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>
|
||||
|
||||
|
||||
|
||||
@ -1442,9 +1419,7 @@ 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>
|
||||
|
||||
|
||||
|
||||
@ -1568,9 +1543,7 @@ 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>
|
||||
|
||||
|
||||
|
||||
@ -1602,9 +1575,7 @@ 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" linenums="false" header="src/app/app-routing.module.ts (v2)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app-routing.module.2.ts" header="src/app/app-routing.module.ts (v2)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -1618,9 +1589,7 @@ 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>
|
||||
|
||||
|
||||
|
||||
@ -1632,9 +1601,7 @@ 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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.module.3.ts" region="module-imports" header="src/app/app.module.ts (module-imports)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -1678,9 +1645,7 @@ 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" linenums="false" 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" header="src/app/heroes/heroes-routing.module.ts (excerpt)" region="hero-detail-route"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -1734,9 +1699,7 @@ 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" linenums="false" 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" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -1765,18 +1728,14 @@ 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" linenums="false" 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" 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" linenums="false" 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" header="src/app/heroes/hero-detail/hero-detail.component.ts (switchMap operator import)" region="rxjs-operator-import"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -1787,17 +1746,13 @@ 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" linenums="false" 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" 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" linenums="false" 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" 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.
|
||||
@ -1934,9 +1889,7 @@ 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" linenums="false" 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" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit snapshot)" region="snapshot"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -1967,9 +1920,7 @@ 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" linenums="false" 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" header="src/app/heroes/hero-detail/hero-detail.component.ts (excerpt)" region="gotoHeroes"></code-example>
|
||||
|
||||
|
||||
{@a optional-route-parameters}
|
||||
@ -2029,9 +1980,7 @@ 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" linenums="false" 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" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -2039,9 +1988,7 @@ 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" linenums="false" 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" header="src/app/heroes/heroes-routing.module.ts (hero-detail-route)" region="hero-detail-route"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -2049,9 +1996,7 @@ 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" linenums="false" 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" header="src/app/heroes/hero-detail/hero-detail.component.ts (gotoHeroes)" region="gotoHeroes"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -2066,9 +2011,7 @@ 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" linenums="false" 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" header="src/app/heroes/hero-detail/hero-detail.component.ts (go to heroes)" region="gotoHeroes"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -2148,27 +2091,21 @@ 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" linenums="false" 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" 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" linenums="false" 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" 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" linenums="false" 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" header="src/app/heroes/hero-list/hero-list.component.ts (constructor and ngOnInit)" region="ctor"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -2181,15 +2118,11 @@ 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" linenums="false" 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" 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" linenums="false" 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" region="selected" header="src/app/heroes/hero-list/hero-list.component.css"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -2215,22 +2148,16 @@ 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" linenums="false" header="src/app/app.module.ts (animations-module)" region="animations-module">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.module.ts" 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" linenums="false" header="src/app/animations.ts (excerpt)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/animations.ts" header="src/app/animations.ts (excerpt)"></code-example>
|
||||
|
||||
|
||||
This file does the following:
|
||||
@ -2248,24 +2175,18 @@ 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" linenums="false" header="src/app/app.component.ts (animations)" region="animation-imports">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.component.2.ts" 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" linenums="false" header="src/app/app.component.html (router outlet)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.component.2.html" 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" linenums="false" header="src/app/app.component.ts (router outlet)" region="function-binding">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.component.2.ts" 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.
|
||||
|
||||
@ -2563,9 +2484,7 @@ 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—**child routing**.
|
||||
@ -2622,8 +2541,7 @@ 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" linenums="false" 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" header="src/app/crisis-center/crisis-center/crisis-center.component.html"></code-example>
|
||||
|
||||
The `CrisisCenterComponent` has the following in common with the `AppComponent`:
|
||||
|
||||
@ -2650,14 +2568,12 @@ 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" linenums="false" 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" 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" linenums="false" 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" 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
|
||||
@ -2703,9 +2619,7 @@ 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" linenums="false" 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" header="src/app/crisis-center/crisis-center-routing.module.ts (excerpt)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -2723,7 +2637,7 @@ _before_ the `AppRoutingModule`:
|
||||
|
||||
</code-pane>
|
||||
|
||||
<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 path="router/src/app/app.module.4.ts" header="src/app/app.module.ts (import CrisisCenterModule)" region="crisis-center-module">
|
||||
|
||||
</code-pane>
|
||||
|
||||
@ -2735,9 +2649,7 @@ 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" linenums="false" header="src/app/app-routing.module.ts (v3)" region="v3">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app-routing.module.3.ts" header="src/app/app-routing.module.ts (v3)" region="v3"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -2814,9 +2726,7 @@ 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" linenums="false" 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" 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.
|
||||
@ -2847,9 +2757,7 @@ 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" linenums="false" header="src/app/app.component.html (outlets)" region="outlets">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.component.4.html" header="src/app/app.component.html (outlets)" region="outlets"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -2923,9 +2831,7 @@ 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" linenums="false" header="src/app/app-routing.module.ts (compose route)" region="compose">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app-routing.module.3.ts" header="src/app/app-routing.module.ts (compose route)" region="compose"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -2936,9 +2842,7 @@ 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" linenums="false" header="src/app/app.component.html (contact-link)" region="contact-link">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.component.4.html" header="src/app/app.component.html (contact-link)" region="contact-link"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3031,9 +2935,7 @@ 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" linenums="false" 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" header="src/app/compose-message/compose-message.component.ts (closePopup)" region="closePopup"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3266,11 +3168,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" linenums="false" path="router/src/app/admin/admin/admin.component.html">
|
||||
<code-pane header="src/app/admin/admin/admin.component.html" path="router/src/app/admin/admin/admin.component.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<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 header="src/app/admin/admin-dashboard/admin-dashboard.component.html" path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
@ -3278,11 +3180,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" linenums="false" path="router/src/app/admin/manage-crises/manage-crises.component.html">
|
||||
<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>
|
||||
|
||||
<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 header="src/app/admin/manage-heroes/manage-heroes.component.html" path="router/src/app/admin/manage-heroes/manage-heroes.component.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
@ -3309,9 +3211,7 @@ 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" linenums="false" 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" 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`.
|
||||
@ -3327,18 +3227,14 @@ 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" linenums="false" header="src/app/app.module.ts (admin module)" region="admin-module">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.module.4.ts" 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" linenums="false" header="src/app/app.component.html (template)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.component.5.html" header="src/app/app.component.html (template)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3367,9 +3263,7 @@ 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" linenums="false" header="src/app/auth/auth.guard.ts (excerpt)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/auth/auth.guard.1.ts" header="src/app/auth/auth.guard.ts (excerpt)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3377,9 +3271,7 @@ 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" linenums="false" 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" header="src/app/admin/admin-routing.module.ts (guarded admin route)" region="admin-route"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3401,9 +3293,7 @@ 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" linenums="false" header="src/app/auth/auth.service.ts (excerpt)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/auth/auth.service.ts" header="src/app/auth/auth.service.ts (excerpt)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3416,9 +3306,7 @@ 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" linenums="false" header="src/app/auth/auth.guard.ts (v2)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/auth/auth.guard.2.ts" header="src/app/auth/auth.guard.ts (v2)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3496,9 +3384,7 @@ async checks and a `boolean` for sync checks.
|
||||
This one returns a `boolean`:
|
||||
|
||||
|
||||
<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>
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
@ -3506,9 +3392,7 @@ 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" linenums="false" 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" header="src/app/admin/admin-routing.module.ts (excerpt)" region="can-activate-child"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3557,9 +3441,7 @@ 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" linenums="false" 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" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (cancel and save methods)" region="cancel-save"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3593,9 +3475,7 @@ 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`).
|
||||
@ -3617,9 +3497,7 @@ 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>
|
||||
|
||||
|
||||
|
||||
@ -3632,18 +3510,14 @@ 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" linenums="false" header="src/app/can-deactivate.guard.ts (component-specific)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/can-deactivate.guard.1.ts" 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" linenums="false" 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" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (excerpt)" region="canDeactivate"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3656,9 +3530,7 @@ 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" linenums="false" 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" 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.
|
||||
@ -3704,9 +3576,7 @@ 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>
|
||||
|
||||
|
||||
|
||||
@ -3728,17 +3598,13 @@ 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" linenums="false" 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" header="src/app/crisis-center/crisis-center-routing.module.ts (resolver)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3748,9 +3614,7 @@ 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" linenums="false" 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" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (ngOnInit v2)" region="ngOnInit"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3853,9 +3717,7 @@ 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" linenums="false" header="src/app/auth/auth.guard.ts (v3)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/auth/auth.guard.4.ts" header="src/app/auth/auth.guard.ts (v3)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3866,9 +3728,7 @@ 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" linenums="false" header="src/app/auth/login/login.component.ts (preserve)" region="preserve">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/auth/login/login.component.ts" header="src/app/auth/login/login.component.ts (preserve)" region="preserve"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -3885,9 +3745,7 @@ 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" linenums="false" 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" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (v2)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -3959,9 +3817,7 @@ 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">
|
||||
|
||||
@ -4020,9 +3876,7 @@ Add it to the `AuthGuard` class's `implements` list.
|
||||
Then implement `canLoad()` as follows:
|
||||
|
||||
|
||||
<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>
|
||||
<code-example path="router/src/app/auth/auth.guard.ts" header="src/app/auth/auth.guard.ts (CanLoad guard)" region="canLoad"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -4034,9 +3888,7 @@ 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>
|
||||
|
||||
|
||||
|
||||
@ -4135,9 +3987,7 @@ 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" linenums="false" header="src/app/app-routing.module.ts (preload all)" region="forRoot">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app-routing.module.6.ts" header="src/app/app-routing.module.ts (preload all)" region="forRoot"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -4182,9 +4032,7 @@ 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" linenums="false" 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" header="src/app/app-routing.module.ts (route data preload)" region="preload-v2"></code-example>
|
||||
|
||||
Generate a new `SelectivePreloadingStrategy` service.
|
||||
|
||||
@ -4193,9 +4041,7 @@ Generate a new `SelectivePreloadingStrategy` service.
|
||||
</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>
|
||||
<code-example path="router/src/app/selective-preloading-strategy.service.ts" header="src/app/selective-preloading-strategy.service.ts (excerpt)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -4233,9 +4079,7 @@ 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" linenums="false" 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" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (preloaded modules)"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -4257,9 +4101,7 @@ 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" linenums="false" header="src/app/heroes/heroes-routing.module.ts (heroes redirects)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/heroes/heroes-routing.module.ts" 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`.
|
||||
@ -4277,21 +4119,15 @@ 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" linenums="false" header="src/app/app-routing.module.ts (superheroes redirect)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app-routing.module.ts" 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" linenums="false" header="src/app/app.component.html (superheroes active routerLink)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.component.html" 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" linenums="false" 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" 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.
|
||||
|
||||
@ -4312,9 +4148,7 @@ 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" linenums="false" 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" header="src/app/app.module.ts (inspect the router config)" region="inspect-config"></code-example>
|
||||
|
||||
|
||||
{@a final-app}
|
||||
@ -4353,27 +4187,21 @@ 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" linenums="false" header="src/app/app.component.ts (h-anchor)" region="h-anchor">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.component.3.ts" 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" linenums="false" 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" 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" linenums="false" 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" header="src/app/app.component.ts (cc-query-params)" region="cc-query-params"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -4383,9 +4211,7 @@ 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" linenums="false" 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" header="src/app/app.component.ts (cc-anchor-w-default)" region="cc-anchor-w-default"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -4401,9 +4227,7 @@ 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" linenums="false" header="src/app/app.component.ts (Dragon-anchor)" region="Dragon-anchor">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (Dragon-anchor)" region="Dragon-anchor"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -4418,9 +4242,7 @@ 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" linenums="false" header="src/app/app.component.ts (template)" region="template">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -4553,9 +4375,7 @@ 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" linenums="false" header="src/index.html (base-href)" region="base-href">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/index.html" header="src/index.html (base-href)" region="base-href"></code-example>
|
||||
|
||||
#### HTML5 URLs and the *<base href>*
|
||||
|
||||
@ -4568,9 +4388,7 @@ 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" linenums="false" header="src/index.html (base-href)" region="base-href">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/index.html" header="src/index.html (base-href)" region="base-href"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -4596,6 +4414,4 @@ 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" linenums="false" header="src/app/app.module.ts (hash URL strategy)">
|
||||
|
||||
</code-example>
|
||||
<code-example path="router/src/app/app.module.6.ts" header="src/app/app.module.ts (hash URL strategy)"></code-example>
|
||||
|
@ -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" linenums="false">
|
||||
<code-example language="TypeScript">
|
||||
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" linenums="false">
|
||||
<code-example language="none">
|
||||
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
schematics <path-to-schematics-project>:<schematics-name> --<required-option>=<value>
|
||||
</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" linenums="false">
|
||||
<code-example language="bash">
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
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" linenums="false">
|
||||
<code-example language="none">
|
||||
{
|
||||
"$schema":
|
||||
"../node_modules/@angular-devkit/schematics/collection-schema.json",
|
||||
|
@ -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" linenums="false">
|
||||
<code-example language="bash">
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
|
||||
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" linenums="false" hideCopy="true">
|
||||
<code-example language="bash" hideCopy="true">
|
||||
|
||||
CREATE src/app/my-data.service.ts (208 bytes)
|
||||
|
||||
|
@ -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" linenums="false">
|
||||
<code-example language="bash">
|
||||
ng generate my-schematic-collection:my-schematic-name
|
||||
</code-example>
|
||||
|
||||
—or—
|
||||
|
||||
<code-example language="bash" linenums="false">
|
||||
<code-example language="bash">
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
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" linenums="false">
|
||||
<code-example language="bash">
|
||||
ng update @angular/material
|
||||
</code-example>
|
||||
|
||||
|
@ -98,9 +98,7 @@ 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>
|
||||
|
||||
|
||||
|
||||
@ -112,9 +110,7 @@ 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" linenums="false" header="src/app/inner-html-binding.component.ts (class)" region="class">
|
||||
|
||||
</code-example>
|
||||
<code-example path="security/src/app/inner-html-binding.component.ts" header="src/app/inner-html-binding.component.ts (class)" region="class"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -200,9 +196,7 @@ 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" linenums="false" header="src/app/bypass-security.component.html (URL)" region="URL">
|
||||
|
||||
</code-example>
|
||||
<code-example path="security/src/app/bypass-security.component.html" header="src/app/bypass-security.component.html (URL)" region="URL"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -211,9 +205,7 @@ 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" linenums="false" header="src/app/bypass-security.component.ts (trust-url)" region="trust-url">
|
||||
|
||||
</code-example>
|
||||
<code-example path="security/src/app/bypass-security.component.ts" header="src/app/bypass-security.component.ts (trust-url)" region="trust-url"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -231,15 +223,11 @@ 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" linenums="false" header="src/app/bypass-security.component.html (iframe)" region="iframe">
|
||||
|
||||
</code-example>
|
||||
<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.ts" linenums="false" 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" header="src/app/bypass-security.component.ts (trust-video-url)" region="trust-video-url"></code-example>
|
||||
|
||||
|
||||
|
||||
|
@ -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" linenums="false" header="log-update.service.ts" region="sw-update"> </code-example>
|
||||
<code-example path="service-worker-getting-started/src/app/log-update.service.ts" 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" linenums="false" header="check-for-update.service.ts"> </code-example>
|
||||
<code-example path="service-worker-getting-started/src/app/check-for-update.service.ts" 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.
|
||||
@ -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" linenums="false" header="prompt-update.service.ts" region="sw-activate"> </code-example>
|
||||
<code-example path="service-worker-getting-started/src/app/prompt-update.service.ts" 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.
|
||||
|
||||
|
@ -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)" linenums="false"></code-example>
|
||||
<code-example path="set-document-title/src/app/app.component.ts" region="class" header="src/app/app.component.ts (class)"></code-example>
|
||||
|
||||
Bind that method to three anchor tags and voilà!
|
||||
|
||||
|
@ -1,18 +1,5 @@
|
||||
# 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 don’t 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.
|
||||
@ -54,7 +41,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
|
||||
|
@ -1,15 +1,10 @@
|
||||
# Singleton services
|
||||
|
||||
#### Prerequisites:
|
||||
|
||||
* A basic understanding of [Bootstrapping](guide/bootstrapping).
|
||||
* Familiarity with [Providers](guide/providers).
|
||||
A singleton service is a service for which only once instance exists in an app.
|
||||
|
||||
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:
|
||||
@ -25,7 +20,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" linenums="false"> </code-example>
|
||||
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts"></code-example>
|
||||
|
||||
For more detailed information on services, see the [Services](tutorial/toh-pt4) chapter of the
|
||||
[Tour of Heroes tutorial](tutorial).
|
||||
@ -75,7 +70,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" linenums="false"> </code-example>
|
||||
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="for-root" header="src/app/greeting/greeting.module.ts"></code-example>
|
||||
|
||||
|
||||
{@a forRoot-router}
|
||||
@ -122,22 +117,16 @@ 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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="ngmodules/src/app/greeting/user.service.ts" region="ctor" header="src/app/greeting/user.service.ts (constructor)"></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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="for-root" header="src/app/greeting/greeting.module.ts (forRoot)"></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)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="ngmodules/src/app/app.module.ts" region="import-for-root" header="src/app/app.module.ts (imports)"></code-example>
|
||||
|
||||
The app displays "Miss Marple" as the user instead of the default "Sherlock Holmes".
|
||||
|
||||
@ -151,9 +140,7 @@ 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" linenums="false">
|
||||
|
||||
</code-example>
|
||||
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts"></code-example>
|
||||
|
||||
The constructor tells Angular to inject the `GreetingModule` into itself.
|
||||
The injection would be circular if Angular looked for
|
||||
@ -177,7 +164,7 @@ Now `parentModule` exists and the constructor throws the error.
|
||||
|
||||
Here are the two files in their entirety for reference:
|
||||
|
||||
<code-tabs linenums="false">
|
||||
<code-tabs>
|
||||
<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">
|
||||
|
@ -156,3 +156,12 @@ In Angular version 9 and later, it will be safe to remove any `{static: false}`
|
||||
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.
|
||||
|
@ -31,9 +31,7 @@ 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" linenums="false" header="src/app/app.component.html (ngif)" region="ngif">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif)" region="ngif"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -52,9 +50,7 @@ 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" linenums="false" header="src/app/app.component.html (built-in)" region="built-in">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (built-in)" region="built-in"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -121,9 +117,7 @@ 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" linenums="false" header="src/app/app.component.html (ngif-true)" region="ngif-true">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-true)" region="ngif-true"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -150,9 +144,7 @@ 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" linenums="false" header="src/app/app.component.html (display-none)" region="display-none">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (display-none)" region="display-none"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -201,9 +193,7 @@ 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" linenums="false" header="src/app/app.component.html (asterisk)" region="asterisk">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (asterisk)" region="asterisk"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -211,9 +201,7 @@ 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" linenums="false" header="src/app/app.component.html (ngif-template)" region="ngif-template">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-template)" region="ngif-template"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -246,9 +234,7 @@ 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" linenums="false" header="src/app/app.component.html (inside-ngfor)" region="inside-ngfor">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (inside-ngfor)" region="inside-ngfor"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -275,53 +261,179 @@ 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 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_ .
|
||||
* 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.
|
||||
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 include `index` and `odd` and a special property named `$implicit`.
|
||||
These properties can include, but aren't limited to, `index`, `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 [API guide](api/common/NgForOf "API: NgFor")
|
||||
* The [`NgFor` API guide](api/common/NgForOf "API: NgFor")
|
||||
describes additional `NgFor` directive properties and context properties.
|
||||
|
||||
* `NgFor` is implemented by the `NgForOf` directive. Read more about additional `NgForOf` directive properties and context properties [NgForOf API reference](api/common/NgForOf).
|
||||
* 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>
|
||||
|
||||
|
||||
These microsyntax mechanisms are available to you when you write your own structural directives.
|
||||
### 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><ng-template ngFor let-item [ngForOf]="[1,2,3]"></code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>*ngFor="let item of [1,2,3] as items; trackBy: myTrack; index as i"</code></td>
|
||||
<td><code><ng-template ngFor let-item [ngForOf]="[1,2,3]" let-items="ngForOf" [ngForTrackBy]="myTrack" let-i="index"></code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>*ngIf="exp"</code></td>
|
||||
<td><code><ng-template [ngIf]="exp"></code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>*ngIf="exp as value"</code></td>
|
||||
<td><code><ng-template [ngIf]="exp" let-value="ngIf"></code></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
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`.
|
||||
@ -346,7 +458,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.
|
||||
@ -373,9 +485,7 @@ 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" linenums="false" header="src/app/app.component.html (ngswitch)" region="ngswitch">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngswitch)" region="ngswitch"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -409,9 +519,7 @@ 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" linenums="false" header="src/app/app.component.html (ngswitch-template)" region="ngswitch-template">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngswitch-template)" region="ngswitch-template"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -444,9 +552,7 @@ 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" linenums="false" header="src/app/app.component.html (template-tag)" region="template-tag">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (template-tag)" region="template-tag"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -476,9 +582,7 @@ 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" linenums="false" header="src/app/app.component.html (ngfor-li)" region="ngfor-li">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngfor-li)" region="ngfor-li"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -486,9 +590,7 @@ 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" linenums="false" header="src/app/app.component.html (ngif)" region="ngif">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif)" region="ngif"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -501,18 +603,14 @@ 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" linenums="false" header="src/app/app.component.html (ngif-span)" region="ngif-span">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" 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" linenums="false" header="src/app/app.component.css (p-span)" region="p-span">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.css" header="src/app/app.component.css (p-span)" region="p-span"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -534,9 +632,7 @@ 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" linenums="false" header="src/app/app.component.html (select-span)" region="select-span">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (select-span)" region="select-span"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -559,9 +655,7 @@ 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" linenums="false" header="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -577,9 +671,7 @@ It renders properly.
|
||||
Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
|
||||
|
||||
|
||||
<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>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -630,9 +722,7 @@ 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" linenums="false" header="src/app/app.component.html (appUnless-1)" region="appUnless-1">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (appUnless-1)" region="appUnless-1"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -649,9 +739,7 @@ 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" linenums="false" header="src/app/unless.directive.ts (skeleton)" region="skeleton">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/unless.directive.ts" header="src/app/unless.directive.ts (skeleton)" region="skeleton"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -684,9 +772,7 @@ 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" linenums="false" header="src/app/unless.directive.ts (ctor)" region="ctor">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/unless.directive.ts" header="src/app/unless.directive.ts (ctor)" region="ctor"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -707,9 +793,7 @@ Read about `@Input` in the [_Template Syntax_](guide/template-syntax#inputs-outp
|
||||
|
||||
|
||||
|
||||
<code-example path="structural-directives/src/app/unless.directive.ts" linenums="false" header="src/app/unless.directive.ts (set)" region="set">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/unless.directive.ts" header="src/app/unless.directive.ts (set)" region="set"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -727,9 +811,7 @@ 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" linenums="false" header="src/app/unless.directive.ts (excerpt)" region="no-docs">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/unless.directive.ts" header="src/app/unless.directive.ts (excerpt)" region="no-docs"></code-example>
|
||||
|
||||
|
||||
|
||||
@ -738,9 +820,7 @@ 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" linenums="false" header="src/app/app.component.html (appUnless)" region="appUnless">
|
||||
|
||||
</code-example>
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (appUnless)" region="appUnless"></code-example>
|
||||
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user