Compare commits
110 Commits
Author | SHA1 | Date | |
---|---|---|---|
bbeac0727b | |||
03b3a3881a | |||
2c74996b7c | |||
28406707e0 | |||
b43ae44d84 | |||
2174827fe5 | |||
8bce694c2f | |||
38e9959cf0 | |||
1fc778644a | |||
a08b1816d5 | |||
f97c464a9e | |||
ac4353ce8a | |||
26a6db208c | |||
e0428fe83c | |||
82df950c65 | |||
663aba5775 | |||
2d465ee37b | |||
70238f7d7d | |||
704e3543dc | |||
98e71d31db | |||
7478d0cf4d | |||
ef5c0dae7d | |||
57c3a238ea | |||
bb284066ca | |||
fc147289a9 | |||
3f3e1bfce9 | |||
2b02e87f4a | |||
88869a2965 | |||
958aba9391 | |||
d9581bd6a4 | |||
19ada81a08 | |||
3d60f2cefc | |||
f13e40db1b | |||
fab6ea0109 | |||
05a1cebceb | |||
1281bef896 | |||
37cbcfaa71 | |||
ef0b8f4a87 | |||
8bc2f0647b | |||
b71ccc2954 | |||
2daa838a04 | |||
a2716acf4c | |||
7a0cc534df | |||
5f52e63857 | |||
9409dce93c | |||
2fa788c9e6 | |||
73e667f61f | |||
a7d5d33f0a | |||
7e511e78cd | |||
9e76a38073 | |||
242981963e | |||
970df9ebaf | |||
4c7e7fbd09 | |||
5f78456170 | |||
36fd063737 | |||
c1b7f0370e | |||
882a9e3856 | |||
362b3e4d03 | |||
2952ea57a5 | |||
3541e590f4 | |||
8ef0ae3561 | |||
c3ff66c1ba | |||
a1d9848456 | |||
a3482f7076 | |||
006af0b985 | |||
9dc4815e39 | |||
4263d9ea0d | |||
30253a7df3 | |||
16b83e8e2f | |||
e0c10632ea | |||
686b62129c | |||
dd2587d9e5 | |||
2742649a52 | |||
eb0461d2d4 | |||
e24393c35b | |||
8237e958a6 | |||
9ba898d588 | |||
cd1b0c1b1f | |||
06072e0062 | |||
288e0ef7b6 | |||
88ad5052bf | |||
7e6644a25a | |||
d533d15001 | |||
d0abf1bc54 | |||
56ac18ea8c | |||
11d3b19b32 | |||
d96167fc54 | |||
68fed2fc28 | |||
57457fb7c7 | |||
a2fc5774a7 | |||
c6041b985e | |||
b45d2e0fb1 | |||
42f7419522 | |||
32f5241598 | |||
06e72721ca | |||
3a80069f65 | |||
0818e2c799 | |||
7b8b2d4622 | |||
f6526a0872 | |||
0984022192 | |||
bb7603104f | |||
bdea243fed | |||
2397144e01 | |||
466c754cc8 | |||
45fee069d7 | |||
432f7ce2a0 | |||
e8c34c47af | |||
e81fc14da1 | |||
d5c210a2e1 | |||
7dbde6f570 |
43
.bazelrc
43
.bazelrc
@ -18,10 +18,6 @@ test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test
|
||||
# See https://github.com/bazelbuild/bazel/issues/4603
|
||||
build --symlink_prefix=dist/
|
||||
|
||||
# Disable watchfs as it causes tests to be flaky on Windows
|
||||
# https://github.com/angular/angular/issues/29541
|
||||
build --nowatchfs
|
||||
|
||||
# Turn off legacy external runfiles
|
||||
run --nolegacy_external_runfiles
|
||||
test --nolegacy_external_runfiles
|
||||
@ -71,11 +67,26 @@ test --test_output=errors
|
||||
# any bazel target. This is a temporary flag until codebase is permanently switched to Ivy.
|
||||
build --define=compile=legacy
|
||||
|
||||
###############################
|
||||
# Remote Build Execution support
|
||||
# Turn on these settings with
|
||||
# --config=remote
|
||||
###############################
|
||||
#######################
|
||||
# Remote HTTP Caching #
|
||||
#######################
|
||||
build --remote_http_cache=https://storage.googleapis.com/angular-team-cache
|
||||
build --remote_accept_cached=true
|
||||
build --remote_upload_local_results=false
|
||||
|
||||
######################################
|
||||
# Remote HTTP Caching writes support #
|
||||
# Turn on these settings with #
|
||||
# --config=-http-caching #
|
||||
######################################
|
||||
build:remote-http-caching --remote_upload_local_results=true
|
||||
build:remote-http-caching --google_default_credentials
|
||||
|
||||
##################################
|
||||
# Remote Build Execution support #
|
||||
# Turn on these settings with #
|
||||
# --config=remote #
|
||||
##################################
|
||||
|
||||
# Load default settings for Remote Build Execution.
|
||||
import %workspace%/third_party/github.com/bazelbuild/bazel-toolchains/bazelrc/.bazelrc.notoolchain
|
||||
@ -83,6 +94,7 @@ import %workspace%/third_party/github.com/bazelbuild/bazel-toolchains/bazelrc/.b
|
||||
# Increase the default number of jobs by 50% because our build has lots of
|
||||
# parallelism
|
||||
build:remote --jobs=150
|
||||
build:remote --google_default_credentials
|
||||
|
||||
# Toolchain and platform related flags
|
||||
build:remote --host_javabase=@rbe_ubuntu1604_angular//java:jdk
|
||||
@ -96,21 +108,10 @@ build:remote --extra_execution_platforms=//tools:rbe_ubuntu1604-angular
|
||||
build:remote --host_platform=//tools:rbe_ubuntu1604-angular
|
||||
build:remote --platforms=//tools:rbe_ubuntu1604-angular
|
||||
|
||||
# Remote instance.
|
||||
# Remote instance and caching
|
||||
build:remote --remote_instance_name=projects/internal-200822/instances/default_instance
|
||||
build:remote --project_id=internal-200822
|
||||
|
||||
# Remote caching
|
||||
build:remote --remote_cache=remotebuildexecution.googleapis.com
|
||||
# By default, do not accept remote cache, to be set to true for CI based on environment
|
||||
build:remote --remote_accept_cached=false
|
||||
# By default, do not upload local results to cache, to be set to true for CI based on environment
|
||||
build:remote --remote_upload_local_results=false
|
||||
|
||||
# Build Event Service Configuration
|
||||
build:remote --bes_backend=buildeventservice.googleapis.com
|
||||
build:remote --bes_timeout=30s
|
||||
build:remote --bes_results_url="https://source.cloud.google.com/results/invocations/"
|
||||
|
||||
###############################
|
||||
# NodeJS rules settings
|
||||
|
@ -7,158 +7,162 @@
|
||||
# To validate changes, use an online parser, eg.
|
||||
# http://yaml-online-parser.appspot.com/
|
||||
|
||||
# Note that the browser docker image comes with Chrome and Firefox preinstalled. This is just
|
||||
# needed for jobs that run tests without Bazel. Bazel runs tests with browsers that will be
|
||||
# fetched by the Webtesting rules. Therefore for jobs that run tests with Bazel, we don't need a
|
||||
# docker image with browsers pre-installed.
|
||||
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
|
||||
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
|
||||
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
|
||||
# **NOTE 3**: If you change the version of the `*-browsers` docker image, make sure the
|
||||
# `CI_CHROMEDRIVER_VERSION_ARG` env var (in `.circleci/env.sh`) points to a ChromeDriver
|
||||
# version that is compatible with the Chrome version in the image.
|
||||
var_1: &default_docker_image circleci/node:10.16@sha256:75c05084fff4afa3683a03c5a04a4a3ad95c536ff2439d8fe14e7e1f5c58b09a
|
||||
var_2: &browsers_docker_image circleci/node:10.16-browsers@sha256:d2a96fe1cbef51257ee626b5f645e64dade3e886f00ba9cb7e8ea65b4efe8db1
|
||||
# CircleCI configuration version
|
||||
# Version 2.1 allows for extra config reuse features
|
||||
# https://circleci.com/docs/2.0/reusing-config/#getting-started-with-config-reuse
|
||||
version: 2.1
|
||||
|
||||
# We don't want to include the current branch name in the cache key because that would prevent
|
||||
# PRs from being able to restore the cache since the branch names are always different for PRs.
|
||||
# The cache key should only consist of dynamic values that change whenever something in the
|
||||
# cache changes. For example:
|
||||
# 1) yarn lock file changes --> cached "node_modules" are different.
|
||||
# 2) bazel repository definitions change --> cached bazel repositories are different.
|
||||
# **NOTE 1 **: If you change the cache key prefix, also sync the restore_cache fallback to match.
|
||||
# **NOTE 1 **: If you change the cache key prefix, also sync the cache_key_fallback to match.
|
||||
# **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks.
|
||||
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
|
||||
var_3: &cache_key v3-angular-node-10.16-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||
|
||||
# Initializes the CI environment by setting up common environment variables.
|
||||
var_4: &init_environment
|
||||
run:
|
||||
name: Initializing environment (setting up variables, overwriting Yarn)
|
||||
# Overwrite the yarn installed in the docker container with our own version.
|
||||
command: |
|
||||
./.circleci/env.sh
|
||||
ourYarn=$(realpath ./third_party/github.com/yarnpkg/yarn/releases/download/v1.17.3/bin/yarn.js)
|
||||
sudo chmod a+x $ourYarn
|
||||
sudo ln -fs $ourYarn /usr/local/bin/yarn
|
||||
echo "Yarn version: $(yarn --version)"
|
||||
|
||||
# Add GitHub to known hosts.
|
||||
mkdir -p ~/.ssh
|
||||
echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts
|
||||
|
||||
# use git+ssh instead of https
|
||||
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
|
||||
git config --global gc.auto 0 || true
|
||||
|
||||
|
||||
var_5: &setup_bazel_remote_execution
|
||||
run:
|
||||
name: "Setup bazel RBE remote execution"
|
||||
command: |
|
||||
# We need ensure that the same default digest is used for encoding and decoding
|
||||
# with openssl. Openssl versions might have different default digests which can
|
||||
# cause decryption failures based on the openssl version. https://stackoverflow.com/a/39641378/4317734
|
||||
openssl aes-256-cbc -d -in .circleci/gcp_token -md md5 -k "$CI_REPO_NAME" -out /home/circleci/.gcp_credentials
|
||||
echo "export GOOGLE_APPLICATION_CREDENTIALS=/home/circleci/.gcp_credentials" >> $BASH_ENV
|
||||
./.circleci/setup-rbe.sh .bazelrc.user
|
||||
|
||||
# Settings common to each job
|
||||
var_6: &job_defaults
|
||||
working_directory: ~/ng
|
||||
docker:
|
||||
- image: *default_docker_image
|
||||
|
||||
# After checkout, rebase on top of target branch.
|
||||
var_7: &post_checkout
|
||||
run:
|
||||
name: Rebase PR on target branch
|
||||
command: >
|
||||
if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
|
||||
# User is required for rebase.
|
||||
git config user.name "angular-ci"
|
||||
git config user.email "angular-ci"
|
||||
# Rebase PR on top of target branch.
|
||||
node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER}
|
||||
else
|
||||
echo "This build is not over a PR, nothing to do."
|
||||
fi
|
||||
|
||||
var_8: &yarn_install
|
||||
run:
|
||||
name: Running Yarn install
|
||||
command: |
|
||||
# Yarn's requests sometimes take more than 10mins to complete.
|
||||
# Print something to stdout, to prevent CircleCI from failing due to not output.
|
||||
while true; do sleep 60; echo "[`date`] Keeping alive..."; done &
|
||||
KEEP_ALIVE_PID=$!
|
||||
yarn install --frozen-lockfile --non-interactive
|
||||
kill $KEEP_ALIVE_PID
|
||||
|
||||
var_9: &setup_circleci_bazel_config
|
||||
run:
|
||||
name: Setting up CircleCI bazel configuration
|
||||
command: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
|
||||
var_10: &restore_cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- *cache_key
|
||||
# This fallback should be the cache_key without variables.
|
||||
- v3-angular-node-10.16-
|
||||
|
||||
# Branch filter that can be specified for jobs that should only run on publish branches
|
||||
# (e.g. master or the patch branch)
|
||||
var_11: &publish_branches_filter
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
# e.g. 7.0.x, 7.1.x, etc.
|
||||
- /\d+\.\d+\.x/
|
||||
|
||||
# Workspace initially persisted by the `install` job, and then enhanced by `test_aio` and
|
||||
# `build-npm-packages`.
|
||||
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
|
||||
# https://circleci.com/blog/deep-diving-into-circleci-workspaces/
|
||||
var_12: &attach_workspace
|
||||
attach_workspace:
|
||||
at: ~/
|
||||
|
||||
var_13: ¬ify_caretaker_on_fail
|
||||
run:
|
||||
when: on_fail
|
||||
name: Notify caretaker about failure
|
||||
# `$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 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
|
||||
run:
|
||||
when: on_fail
|
||||
name: Notify dev-infra about failure
|
||||
# `$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 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
|
||||
var_4: &cache_key_fallback v3-angular-node-10.16-
|
||||
|
||||
# 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-18b9ef3f5529f0fa8f034944681486447af7b879
|
||||
var_16: &material_unit_tests_cache_key_short v4-angular-material
|
||||
var_5: &material_unit_tests_cache_key v4-angular-material-18b9ef3f5529f0fa8f034944681486447af7b879
|
||||
var_6: &material_unit_tests_cache_key_fallback v4-angular-material-
|
||||
|
||||
version: 2
|
||||
|
||||
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
|
||||
# `build-ivy-npm-packages`.
|
||||
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
|
||||
# https://circleci.com/blog/deep-diving-into-circleci-workspaces/
|
||||
var_7: &workspace_location ~/
|
||||
|
||||
# Executor Definitions
|
||||
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-executors
|
||||
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
|
||||
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
|
||||
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
|
||||
# **NOTE 3**: If you change the version of the `*-browsers` docker image, make sure the
|
||||
# `CI_CHROMEDRIVER_VERSION_ARG` env var (in `.circleci/env.sh`) points to a ChromeDriver
|
||||
# version that is compatible with the Chrome version in the image.
|
||||
executors:
|
||||
default-executor:
|
||||
parameters:
|
||||
resource_class:
|
||||
type: string
|
||||
default: medium
|
||||
docker:
|
||||
- image: circleci/node:10.16@sha256:75c05084fff4afa3683a03c5a04a4a3ad95c536ff2439d8fe14e7e1f5c58b09a
|
||||
resource_class: << parameters.resource_class >>
|
||||
working_directory: ~/ng
|
||||
|
||||
browsers-executor:
|
||||
parameters:
|
||||
resource_class:
|
||||
type: string
|
||||
default: medium
|
||||
docker:
|
||||
# The browser docker image comes with Chrome and Firefox preinstalled. This is just
|
||||
# needed for jobs that run tests without Bazel. Bazel runs tests with browsers that will be
|
||||
# fetched by the Webtesting rules. Therefore for jobs that run tests with Bazel, we don't need a
|
||||
# docker image with browsers pre-installed.
|
||||
- image: circleci/node:10.16-browsers@sha256:d2a96fe1cbef51257ee626b5f645e64dade3e886f00ba9cb7e8ea65b4efe8db1
|
||||
resource_class: << parameters.resource_class >>
|
||||
working_directory: ~/ng
|
||||
|
||||
# Command Definitions
|
||||
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-commands
|
||||
commands:
|
||||
custom_attach_workspace:
|
||||
description: Attach workspace at a predefined location
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: *workspace_location
|
||||
|
||||
# Initializes the CI environment by setting up common environment variables.
|
||||
init_environment:
|
||||
description: Initializing environment (setting up variables, overwriting Yarn)
|
||||
steps:
|
||||
- run: ./.circleci/env.sh
|
||||
- run:
|
||||
# Overwrite the yarn installed in the docker container with our own version.
|
||||
name: Overwrite yarn with our own version
|
||||
command: |
|
||||
ourYarn=$(realpath ./third_party/github.com/yarnpkg/yarn/releases/download/v1.17.3/bin/yarn.js)
|
||||
sudo chmod a+x $ourYarn
|
||||
sudo ln -fs $ourYarn /usr/local/bin/yarn
|
||||
- run: echo "Yarn version $(yarn --version)"
|
||||
- run:
|
||||
# Configure git as the CircleCI `checkout` command does.
|
||||
# This is needed because we only checkout on the setup job.
|
||||
# Add GitHub to known hosts
|
||||
name: Configure git
|
||||
command: |
|
||||
mkdir -p ~/.ssh
|
||||
echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts
|
||||
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
|
||||
git config --global gc.auto 0 || true
|
||||
|
||||
setup_circleci_bazel_config:
|
||||
description: Set up CircleCI bazel configuration
|
||||
steps:
|
||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
|
||||
setup_bazel_rbe:
|
||||
description: Setup bazel RBE remote execution
|
||||
steps:
|
||||
# We need ensure that the same default digest is used for encoding and decoding
|
||||
# with openssl. Openssl versions might have different default digests which can
|
||||
# cause decryption failures based on the openssl version. https://stackoverflow.com/a/39641378/4317734
|
||||
- run: openssl aes-256-cbc -d -in .circleci/gcp_token -md md5 -k "$CI_REPO_NAME" -out /home/circleci/.gcp_credentials
|
||||
- run: echo "export GOOGLE_APPLICATION_CREDENTIALS=/home/circleci/.gcp_credentials" >> $BASH_ENV
|
||||
- run: ./.circleci/setup-rbe.sh .bazelrc.user
|
||||
|
||||
notify_webhook_on_fail:
|
||||
description: Notify a webhook about failure
|
||||
parameters:
|
||||
# `webhook_url_env_var` are secret env vars defined in CircleCI project settings.
|
||||
# The URLs come from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
|
||||
webhook_url_env_var:
|
||||
type: env_var_name
|
||||
steps:
|
||||
- run:
|
||||
when: on_fail
|
||||
command: |
|
||||
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" ${<< parameters.webhook_url_env_var >>}
|
||||
|
||||
# Job definitions
|
||||
# Jobs can include parameters that are passed in the workflow job invocation.
|
||||
# https://circleci.com/docs/2.0/reusing-config/#authoring-parameterized-jobs
|
||||
jobs:
|
||||
setup:
|
||||
<<: *job_defaults
|
||||
executor: default-executor
|
||||
steps:
|
||||
- checkout
|
||||
- *post_checkout
|
||||
- run:
|
||||
name: Rebase PR on target branch
|
||||
# After checkout, rebase on top of target branch.
|
||||
command: >
|
||||
if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
|
||||
# User is required for rebase.
|
||||
git config user.name "angular-ci"
|
||||
git config user.email "angular-ci"
|
||||
# Rebase PR on top of target branch.
|
||||
node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER}
|
||||
else
|
||||
echo "This build is not over a PR, nothing to do."
|
||||
fi
|
||||
# This cache is saved in the build-npm-packages so that Bazel cache is also included.
|
||||
- *restore_cache
|
||||
- *init_environment
|
||||
- *yarn_install
|
||||
- restore_cache:
|
||||
keys:
|
||||
- *cache_key
|
||||
- *cache_key_fallback
|
||||
- init_environment
|
||||
- run:
|
||||
name: Running Yarn install
|
||||
command: yarn install --frozen-lockfile --non-interactive
|
||||
# Yarn's requests sometimes take more than 10mins to complete.
|
||||
no_output_timeout: 45m
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
# Make the bazel directories and add a file to them if they don't exist already so that
|
||||
# persist_to_workspace does not fail.
|
||||
@ -168,19 +172,18 @@ jobs:
|
||||
touch ~/bazel_repository_cache/MARKER
|
||||
fi
|
||||
# Persist any changes at this point to be reused by further jobs.
|
||||
# **NOTE 1 **: Folders persisted here should be kept in sync with `var_13: &attach_workspace`.
|
||||
# **NOTE 2 **: To add new content to the workspace, always persist on the same root.
|
||||
# **NOTE**: To add new content to the workspace, always persist on the same root.
|
||||
- persist_to_workspace:
|
||||
root: ~/
|
||||
root: *workspace_location
|
||||
paths:
|
||||
- ./ng
|
||||
- ./bazel_repository_cache
|
||||
|
||||
lint:
|
||||
<<: *job_defaults
|
||||
executor: default-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
|
||||
- run: 'yarn bazel:format -mode=check ||
|
||||
(echo "BUILD files not formatted. Please run ''yarn bazel:format''" ; exit 1)'
|
||||
@ -192,25 +195,29 @@ jobs:
|
||||
- run: node tools/verify-codeownership
|
||||
|
||||
test:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
executor:
|
||||
name: default-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- setup_circleci_bazel_config
|
||||
# Setup remote execution and run RBE-compatible tests.
|
||||
- *setup_bazel_remote_execution
|
||||
- run: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
|
||||
- setup_bazel_rbe
|
||||
- run:
|
||||
command: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
|
||||
no_output_timeout: 20m
|
||||
|
||||
# Temporary job to test what will happen when we flip the Ivy flag to true
|
||||
test_ivy_aot:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
executor:
|
||||
name: default-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- *setup_bazel_remote_execution
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- setup_circleci_bazel_config
|
||||
- setup_bazel_rbe
|
||||
|
||||
# We need to explicitly specify the --symlink_prefix option because otherwise we would
|
||||
# not be able to easily find the output bin directory when uploading artifacts for size
|
||||
@ -242,16 +249,17 @@ jobs:
|
||||
#
|
||||
# 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
|
||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
||||
resource_class: xlarge
|
||||
executor:
|
||||
name: default-executor
|
||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- *setup_bazel_remote_execution
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- setup_circleci_bazel_config
|
||||
- setup_bazel_rbe
|
||||
- run:
|
||||
name: Run Bazel tests in saucelabs
|
||||
# All web tests are contained within a single //:test_web_all target for Saucelabs
|
||||
@ -266,16 +274,16 @@ jobs:
|
||||
--username $SAUCE_USERNAME \
|
||||
--key $(echo $SAUCE_ACCESS_KEY | rev) \
|
||||
yarn bazel test //:test_web_all
|
||||
- *notify_dev_infra_on_fail
|
||||
no_output_timeout: 20m
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
|
||||
test_aio:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
- image: *browsers_docker_image
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
executor: browsers-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Build aio
|
||||
- run: yarn --cwd aio build --progress=false
|
||||
# Lint the code
|
||||
@ -294,27 +302,27 @@ jobs:
|
||||
- run: yarn --cwd aio redirects-test
|
||||
|
||||
deploy_aio:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because before deploying the deploy-production script runs the PWA score tests.
|
||||
- image: *browsers_docker_image
|
||||
executor: browsers-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Deploy angular.io to production (if necessary)
|
||||
- run: setPublicVar_CI_STABLE_BRANCH
|
||||
- run: yarn --cwd aio deploy-production
|
||||
|
||||
test_aio_local:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
- image: *browsers_docker_image
|
||||
parameters:
|
||||
ivy:
|
||||
type: boolean
|
||||
default: false
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
executor: browsers-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Build aio (with local Angular packages)
|
||||
- run: yarn --cwd aio build-local-ci
|
||||
- run: yarn --cwd aio build-local<<# parameters.ivy >>-with-ivy<</ parameters.ivy >>-ci
|
||||
# Run unit tests
|
||||
- run: yarn --cwd aio test --progress=false --watch=false
|
||||
# Run e2e tests
|
||||
@ -322,32 +330,13 @@ jobs:
|
||||
# Run PWA-score tests
|
||||
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
|
||||
# Check the bundle sizes.
|
||||
- run: yarn --cwd aio payload-size aio-local
|
||||
|
||||
test_aio_local_ivy:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
- image: *browsers_docker_image
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Build aio with Ivy (using local Angular packages)
|
||||
- run: yarn --cwd aio build-with-ivy-ci
|
||||
# Run unit tests
|
||||
- run: yarn --cwd aio test --progress=false --watch=false
|
||||
# Run e2e tests
|
||||
- run: yarn --cwd aio e2e --configuration=ci
|
||||
# Run PWA-score tests
|
||||
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
|
||||
# Check the bundle sizes.
|
||||
- run: yarn --cwd aio payload-size aio-local-ivy
|
||||
- run: yarn --cwd aio payload-size aio-local<<# parameters.ivy >>-ivy<</ parameters.ivy >>
|
||||
|
||||
test_aio_tools:
|
||||
<<: *job_defaults
|
||||
executor: default-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Install
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
- run: yarn --cwd aio extract-cli-command-docs
|
||||
@ -356,56 +345,42 @@ jobs:
|
||||
- run: ./aio/aio-builds-setup/scripts/test.sh
|
||||
|
||||
test_docs_examples:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
parameters:
|
||||
ivy:
|
||||
type: boolean
|
||||
default: false
|
||||
executor:
|
||||
# Needed because the example e2e tests depend on Chrome.
|
||||
- image: *browsers_docker_image
|
||||
parallelism: 4
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Install aio
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
# Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
||||
# Since the parallelism is set to "3", there will be three parallel CircleCI containers
|
||||
# with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument.
|
||||
- run: yarn --cwd aio example-e2e --setup --local --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
|
||||
|
||||
test_docs_examples_ivy:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because the example e2e tests depend on Chrome.
|
||||
- image: *browsers_docker_image
|
||||
resource_class: xlarge
|
||||
# We increase the parallelism here to five while the "test_docs_examples" job runs with
|
||||
# a parallelism of four. This is necessary because this job also need to run NGCC which
|
||||
# takes up more time and we don't want these jobs to impact the overall CI turnaround.
|
||||
name: browsers-executor
|
||||
resource_class: xlarge
|
||||
parallelism: 5
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Install aio
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
# Rename the Ivy packages dist folder to "dist/packages-dist" as the AIO
|
||||
# package installer picks up the locally built packages from that location.
|
||||
# *Note*: We could also adjust the packages installer, but given we won't have
|
||||
# two different folders of Angular distributions in the future, we should keep
|
||||
# the packages installer unchanged.
|
||||
- run: mv dist/packages-dist-ivy-aot dist/packages-dist
|
||||
# Run examples tests with ivy. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
||||
# Since the parallelism is set to "3", there will be three parallel CircleCI containers
|
||||
# with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument.
|
||||
- run: yarn --cwd aio example-e2e --setup --local --ivy --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
|
||||
- when:
|
||||
condition: << parameters.ivy >>
|
||||
steps:
|
||||
# Rename the Ivy packages dist folder to "dist/packages-dist" as the AIO
|
||||
# package installer picks up the locally built packages from that location.
|
||||
# *Note*: We could also adjust the packages installer, but given we won't have
|
||||
# two different folders of Angular distributions in the future, we should keep
|
||||
# the packages installer unchanged.
|
||||
- run: mv dist/packages-dist-ivy-aot dist/packages-dist
|
||||
# Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
||||
# Since the parallelism is set to "5", there will be five parallel CircleCI containers.
|
||||
# with either "0", "1", etc as node index. This can be passed to the "--shard" argument.
|
||||
- run: yarn --cwd aio example-e2e --setup --local <<# parameters.ivy >>--ivy<</ parameters.ivy >> --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
|
||||
|
||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||
aio_preview:
|
||||
<<: *job_defaults
|
||||
executor: default-executor
|
||||
environment:
|
||||
AIO_SNAPSHOT_ARTIFACT_PATH: &aio_preview_artifact_path 'aio/tmp/snapshot.tgz'
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run: ./aio/scripts/build-artifacts.sh $AIO_SNAPSHOT_ARTIFACT_PATH $CI_PULL_REQUEST $CI_COMMIT
|
||||
- store_artifacts:
|
||||
path: *aio_preview_artifact_path
|
||||
@ -416,13 +391,11 @@ jobs:
|
||||
|
||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||
test_aio_preview:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because the test-preview script runs e2e tests and the PWA score test with Chrome.
|
||||
- image: *browsers_docker_image
|
||||
# Needed because the test-preview script runs e2e tests and the PWA score test with Chrome.
|
||||
executor: browsers-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
- run:
|
||||
name: Wait for preview and run tests
|
||||
@ -438,19 +411,20 @@ jobs:
|
||||
|
||||
# Build the view engine npm packages. No new jobs should depend on this.
|
||||
build-npm-packages:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
executor:
|
||||
name: default-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- *setup_bazel_remote_execution
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- setup_circleci_bazel_config
|
||||
- setup_bazel_rbe
|
||||
|
||||
- run: scripts/build-packages-dist.sh
|
||||
|
||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
||||
- persist_to_workspace:
|
||||
root: ~/
|
||||
root: *workspace_location
|
||||
paths:
|
||||
- ng/dist/packages-dist
|
||||
|
||||
@ -464,19 +438,20 @@ jobs:
|
||||
|
||||
# Build the ivy npm packages.
|
||||
build-ivy-npm-packages:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
executor:
|
||||
name: default-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- *setup_bazel_remote_execution
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- setup_circleci_bazel_config
|
||||
- setup_bazel_rbe
|
||||
|
||||
- run: scripts/build-ivy-npm-packages.sh
|
||||
|
||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
||||
- persist_to_workspace:
|
||||
root: ~/
|
||||
root: *workspace_location
|
||||
paths:
|
||||
- ng/dist/packages-dist-ivy-aot
|
||||
|
||||
@ -487,18 +462,17 @@ jobs:
|
||||
# need to re-run manually should be alleviated.
|
||||
# See comments inside the integration/run_tests.sh script.
|
||||
integration_test:
|
||||
<<: *job_defaults
|
||||
parallelism: 4
|
||||
docker:
|
||||
executor:
|
||||
# Needed because the integration tests expect Chrome to be installed (e.g cli-hello-world)
|
||||
- image: *browsers_docker_image
|
||||
# Note: we run Bazel in one of the integration tests, and it can consume >2G
|
||||
# of memory. Together with the system under test, this can exhaust the RAM
|
||||
# on a 4G worker so we use a larger machine here too.
|
||||
resource_class: xlarge
|
||||
name: browsers-executor
|
||||
# Note: we run Bazel in one of the integration tests, and it can consume >2G
|
||||
# of memory. Together with the system under test, this can exhaust the RAM
|
||||
# on a 4G worker so we use a larger machine here too.
|
||||
resource_class: xlarge
|
||||
parallelism: 4
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Runs the integration tests in parallel across multiple CircleCI container instances. The
|
||||
# amount of container nodes for this job is controlled by the "parallelism" option.
|
||||
- run: ./integration/run_tests.sh ${CIRCLE_NODE_INDEX} ${CIRCLE_NODE_TOTAL}
|
||||
@ -506,7 +480,7 @@ jobs:
|
||||
# This job updates the content of repos like github.com/angular/core-builds
|
||||
# for every green build on angular/angular.
|
||||
publish_snapshot:
|
||||
<<: *job_defaults
|
||||
executor: default-executor
|
||||
steps:
|
||||
# See below - ideally this job should not trigger for non-upstream builds.
|
||||
# But since it does, we have to check this condition.
|
||||
@ -520,8 +494,8 @@ jobs:
|
||||
[[ "$CIRCLE_PROJECT_REPONAME" != "angular" ]]; then
|
||||
circleci step halt
|
||||
fi
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# CircleCI has a config setting to force SSH for all github connections
|
||||
# This is not compatible with our mechanism of using a Personal Access Token
|
||||
# Clear the global setting
|
||||
@ -535,14 +509,12 @@ jobs:
|
||||
- run: ./scripts/ci/publish-build-artifacts.sh
|
||||
|
||||
aio_monitoring_stable:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# This job needs Chrome to be globally installed because the tests run with Protractor
|
||||
# which does not load the browser through the Bazel webtesting rules.
|
||||
- image: *browsers_docker_image
|
||||
# This job needs Chrome to be globally installed because the tests run with Protractor
|
||||
# which does not load the browser through the Bazel webtesting rules.
|
||||
executor: browsers-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run: setPublicVar_CI_STABLE_BRANCH
|
||||
- run:
|
||||
name: Check out `aio/` from the stable branch
|
||||
@ -552,33 +524,36 @@ jobs:
|
||||
- run:
|
||||
name: Run tests against https://angular.io/
|
||||
command: ./aio/scripts/test-production.sh https://angular.io/ $CI_AIO_MIN_PWA_SCORE
|
||||
- *notify_caretaker_on_fail
|
||||
- *notify_dev_infra_on_fail
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_CARETAKER_WEBHOOK_URL
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
|
||||
aio_monitoring_next:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# This job needs Chrome to be globally installed because the tests run with Protractor
|
||||
# which does not load the browser through the Bazel webtesting rules.
|
||||
- image: *browsers_docker_image
|
||||
# This job needs Chrome to be globally installed because the tests run with Protractor
|
||||
# which does not load the browser through the Bazel webtesting rules.
|
||||
executor: browsers-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run:
|
||||
name: Run tests against https://next.angular.io/
|
||||
command: ./aio/scripts/test-production.sh https://next.angular.io/ $CI_AIO_MIN_PWA_SCORE
|
||||
- *notify_caretaker_on_fail
|
||||
- *notify_dev_infra_on_fail
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_CARETAKER_WEBHOOK_URL
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
|
||||
legacy-unit-tests-saucelabs:
|
||||
<<: *job_defaults
|
||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
||||
resource_class: xlarge
|
||||
executor:
|
||||
name: default-executor
|
||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run:
|
||||
name: Preparing environment for running tests on Saucelabs.
|
||||
command: |
|
||||
@ -597,10 +572,10 @@ jobs:
|
||||
- run: ./scripts/saucelabs/stop-tunnel.sh
|
||||
|
||||
legacy-misc-tests:
|
||||
<<: *job_defaults
|
||||
executor: default-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run: yarn gulp check-cycle
|
||||
# TODO: disabled because the Bazel packages-dist does not seem to have map files for
|
||||
# the ESM5/ES2015 output. See: https://github.com/angular/angular/issues/27966
|
||||
@ -609,23 +584,22 @@ jobs:
|
||||
# Job to run unit tests from angular/material2. Needs a browser since all
|
||||
# component unit tests assume they're running in the browser environment.
|
||||
material-unit-tests:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
docker:
|
||||
- image: *browsers_docker_image
|
||||
executor:
|
||||
name: browsers-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Although RBE is configured below for the Material repo, also setup RBE in the Angular repo
|
||||
# to provision Angular's GCP token into the environment variables.
|
||||
- *setup_bazel_remote_execution
|
||||
- setup_bazel_rbe
|
||||
# Restore the cache before cloning the repository because the clone script re-uses
|
||||
# the restored repository if present. This reduces the amount of times the components
|
||||
# repository needs to be cloned (this is slow and increases based on commits in the repo).
|
||||
- restore_cache:
|
||||
keys:
|
||||
- *material_unit_tests_cache_key
|
||||
- *material_unit_tests_cache_key_short
|
||||
- *material_unit_tests_cache_key_fallback
|
||||
- run:
|
||||
name: "Fetching Material repository"
|
||||
command: ./scripts/ci/clone_angular_material_repo.sh
|
||||
@ -648,10 +622,14 @@ jobs:
|
||||
command: ./scripts/ci/run_angular_material_unit_tests.sh
|
||||
|
||||
test_zonejs:
|
||||
<<: *job_defaults
|
||||
executor:
|
||||
name: default-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- setup_circleci_bazel_config
|
||||
- setup_bazel_rbe
|
||||
# Install
|
||||
- run: yarn --cwd packages/zone.js install --frozen-lockfile --non-interactive
|
||||
# Run zone.js tools tests
|
||||
@ -710,7 +688,9 @@ workflows:
|
||||
- test_aio_local:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- test_aio_local_ivy:
|
||||
- test_aio_local:
|
||||
name: test_aio_local_ivy
|
||||
ivy: true
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- test_aio_tools:
|
||||
@ -719,7 +699,9 @@ workflows:
|
||||
- test_docs_examples:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- test_docs_examples_ivy:
|
||||
- test_docs_examples:
|
||||
name: test_docs_examples_ivy
|
||||
ivy: true
|
||||
requires:
|
||||
- build-ivy-npm-packages
|
||||
- aio_preview:
|
||||
|
76
.github/CODEOWNERS
vendored
76
.github/CODEOWNERS
vendored
@ -367,6 +367,43 @@
|
||||
* @IgorMinar @angular/framework-global-approvers
|
||||
|
||||
|
||||
# ================================================
|
||||
# Build, CI & Dev-infra Owners
|
||||
# ================================================
|
||||
|
||||
/* @angular/dev-infra-framework
|
||||
/.buildkite/** @angular/dev-infra-framework
|
||||
/.circleci/** @angular/dev-infra-framework
|
||||
/.devcontainer/** @angular/dev-infra-framework
|
||||
/.github/** @angular/dev-infra-framework
|
||||
/.vscode/** @angular/dev-infra-framework
|
||||
/docs/BAZEL.md @angular/dev-infra-framework
|
||||
/packages/* @angular/dev-infra-framework
|
||||
/packages/examples/test-utils/** @angular/dev-infra-framework
|
||||
/packages/private/** @angular/dev-infra-framework
|
||||
/scripts/** @angular/dev-infra-framework
|
||||
/third_party/** @angular/dev-infra-framework
|
||||
/tools/build/** @angular/dev-infra-framework
|
||||
/tools/cjs-jasmine/** @angular/dev-infra-framework
|
||||
/tools/gulp-tasks/** @angular/dev-infra-framework
|
||||
/tools/ngcontainer/** @angular/dev-infra-framework
|
||||
/tools/npm/** @angular/dev-infra-framework
|
||||
/tools/npm_workspace/** @angular/dev-infra-framework
|
||||
/tools/public_api_guard/** @angular/dev-infra-framework
|
||||
/tools/rxjs/** @angular/dev-infra-framework
|
||||
/tools/size-tracking/** @angular/dev-infra-framework
|
||||
/tools/source-map-test/** @angular/dev-infra-framework
|
||||
/tools/symbol-extractor/** @angular/dev-infra-framework
|
||||
/tools/testing/** @angular/dev-infra-framework
|
||||
/tools/ts-api-guardian/** @angular/dev-infra-framework
|
||||
/tools/tslint/** @angular/dev-infra-framework
|
||||
/tools/validate-commit-message/** @angular/dev-infra-framework
|
||||
/tools/yarn/** @angular/dev-infra-framework
|
||||
/tools/* @angular/dev-infra-framework
|
||||
*.BAZEL @angular/dev-infra-framework
|
||||
*.bzl @angular/dev-infra-framework
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
# @angular/animations
|
||||
@ -841,8 +878,7 @@ testing/** @angular/fw-test
|
||||
/aio/content/guide/updating.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/workspace-config.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/deprecations.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/migration-renderer.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/migration-undecorated-classes.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
@ -878,42 +914,6 @@ testing/** @angular/fw-test
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
# Build, CI & Dev-infra Owners
|
||||
# ================================================
|
||||
|
||||
/* @angular/dev-infra-framework
|
||||
/.buildkite/** @angular/dev-infra-framework
|
||||
/.circleci/** @angular/dev-infra-framework
|
||||
/.devcontainer/** @angular/dev-infra-framework
|
||||
/.github/** @angular/dev-infra-framework
|
||||
/.vscode/** @angular/dev-infra-framework
|
||||
/docs/BAZEL.md @angular/dev-infra-framework
|
||||
/packages/* @angular/dev-infra-framework
|
||||
/packages/examples/test-utils/** @angular/dev-infra-framework
|
||||
/packages/private/** @angular/dev-infra-framework
|
||||
/scripts/** @angular/dev-infra-framework
|
||||
/third_party/** @angular/dev-infra-framework
|
||||
/tools/build/** @angular/dev-infra-framework
|
||||
/tools/cjs-jasmine/** @angular/dev-infra-framework
|
||||
/tools/gulp-tasks/** @angular/dev-infra-framework
|
||||
/tools/ngcontainer/** @angular/dev-infra-framework
|
||||
/tools/npm/** @angular/dev-infra-framework
|
||||
/tools/npm_workspace/** @angular/dev-infra-framework
|
||||
/tools/public_api_guard/** @angular/dev-infra-framework
|
||||
/tools/rxjs/** @angular/dev-infra-framework
|
||||
/tools/source-map-test/** @angular/dev-infra-framework
|
||||
/tools/symbol-extractor/** @angular/dev-infra-framework
|
||||
/tools/testing/** @angular/dev-infra-framework
|
||||
/tools/ts-api-guardian/** @angular/dev-infra-framework
|
||||
/tools/tslint/** @angular/dev-infra-framework
|
||||
/tools/validate-commit-message/** @angular/dev-infra-framework
|
||||
/tools/yarn/** @angular/dev-infra-framework
|
||||
/tools/* @angular/dev-infra-framework
|
||||
*.bzl @angular/dev-infra-framework
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
# Public API
|
||||
# ================================================
|
||||
|
58
CHANGELOG.md
58
CHANGELOG.md
@ -1,3 +1,61 @@
|
||||
<a name="8.2.14"></a>
|
||||
## [8.2.14](https://github.com/angular/angular/compare/8.2.13...8.2.14) (2019-11-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** exclude [@angular](https://github.com/angular)/cli from metadata build ([b43ae44](https://github.com/angular/angular/commit/b43ae44)), closes [#33502](https://github.com/angular/angular/issues/33502)
|
||||
* **service-worker:** ensure initialization before handling messages ([#32525](https://github.com/angular/angular/issues/32525)) ([2840670](https://github.com/angular/angular/commit/2840670)), closes [#25611](https://github.com/angular/angular/issues/25611)
|
||||
|
||||
|
||||
|
||||
<a name="8.2.13"></a>
|
||||
## [8.2.13](https://github.com/angular/angular/compare/8.2.12...8.2.13) (2019-10-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** i18n - ignore `alt-trans` tags in XLIFF 1.2 ([#33464](https://github.com/angular/angular/issues/33464)) ([f97c464](https://github.com/angular/angular/commit/f97c464)), closes [#33161](https://github.com/angular/angular/issues/33161)
|
||||
|
||||
|
||||
|
||||
<a name="8.2.12"></a>
|
||||
## [8.2.12](https://github.com/angular/angular/compare/8.2.11...8.2.12) (2019-10-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **upgrade:** remove unused version export ([#33180](https://github.com/angular/angular/issues/33180)) ([37cbcfa](https://github.com/angular/angular/commit/37cbcfa))
|
||||
|
||||
|
||||
|
||||
<a name="8.2.11"></a>
|
||||
## [8.2.11](https://github.com/angular/angular/compare/8.2.10...8.2.11) (2019-10-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **service-worker:** continue serving api requests on cache failure ([#33165](https://github.com/angular/angular/issues/33165)) ([a2716ac](https://github.com/angular/angular/commit/a2716ac)), closes [#32996](https://github.com/angular/angular/issues/32996) [#21412](https://github.com/angular/angular/issues/21412)
|
||||
|
||||
|
||||
|
||||
<a name="8.2.10"></a>
|
||||
## [8.2.10](https://github.com/angular/angular/compare/8.2.9...8.2.10) (2019-10-09)
|
||||
|
||||
This release contains various API docs improvements.
|
||||
|
||||
|
||||
|
||||
<a name="8.2.9"></a>
|
||||
## [8.2.9](https://github.com/angular/angular/compare/8.2.8...8.2.9) (2019-10-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **upgrade:** fix AngularJsUrlCodec to support Safari ([#32959](https://github.com/angular/angular/issues/32959)) ([57457fb](https://github.com/angular/angular/commit/57457fb))
|
||||
|
||||
|
||||
|
||||
<a name="8.2.8"></a>
|
||||
## [8.2.8](https://github.com/angular/angular/compare/8.2.7...8.2.8) (2019-09-25)
|
||||
|
||||
|
@ -201,7 +201,7 @@ Must be one of the following:
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
|
||||
### Scope
|
||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages.
|
||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
|
||||
|
||||
The following is the list of supported scopes:
|
||||
|
||||
|
@ -18,7 +18,7 @@ Here are the most important tasks you might need to use:
|
||||
|
||||
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
|
||||
* `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
|
||||
* `yarn build-with-ivy` - same as `build-local`, but in addition also turns on `ivy` mode in aio.
|
||||
* `yarn build-local-with-ivy` - same as `build-local`, but in addition also turns on `ivy` mode in aio.
|
||||
(Note: To turn on `ivy` mode in examples, see `yarn boilerplate:add` below.)
|
||||
|
||||
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
|
||||
@ -34,17 +34,18 @@ Here are the most important tasks you might need to use:
|
||||
* `yarn docs-test` - run the unit tests for the doc generation code.
|
||||
|
||||
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally.
|
||||
- Add the option `--local` to use your local version of Angular contained in the "dist" folder.
|
||||
- Add the option `--ivy` to turn on `ivy` mode.
|
||||
* `yarn boilerplate:add:ivy` - same as `boilerplate:add` but also turns on `ivy` mode.
|
||||
|
||||
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
|
||||
* `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
|
||||
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
|
||||
|
||||
* `yarn example-e2e` - run all e2e tests for examples
|
||||
- `yarn example-e2e --setup` - force webdriver update & other setup, then run tests
|
||||
- `yarn example-e2e --filter=foo` - limit e2e tests to those containing the word "foo"
|
||||
- `yarn example-e2e --setup --local` - run e2e tests with the local version of Angular contained in the "dist" folder
|
||||
* `yarn example-e2e` - run all e2e tests for examples. Available options:
|
||||
- `--setup`: generate boilerplate, force webdriver update & other setup, then run tests.
|
||||
- `--local`: run e2e tests with the local version of Angular contained in the "dist" folder.
|
||||
_Requires `--setup` in order to take effect._
|
||||
- `--ivy`: run e2e tests in `ivy` mode.
|
||||
- `--filter=foo`: limit e2e tests to those containing the word "foo".
|
||||
|
||||
> **Note for Windows users**
|
||||
>
|
||||
|
@ -206,6 +206,7 @@ function heroModuleSetup() {
|
||||
nameInput.value = 'quick BROWN fOx';
|
||||
|
||||
// dispatch a DOM event so that Angular learns of input value change.
|
||||
// use newEvent utility function (not provided by Angular) for better browser compatibility
|
||||
nameInput.dispatchEvent(newEvent('input'));
|
||||
|
||||
// Tell Angular to update the display binding through the title pipe
|
||||
|
@ -28,7 +28,7 @@ export class KeyUpComponent_v1 {
|
||||
// #docregion key-up-component-1-class
|
||||
|
||||
onKey(event: KeyboardEvent) { // with type info
|
||||
this.values += (<HTMLInputElement>event.target).value + ' | ';
|
||||
this.values += (event.target as HTMLInputElement).value + ' | ';
|
||||
}
|
||||
// #docregion key-up-component-1-class-no-type
|
||||
}
|
||||
|
@ -1148,9 +1148,7 @@ The Angular code is shown using TypeScript.
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="class"></code-example>
|
||||
|
||||
|
||||
In Angular, you create a component class.
|
||||
|
||||
NOTE: If you are using TypeScript with AngularJS, you must use the `export` keyword to export the component class.
|
||||
In Angular, you create a component class to contain the data model and control methods. Use the TypeScript <code>export</code> keyword to export the class so that the functionality can be imported into NgModules.
|
||||
|
||||
For more information, see the [Components](guide/architecture#components)
|
||||
section of the [Architecture Overview](guide/architecture) page.
|
||||
|
@ -44,7 +44,7 @@ For example:
|
||||
}
|
||||
```
|
||||
|
||||
For more informaton, see the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
|
||||
For more information, see the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
|
||||
|
||||
## Template options
|
||||
|
||||
|
@ -69,8 +69,10 @@ Let's animate a simple transition that changes a single HTML element from one st
|
||||
|
||||
In HTML, these attributes are set using ordinary CSS styles such as color and opacity. In Angular, use the `style()` function to specify a set of CSS styles for use with animations. You can collect a set of styles in an animation state, and give the state a name, such as `open` or `closed`.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/animations/open-closed.png" alt="open and closed states">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/animations/open-closed.png" alt="open and closed states">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
### Animation state and styles
|
||||
@ -93,7 +95,7 @@ In the `closed` state, shown below, the button has a height of 100 pixels, an op
|
||||
|
||||
In Angular, you can set multiple styles without any animation. However, without further refinement, the button instantly transforms with no fade, no shrinkage, or other visible indicator that a change is occurring.
|
||||
|
||||
To make the change less abrupt, we need to define an animation *transition* to specify the changes that occur between one state and another over a period of time. The `transition()` function accepts two arguments: the first argument accepts an expression that defines the direction between two transition states, and the second argument accepts an `animate()` function.
|
||||
To make the change less abrupt, we need to define an animation *transition* to specify the changes that occur between one state and another over a period of time. The `transition()` function accepts two arguments: the first argument accepts an expression that defines the direction between two transition states, and the second argument accepts one or a series of `animate()` steps.
|
||||
|
||||
|
||||
Use the `animate()` function to define the length, delay, and easing of a transition, and to designate the style function for defining styles while transitions are taking place. You can also use the `animate()` function to define the `keyframes()` function for multi-step animations. These definitions are placed in the second argument of the `animate()` function.
|
||||
@ -166,8 +168,10 @@ The `trigger()` function describes the property name to watch for changes. When
|
||||
|
||||
In this example, we'll name the trigger `openClose`, and attach it to the `button` element. The trigger describes the open and closed states, and the timings for the two transitions.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/animations/triggering-the-animation.png" alt="triggering the animation">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/animations/triggering-the-animation.png" alt="triggering the animation">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
@ -144,7 +144,7 @@ describes the JSON format as a collection of TypeScript interfaces.
|
||||
{@a expression-syntax}
|
||||
### Expression syntax limitations
|
||||
|
||||
The AOT collector only understands a subset of JavaScript.
|
||||
The AOT collector only understands a subset of JavaScript.
|
||||
Define metadata objects with the following limited syntax:
|
||||
|
||||
<style>
|
||||
@ -467,7 +467,7 @@ export class AppComponent {
|
||||
The collector can represent a function call or object creation with `new` as long as the syntax is valid.
|
||||
The compiler, however, can later refuse to generate a call to a _particular_ function or creation of a _particular_ object.
|
||||
|
||||
The compiler can only create instances certain classes, supports only core decorators, and only supports calls to macros (functions or static methods) that return expressions.
|
||||
The compiler can only create instances of certain classes, supports only core decorators, and only supports calls to macros (functions or static methods) that return expressions.
|
||||
* New instances
|
||||
|
||||
The compiler only allows metadata that create instances of the class `InjectionToken` from `@angular/core`.
|
||||
@ -659,7 +659,7 @@ There is no convenient way to describe this constraint to TypeScript and the tem
|
||||
|
||||
The non-null assertion operator should be used sparingly as refactoring of the component might break this constraint.
|
||||
|
||||
In this example it is recommended to include the checking of `address` in the `*ngIf`as shown below:
|
||||
In this example it is recommended to include the checking of `address` in the `*ngIf` as shown below:
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
|
@ -35,7 +35,7 @@ Here's an example of basic metadata for `HeroListComponent`.
|
||||
|
||||
This example shows some of the most useful `@Component` configuration options:
|
||||
|
||||
* `selector`: A CSS selector that tells Angular to create and insert an instance of this component wherever it finds the corresponding tag in template HTML. For example, if an app's HTML contains `<app-hero-list></app-hero-list>`, then
|
||||
* `selector`: A CSS selector that tells Angular to create and insert an instance of this component wherever it finds the corresponding tag in template HTML. For example, if an app's HTML contains `<app-hero-list></app-hero-list>`, then
|
||||
Angular inserts an instance of the `HeroListComponent` view between those tags.
|
||||
|
||||
* `templateUrl`: The module-relative address of this component's HTML template. Alternatively, you can provide the HTML template inline, as the value of the `template` property. This template defines the component's *host view*.
|
||||
@ -51,8 +51,10 @@ You define a component's view with its companion template. A template is a form
|
||||
|
||||
Views are typically arranged hierarchically, allowing you to modify or show and hide entire UI sections or pages as a unit. The template immediately associated with a component defines that component's *host view*. The component can also define a *view hierarchy*, which contains *embedded views*, hosted by other components.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/architecture/component-tree.png" alt="Component tree" class="left">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/architecture/component-tree.png" alt="Component tree" class="left">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
A view hierarchy can include views from components in the same NgModule, but it also can (and often does) include views from components that are defined in different NgModules.
|
||||
@ -81,8 +83,10 @@ Angular supports *two-way data binding*, a mechanism for coordinating the parts
|
||||
|
||||
The following diagram shows the four forms of data binding markup. Each form has a direction: to the DOM, from the DOM, or both.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/architecture/databinding.png" alt="Data Binding" class="left">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/architecture/databinding.png" alt="Data Binding" class="left">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
This example from the `HeroListComponent` template uses three of these forms.
|
||||
@ -110,14 +114,18 @@ as with event binding.
|
||||
Angular processes *all* data bindings once for each JavaScript event cycle,
|
||||
from the root of the application component tree through all child components.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/architecture/component-databinding.png" alt="Data Binding" class="left">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/architecture/component-databinding.png" alt="Data Binding" class="left">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Data binding plays an important role in communication between a template and its component, and is also important for communication between parent and child components.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/architecture/parent-child-binding.png" alt="Parent/Child binding" class="left">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/architecture/parent-child-binding.png" alt="Parent/Child binding" class="left">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
### Pipes
|
||||
|
@ -35,20 +35,20 @@ Here's a simple root NgModule definition.
|
||||
|
||||
NgModules provide a *compilation context* for their components. A root NgModule always has a root component that is created during bootstrap, but any NgModule can include any number of additional components, which can be loaded through the router or created through the template. The components that belong to an NgModule share a compilation context.
|
||||
|
||||
<figure>
|
||||
|
||||
<img src="generated/images/guide/architecture/compilation-context.png" alt="Component compilation context" class="left">
|
||||
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/architecture/compilation-context.png" alt="Component compilation context" class="left">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
A component and its template together define a *view*. A component can contain a *view hierarchy*, which allows you to define arbitrarily complex areas of the screen that can be created, modified, and destroyed as a unit. A view hierarchy can mix views defined in components that belong to different NgModules. This is often the case, especially for UI libraries.
|
||||
|
||||
<figure>
|
||||
|
||||
<img src="generated/images/guide/architecture/view-hierarchy.png" alt="View hierarchy" class="left">
|
||||
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/architecture/view-hierarchy.png" alt="View hierarchy" class="left">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<br class="clear">
|
||||
|
@ -70,8 +70,10 @@ When all requested services have been resolved and returned, Angular can call th
|
||||
|
||||
The process of `HeroService` injection looks something like this.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/architecture/injector-injects.png" alt="Service" class="left">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/architecture/injector-injects.png" alt="Service" class="left">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
### Providing services
|
||||
@ -82,7 +84,7 @@ or you can register providers with specific modules or components.
|
||||
You register providers in the metadata of the service (in the `@Injectable()` decorator),
|
||||
or in the `@NgModule()` or `@Component()` metadata
|
||||
|
||||
* By default, the Angular CLI command [`ng generate service`](cli/generate) registers a provider with the root injector for your service by including provider metadata in the `@Injectable()` decorator. The tutorial uses this method to register the provider of HeroService class definition.
|
||||
* By default, the Angular CLI command [`ng generate service`](cli/generate) registers a provider with the root injector for your service by including provider metadata in the `@Injectable()` decorator. The tutorial uses this method to register the provider of HeroService class definition.
|
||||
|
||||
```
|
||||
@Injectable({
|
||||
|
@ -113,8 +113,10 @@ To define navigation rules, you associate *navigation paths* with your component
|
||||
|
||||
You've learned the basics about the main building blocks of an Angular application. The following diagram shows how these basic pieces are related.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/architecture/overview2.png" alt="overview">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/architecture/overview2.png" alt="overview">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
* Together, a component and template define an Angular view.
|
||||
|
@ -142,7 +142,7 @@ Begin by adding `HostListener` to the list of imported symbols.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
|
||||
Then add two eventhandlers that respond when the mouse enters or leaves,
|
||||
Then add two event handlers that respond when the mouse enters or leaves,
|
||||
each adorned by the `HostListener` decorator.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"></code-example>
|
||||
@ -175,8 +175,10 @@ Here's the updated directive in full:
|
||||
Run the app and confirm that the background color appears when
|
||||
the mouse hovers over the `p` and disappears as it moves out.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
{@a bindings}
|
||||
@ -247,7 +249,7 @@ You get the best of both worlds: the property name you want and the binding synt
|
||||
<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:
|
||||
If someone neglects to bind to `appHighlight`, highlight the host element in red:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"></code-example>
|
||||
|
||||
@ -271,8 +273,10 @@ Revise the `AppComponent.color` so that it has no initial value.
|
||||
|
||||
Here are the harness and directive in action.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
{@a second-property}
|
||||
@ -307,8 +311,10 @@ because you made it _public_ with the `@Input` decorator.
|
||||
|
||||
Here's how the harness should work when you're done coding.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
## Summary
|
||||
|
@ -328,7 +328,7 @@ For more information, see [Autoprefixer documentation](https://autoprefixer.gith
|
||||
You can use the [proxying support](https://webpack.js.org/configuration/dev-server/#devserver-proxy) in the `webpack` dev server to divert certain URLs to a backend server, by passing a file to the `--proxy-config` build option.
|
||||
For example, to divert all calls for `http://localhost:4200/api` to a server running on `http://localhost:3000/api`, take the following steps.
|
||||
|
||||
1. Create a file `proxy.conf.json` in the projects `src/` folder, in the same directory as `package.json`.
|
||||
1. Create a file `proxy.conf.json` in your project's `src/` folder.
|
||||
|
||||
1. Add the following content to the new proxy file:
|
||||
```
|
||||
@ -411,7 +411,7 @@ Proxy log levels are `info` (the default), `debug`, `warn`, `error`, and `silent
|
||||
|
||||
### Proxy multiple entries
|
||||
|
||||
You can proxy multiple entries to the same target by defining the configuration in JavaScript.
|
||||
You can proxy multiple entries to the same target by defining the configuration in JavaScript.
|
||||
|
||||
Set the proxy configuration file to `proxy.conf.js` (instead of `proxy.conf.json`), and specify configuration files as in the following example.
|
||||
|
||||
|
@ -30,7 +30,7 @@ This object contains a Boolean `success` field and an optional `error` field tha
|
||||
|
||||
Angular provides some builders that are used by the CLI for commands such as `ng build`, `ng test`, and `ng lint`.
|
||||
Default target configurations for these and other built-in CLI builders can be found (and customized) in the "architect" section of the [workspace configuration file](guide/workspace-config), `angular.json`.
|
||||
You can also extend and customize Angular by creating your own builders, which you can run using the [`ng run` CLI command](cli/run).
|
||||
You can also extend and customize Angular by creating your own builders, which you can run using the [`ng run` CLI command](cli/run).
|
||||
|
||||
### Builder project structure
|
||||
|
||||
@ -59,8 +59,7 @@ npm install @example/my-builder
|
||||
As an example, let’s create a builder that executes a shell command.
|
||||
To create a builder, use the `createBuilder()` CLI Builder function, and return a `BuilderOutput` object.
|
||||
|
||||
```
|
||||
|
||||
<code-example language="typescript" header="/command/index.ts">
|
||||
import { BuilderOutput, createBuilder } from '@angular-devkit/architect';
|
||||
|
||||
export default createBuilder(_commandBuilder);
|
||||
@ -72,13 +71,13 @@ function _commandBuilder(
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
</code-example>
|
||||
|
||||
Now let’s add some logic to it.
|
||||
The following code retrieves the command and arguments from the user options, spawns the new process, and waits for the process to finish.
|
||||
If the process is successful (returns a code of 0), it resolves the return value.
|
||||
|
||||
```
|
||||
<code-example language="typescript" header="/command/index.ts">
|
||||
import { BuilderOutput, createBuilder } from '@angular-devkit/architect';
|
||||
import * as childProcess from 'child_process';
|
||||
|
||||
@ -95,7 +94,8 @@ function _commandBuilder(
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
</code-example>
|
||||
|
||||
### Handling output
|
||||
|
||||
@ -105,7 +105,7 @@ This also allows the builder itself to be executed in a separate process, even i
|
||||
|
||||
We can retrieve a Logger instance from the context.
|
||||
|
||||
```
|
||||
<code-example language="typescript" header="/command/index.ts">
|
||||
import { BuilderOutput, createBuilder, BuilderContext } from '@angular-devkit/architect';
|
||||
import * as childProcess from 'child_process';
|
||||
|
||||
@ -130,7 +130,7 @@ function _commandBuilder(
|
||||
});
|
||||
}
|
||||
|
||||
```
|
||||
</code-example>
|
||||
|
||||
### Progress and status reporting
|
||||
|
||||
@ -144,10 +144,10 @@ You can see an [example](https://github.com/angular/angular-cli/blob/ba21c855c0c
|
||||
|
||||
In our example, the shell command either finishes or is still executing, so there’s no need for a progress report, but we can report status so that a parent builder that called our builder would know what’s going on.
|
||||
Use the `BuilderContext.reportStatus()` method to generate a status string of any length.
|
||||
(Note that there’s no guarantee that a long string will be shown entirely; it could be cut to fit the UI that displays it.)
|
||||
(Note that there’s no guarantee that a long string will be shown entirely; it could be cut to fit the UI that displays it.)
|
||||
Pass an empty string to remove the status.
|
||||
|
||||
```
|
||||
<code-example language="typescript" header="/command/index.ts">
|
||||
import { BuilderOutput, createBuilder, BuilderContext } from '@angular-devkit/architect';
|
||||
import * as childProcess from 'child_process';
|
||||
|
||||
@ -174,7 +174,8 @@ function _commandBuilder(
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
</code-example>
|
||||
|
||||
## Builder input
|
||||
|
||||
@ -191,8 +192,7 @@ For our example builder, we expect the `options` value to be a `JsonObject` with
|
||||
|
||||
We can provide the following schema for type validation of these values.
|
||||
|
||||
<code-example language="json">
|
||||
|
||||
<code-example language="json" header="command/schema.json">
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"type": "object",
|
||||
@ -222,7 +222,7 @@ To link our builder implementation with its schema and name, we need to create a
|
||||
|
||||
Create a file named `builders.json` file that looks like this.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example language="json" header="builders.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 language="json">
|
||||
<code-example language="json" header="package.json">
|
||||
|
||||
{
|
||||
"name": "@example/command-runner",
|
||||
@ -253,11 +253,11 @@ In the `package.json` file, add a `builders` key that tells the Architect tool w
|
||||
</code-example>
|
||||
|
||||
The official name of our builder is now ` @example/command-runner:command`.
|
||||
The first part of this is the package name (resolved using node resolution), and the second part is the builder name (resolved using the `builders.json` file).
|
||||
The first part of this is the package name (resolved using node resolution), and the second part is the builder name (resolved using the `builders.json` file).
|
||||
|
||||
Using one of our `options` is very straightforward, we did this in the previous section when we accessed `options.command`.
|
||||
|
||||
<code-example language="typescript">
|
||||
<code-example language="typescript" header="/command/index.ts">
|
||||
context.reportStatus(`Executing "${options.command}"...`);
|
||||
const child = childProcess.spawn(options.command, options.args, { stdio: 'pipe' });
|
||||
|
||||
@ -267,14 +267,14 @@ Using one of our `options` is very straightforward, we did this in the previous
|
||||
|
||||
A builder must have a defined target that associates it with a specific input configuration and [project](guide/glossary#project).
|
||||
|
||||
Targets are defined in the `angular.json` [workspace configuration file](guide/workspace-config).
|
||||
Targets are defined in the `angular.json` [CLI configuration file](guide/workspace-config).
|
||||
A target specifies the builder to use, its default options configuration, and named alternative configurations.
|
||||
The Architect tool uses the target definition to resolve input options for a given run.
|
||||
|
||||
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`.
|
||||
By default, for example, the `build` command runs the builder `@angular-devkit/build-angular:browser` to perform the build task, and passes in default option values as specified for the `build` target in `angular.json`.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example language="json" header="angular.json">
|
||||
{
|
||||
"myApp": {
|
||||
...
|
||||
@ -311,7 +311,7 @@ You might also add more alternative configurations to the `build` target, to def
|
||||
|
||||
#### Target strings
|
||||
|
||||
The generic `ng run` CLI command takes as its first argument a target string of the form *project:target[:configuration]*.
|
||||
The generic `ng run` CLI command takes as its first argument a target string of the form *project:target[:configuration]*.
|
||||
|
||||
* *project*: The name of the Angular CLI project that the target is associated with.
|
||||
|
||||
@ -361,7 +361,7 @@ npm install @example/command-runner
|
||||
|
||||
If we create a new project with `ng new builder-test`, the generated `angular.json` file looks something like this, with only default builder configurations.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example language="json" header="angular.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 language="json">
|
||||
<code-example language="json" header="angular.json">
|
||||
|
||||
{
|
||||
"projects": {
|
||||
@ -493,7 +493,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 language="typescript">
|
||||
<code-example language="typescript" header="command/index_spec.ts">
|
||||
|
||||
import { Architect } from '@angular-devkit/architect';
|
||||
import { TestingArchitectHost } from '@angular-devkit/architect/testing';
|
||||
|
@ -26,8 +26,8 @@ Observables are often compared to promises. Here are some key differences:
|
||||
new Observable((observer) => { subscriber_fn });
|
||||
// initiate execution
|
||||
observable.subscribe(() => {
|
||||
// observer handles notifications
|
||||
});
|
||||
// observer handles notifications
|
||||
});
|
||||
</code-example>
|
||||
|
||||
* Promises execute immediately, and just once. The computation of the result is initiated when the promise is created. There is no way to restart work. All `then` clauses (subscriptions) share the same computation.
|
||||
@ -37,8 +37,8 @@ observable.subscribe(() => {
|
||||
new Promise((resolve, reject) => { executer_fn });
|
||||
// handle return value
|
||||
promise.then((value) => {
|
||||
// handle result here
|
||||
});
|
||||
// handle result here
|
||||
});
|
||||
</code-example>
|
||||
|
||||
### Chaining
|
||||
@ -80,7 +80,7 @@ obs.subscribe(() => {
|
||||
|
||||
<code-example hideCopy>
|
||||
promise.then(() => {
|
||||
throw Error('my error');
|
||||
throw Error('my error');
|
||||
});
|
||||
</code-example>
|
||||
|
||||
@ -314,6 +314,3 @@ An observable produces values over time. An array is created as a static set of
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
A basic understanding of the following concepts:
|
||||
A basic understanding of the following concepts:
|
||||
|
||||
* [Introduction to Angular animations](guide/animations)
|
||||
* [Transition and triggers](guide/transition-and-triggers)
|
||||
|
@ -50,8 +50,10 @@ and each iteration's `hero` instance to the child's `hero` property.
|
||||
The running application displays three heroes:
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/component-interaction/parent-to-child.png" alt="Parent-to-child">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/component-interaction/parent-to-child.png" alt="Parent-to-child">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -94,8 +96,10 @@ Here's the `NameParentComponent` demonstrating name variations including a name
|
||||
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/component-interaction/setter.png" alt="Parent-to-child-setter">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/component-interaction/setter.png" alt="Parent-to-child-setter">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -125,7 +129,7 @@ Detect and act upon changes to input property values with the `ngOnChanges()` me
|
||||
|
||||
You may prefer this approach to the property setter when watching multiple, interacting input properties.
|
||||
|
||||
Learn about `ngOnChanges()` in the [LifeCycle Hooks](guide/lifecycle-hooks) chapter.
|
||||
Learn about `ngOnChanges()` in the [Lifecycle Hooks](guide/lifecycle-hooks) chapter.
|
||||
|
||||
</div>
|
||||
|
||||
@ -152,8 +156,10 @@ The `VersionParentComponent` supplies the `minor` and `major` values and binds b
|
||||
Here's the output of a button-pushing sequence:
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/component-interaction/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/component-interaction/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -206,8 +212,10 @@ The framework passes the event argument—represented by `$event`—to t
|
||||
and the method processes it:
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/component-interaction/child-to-parent.gif" alt="Child-to-parent">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/component-interaction/child-to-parent.gif" alt="Child-to-parent">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -268,8 +276,10 @@ uses interpolation to display the child's `seconds` property.
|
||||
Here we see the parent and child working together.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/component-interaction/countdown-timer-anim.gif" alt="countdown timer">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/component-interaction/countdown-timer-anim.gif" alt="countdown timer">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -421,8 +431,10 @@ the parent `MissionControlComponent` and the `AstronautComponent` children,
|
||||
facilitated by the service:
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/component-interaction/bidirectional-service.gif" alt="bidirectional-service">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/component-interaction/bidirectional-service.gif" alt="bidirectional-service">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Creating Libraries
|
||||
# Creating Libraries
|
||||
|
||||
You can create and publish new libraries to extend Angular functionality. If you find that you need to solve the same problem in more than one app (or want to share your solution with other developers), you have a candidate for a library.
|
||||
|
||||
@ -13,9 +13,15 @@ A simple example might be a button that sends users to your company website, tha
|
||||
Use the Angular CLI to generate a new library skeleton with the following command:
|
||||
|
||||
<code-example language="bash">
|
||||
ng new my-workspace --create-application=false
|
||||
cd my-workspace
|
||||
ng generate library my-lib
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<p>You can use the monorepo model to use the same workspace for multiple projects. See <a href="guide/file-structure#multiple-projects">Setting up for a multi-project workspace</a>.</p>
|
||||
</div>
|
||||
|
||||
This creates the `projects/my-lib` folder in your workspace, which contains a component and a service inside an NgModule.
|
||||
The workspace configuration file, `angular.json`, is updated with a project of type 'library'.
|
||||
|
||||
@ -41,7 +47,7 @@ You can build, test, and lint the project with CLI commands:
|
||||
ng lint my-lib
|
||||
</code-example>
|
||||
|
||||
Notice that the configured builder for the project is different from the default builder for app projects.
|
||||
Notice that the configured builder for the project is different from the default builder for app projects.
|
||||
This builder, among other things, ensures that the library is always built with the [AoT compiler](guide/aot-compiler), without the need to specify the `--prod` flag.
|
||||
|
||||
To make library code reusable you must define a public API for it. This "user layer" defines what is available to consumers of your library. A user of your library should be able to access public functionality (such as NgModules, service providers and general utility functions) through a single import path.
|
||||
@ -175,7 +181,7 @@ For instance, if you clone your git repository and run `npm install`, your edito
|
||||
<div class="alert is-helpful">
|
||||
|
||||
When you import something from a library in an Angular app, Angular looks for a mapping between the library name and a location on disk.
|
||||
When you install a library package, the mapping is in the `node_modules` folder. When you build your own library, it has to find the mapping in your `tsconfig` paths.
|
||||
When you install a library package, the mapping is in the `node_modules` folder. When you build your own library, it has to find the mapping in your `tsconfig` paths.
|
||||
|
||||
Generating a library with the Angular CLI automatically adds its path to the `tsconfig` file.
|
||||
The Angular CLI uses the `tsconfig` paths to tell the build system where to find the library.
|
||||
@ -188,8 +194,23 @@ You can rebuild your library whenever you make changes to it, but this extra ste
|
||||
*Incremental builds* functionality improves the library-development experience.
|
||||
Every time a file is changed a partial build is performed that emits the amended files.
|
||||
|
||||
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:
|
||||
Incremental builds can be run as a background process in your dev environment. To take advantage of this feature add the `--watch` flag to the build command:
|
||||
|
||||
<code-example language="bash">
|
||||
ng build my-lib --watch
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
The CLI `build` command uses a different builder and invokes a different build tool for libraries than it does for applications.
|
||||
|
||||
* The build system for apps, `@angular-devkit/build-angular`, is based on `webpack`, and is included in all new Angular CLI projects.
|
||||
* The build system for libraries is based on `ng-packagr`. It is only added to your dependencies when you add a library using `ng generate library my-lib`.
|
||||
|
||||
The two build systems support different things, and even where they support the same things, they do those things differently.
|
||||
This means that the TypeScript source can result in different JavaScript code in a built library than it would in a built application.
|
||||
|
||||
For this reason, an app that depends on a library should only use TypeScript path mappings that point to the *built library*.
|
||||
TypeScript path mappings should *not* point to the library source `.ts` files.
|
||||
|
||||
</div>
|
||||
|
@ -40,8 +40,10 @@ and the framework resolves the nested dependencies.
|
||||
|
||||
When all dependencies are in place, `AppComponent` displays the user information.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection-in-action/logged-in-user.png" alt="Logged In User">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection-in-action/logged-in-user.png" alt="Logged In User">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
{@a service-scope}
|
||||
@ -131,8 +133,10 @@ The template displays this data-bound property.
|
||||
Find this example in <live-example name="dependency-injection-in-action">live code</live-example>
|
||||
and confirm that the three `HeroBioComponent` instances have their own cached hero data.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection-in-action/hero-bios.png" alt="Bios">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection-in-action/hero-bios.png" alt="Bios">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
{@a qualify-dependency-lookup}
|
||||
@ -191,8 +195,10 @@ placing it in the `<ng-content>` slot of the `HeroBioComponent` template.
|
||||
|
||||
The result is shown below, with the hero's telephone number from `HeroContactComponent` projected above the hero description.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection-in-action/hero-bio-and-content.png" alt="bio and contact">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection-in-action/hero-bio-and-content.png" alt="bio and contact">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -221,8 +227,10 @@ When the property is marked as optional, Angular sets `loggerService` to null an
|
||||
|
||||
Here's `HeroBiosAndContactsComponent` in action.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection-in-action/hero-bios-and-contacts.png" alt="Bios with contact into">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection-in-action/hero-bios-and-contacts.png" alt="Bios with contact into">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -232,8 +240,10 @@ until it finds the logger at the `AppComponent` level.
|
||||
The logger logic kicks in and the hero display updates
|
||||
with the "!!!" marker to indicate that the logger was found.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection-in-action/hero-bio-contact-no-host.png" alt="Without @Host">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection-in-action/hero-bio-contact-no-host.png" alt="Without @Host">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -262,7 +272,7 @@ Providers can also be scoped by injector through constructor parameter decorator
|
||||
|
||||
</code-example>
|
||||
|
||||
Using the `@Self` decorator, the injector only looks at the component's injector for its providers. The `@SkipSelf` decorator allows you to skip the local injector and look up in the hierarchy to find a provider that satisfies this dependency. The `sessionStorageService` instance interacts with the `BrowserStorageService` using the `sessionStorage` browser API, while the `localStorageService` skips the local injector and uses the root `BrowserStorageService` that uses the `localStorage` browswer API.
|
||||
Using the `@Self` decorator, the injector only looks at the component's injector for its providers. The `@SkipSelf` decorator allows you to skip the local injector and look up in the hierarchy to find a provider that satisfies this dependency. The `sessionStorageService` instance interacts with the `BrowserStorageService` using the `sessionStorage` browser API, while the `localStorageService` skips the local injector and uses the root `BrowserStorageService` that uses the `localStorage` browser API.
|
||||
|
||||
{@a component-element}
|
||||
|
||||
@ -294,8 +304,10 @@ first without a value (yielding the default color) and then with an assigned col
|
||||
|
||||
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection-in-action/highlight.png" alt="Highlighted bios">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection-in-action/highlight.png" alt="Highlighted bios">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
{@a providers}
|
||||
@ -347,8 +359,10 @@ You learned about some other methods in [Dependency Providers](guide/dependency-
|
||||
The following `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why you need them.
|
||||
It's visually simple: a few properties and the logs produced by a logger.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection-in-action/hero-of-month.png" alt="Hero of the month">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection-in-action/hero-of-month.png" alt="Hero of the month">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The code behind it customizes how and where the DI framework provides dependencies.
|
||||
@ -460,8 +474,10 @@ The following example puts `MinimalLogger` to use in a simplified version of `He
|
||||
|
||||
The `HeroOfTheMonthComponent` constructor's `logger` parameter is typed as `MinimalLogger`, so only the `logs` and `logInfo` members are visible in a TypeScript-aware editor.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection-in-action/minimal-logger-intellisense.png" alt="MinimalLogger restricted API">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection-in-action/minimal-logger-intellisense.png" alt="MinimalLogger restricted API">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -472,8 +488,10 @@ Behind the scenes, Angular sets the `logger` parameter to the full service regis
|
||||
|
||||
This is illustrated in the following image, which displays the logging date.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection-in-action/date-logger-entry.png" alt="DateLoggerService entry">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection-in-action/date-logger-entry.png" alt="DateLoggerService entry">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
</div>
|
||||
@ -556,7 +574,7 @@ as the token for a provider of `LoggerService`.
|
||||
|
||||
An abstract class is usually a base class that you can extend.
|
||||
In this app, however there is no class that inherits from `MinimalLogger`.
|
||||
The `LoggerService` and the `DateLoggerService`could have inherited from `MinimalLogger`,
|
||||
The `LoggerService` and the `DateLoggerService` could have inherited from `MinimalLogger`,
|
||||
or they could have implemented it instead, in the manner of an interface.
|
||||
But they did neither.
|
||||
`MinimalLogger` is used only as a dependency injection token.
|
||||
@ -568,7 +586,7 @@ an interface is not a valid DI token because it is a TypeScript artifact that do
|
||||
Use this abstract class interface to get the strong typing of an interface,
|
||||
and also use it as a provider token in the way you would a normal class.
|
||||
|
||||
A class interface should define *only* the members that its consumers are allowed to call.
|
||||
A class interface should define *only* the members that its consumers are allowed to call.
|
||||
Such a narrowing interface helps decouple the concrete class from its consumers.
|
||||
|
||||
|
||||
@ -627,8 +645,10 @@ and then pass them down to the base class through the constructor.
|
||||
In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent`
|
||||
to display a *sorted* list of heroes.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection-in-action/sorted-heroes.png" alt="Sorted Heroes">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection-in-action/sorted-heroes.png" alt="Sorted Heroes">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The `HeroesBaseComponent` can stand on its own.
|
||||
|
@ -145,8 +145,10 @@ the same way you've done it before.
|
||||
|
||||
Here's *Alex* and family in action.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection-in-action/alex.png" alt="Alex in action">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection-in-action/alex.png" alt="Alex in action">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -201,8 +203,10 @@ which *is* what parent means.
|
||||
Here's *Alice*, *Barry*, and family in action.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection-in-action/alice.png" alt="Alice in action">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection-in-action/alice.png" alt="Alice in action">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -229,7 +233,7 @@ A component that could serve as a parent *should* implement the class interface
|
||||
|
||||
|
||||
|
||||
Doing so adds clarity to the code. But it's not technically necessary.
|
||||
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`.
|
||||
|
||||
@ -279,4 +283,3 @@ Here's a revised version that defaults to `parent` but also accepts an optional
|
||||
And here's how you could use it with a different parent type.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="beth-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
|
||||
|
||||
|
@ -114,7 +114,7 @@ from the injector of its parent NgModule, or from the `root` injector.
|
||||
|
||||
* Learn more about the [different kinds of providers](guide/dependency-injection-providers).
|
||||
|
||||
* Learn more about how the [injector hierarchy](guide/hierarchical-dependency-injection) works.
|
||||
* Learn more about how the [injector hierarchy](guide/hierarchical-dependency-injection) works.
|
||||
|
||||
</div>
|
||||
|
||||
@ -229,7 +229,7 @@ Here is the revised `HeroService` that injects `Logger`, side by side with the p
|
||||
|
||||
The constructor asks for an injected instance of `Logger` and stores it in a private field called `logger`. The `getHeroes()` method logs a message when asked to fetch heroes.
|
||||
|
||||
Notice that the `Logger` service also has the `@Injectable()` decorator, even though it might not need its own dependencies. In fact, the `@Injectable()` decorator is **required for all services**.
|
||||
Notice that the `Logger` service also has the `@Injectable()` decorator, even though it might not need its own dependencies. In fact, the `@Injectable()` decorator is **required for all services**.
|
||||
|
||||
When Angular creates a class whose constructor has parameters, it looks for type and injection metadata about those parameters so that it can inject the correct service.
|
||||
If Angular can't find that parameter information, it throws an error.
|
||||
@ -293,7 +293,7 @@ value of `logger` to null.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
`@Inject()` and `@Optional()` are _parameter decorators_. They alter the way the DI framework provides a dependency, by annotating the dependency parameter on the constructor of the class that requires the dependency.
|
||||
`@Inject()` and `@Optional()` are _parameter decorators_. They alter the way the DI framework provides a dependency, by annotating the dependency parameter on the constructor of the class that requires the dependency.
|
||||
|
||||
Learn more about parameter decorators in [Hierarchical Dependency Injectors](guide/hierarchical-dependency-injection).
|
||||
|
||||
@ -314,5 +314,3 @@ Dive deeper into the capabilities and advanced feature of the Angular DI system
|
||||
* Learn more about [DI tokens and providers](guide/dependency-injection-providers).
|
||||
|
||||
* [Dependency Injection in Action](guide/dependency-injection-in-action) is a cookbook for some of the interesting things you can do with DI.
|
||||
|
||||
|
||||
|
@ -79,6 +79,7 @@ In the table below, you can find a list of packages which implement deployment f
|
||||
| [Now](https://zeit.co/now) | [`@zeit/ng-deploy`](https://npmjs.org/package/@zeit/ng-deploy) |
|
||||
| [Netlify](https://www.netlify.com/) | [`@netlify-builder/deploy`](https://npmjs.org/package/@netlify-builder/deploy) |
|
||||
| [GitHub pages](https://pages.github.com/) | [`angular-cli-ghpages`](https://npmjs.org/package/angular-cli-ghpages) |
|
||||
| [NPM](https://npmjs.com/) | [`ngx-deploy-npm`](https://npmjs.org/package/ngx-deploy-npm) |
|
||||
|
||||
If you're deploying to a self-managed server or there's no builder for your favorite cloud platform, you can either create a builder that allows you to use the `ng deploy` command, or read through this guide to learn how to manually deploy your app.
|
||||
|
||||
@ -201,6 +202,36 @@ modified to serve `index.html`:
|
||||
```
|
||||
|
||||
|
||||
* [Golang](https://golang.org/): create a Golang server using ([gorilla/mux](https://github.com/gorilla/mux)) with a basic Golang file that configures the server `main.go`:
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
var httpPort = "80"
|
||||
var folderDist = "./dist" // ng build output folder
|
||||
|
||||
func serverHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := os.Stat(folderDist + r.URL.Path); err != nil {
|
||||
http.ServeFile(w, r, folderDist+"/index.html")
|
||||
return
|
||||
}
|
||||
http.ServeFile(w, r, folderDist+r.URL.Path)
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := mux.NewRouter()
|
||||
r.NotFoundHandler = r.NewRoute().HandlerFunc(serverHandler).GetHandler()
|
||||
http.Handle("/", r)
|
||||
http.ListenAndServe(":"+httpPort, nil)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
* [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown
|
||||
[here](http://stackoverflow.com/a/26152011/2116927):
|
||||
|
||||
@ -304,7 +335,7 @@ Configure the Angular Router to defer loading of all other modules (and their as
|
||||
or by [_lazy loading_](guide/router#asynchronous-routing "Lazy loading")
|
||||
them on demand.
|
||||
|
||||
<div class="callout is-helpful>
|
||||
<div class="callout is-helpful">
|
||||
|
||||
<header>Don't eagerly import something from a lazy-loaded module</header>
|
||||
|
||||
@ -385,8 +416,10 @@ showing exactly which classes are included in the bundle.
|
||||
|
||||
Here's the output for the _main_ bundle of an example app called `cli-quickstart`.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/deployment/quickstart-sourcemap-explorer.png" alt="quickstart sourcemap explorer">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/deployment/quickstart-sourcemap-explorer.png" alt="quickstart sourcemap explorer">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
{@a base-tag}
|
||||
@ -424,7 +457,7 @@ Even as JavaScript continues to evolve, with new features being introduced, not
|
||||
|
||||
The code you write in development using TypeScript is compiled and bundled into ES2015, the JavaScript syntax that is compatible with most browsers.
|
||||
All modern browsers support ES2015 and beyond, but in most cases, you still have to account for users accessing your application from a browser that doesn't.
|
||||
When targeting older browsers, [polyfills](guide/browser-support#polyfills) can bridge the gap by providing functionality that doesn't exist in the older versions of JavaScript supported by those browsers.
|
||||
When targeting older browsers, [polyfills](guide/browser-support#polyfills) can bridge the gap by providing functionality that doesn't exist in the older versions of JavaScript supported by those browsers.
|
||||
|
||||
To maximize compatibility, you could ship a single bundle that includes all your compiled code, plus any polyfills that may be needed.
|
||||
Users with modern browsers, however, shouldn't have to pay the price of increased bundle size that comes with polyfills they don't need.
|
||||
|
@ -321,7 +321,7 @@ In a typical Angular project, the polyfill is not used in production builds, so
|
||||
{@a static-query-resolution}
|
||||
### `@ViewChild()` / `@ContentChild()` static resolution as the default
|
||||
|
||||
See the [dedicated migration guide for static queries](guide/static-query-migration).
|
||||
See our [dedicated migration guide for static queries](guide/static-query-migration).
|
||||
|
||||
{@a contentchild-input-together}
|
||||
### `@ContentChild()` / `@Input()` used together
|
||||
@ -389,18 +389,6 @@ As of Angular version 8, all `platform-webworker` APIs are deprecated.
|
||||
This includes both packages: `@angular/platform-webworker` and
|
||||
`@angular/platform-webworker-dynamic`.
|
||||
|
||||
|
||||
## Angular version 9 schematics
|
||||
|
||||
{@a renderer-to-renderer2}
|
||||
### Migrating from `Renderer` to `Renderer2`
|
||||
|
||||
See the [dedicated migration guide for Renderer](guide/migration-renderer).
|
||||
|
||||
{@a undecorated-classes}
|
||||
### Migrating undecorated classes
|
||||
See the [dedicated migration guide for undecorated classes](guide/migration-undecorated-classes).
|
||||
|
||||
{@a removed}
|
||||
## Removed APIs
|
||||
|
||||
|
@ -9,8 +9,10 @@ conditionally show a message below the list.
|
||||
The final UI looks like this:
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/displaying-data/final.png" alt="Final UI">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/displaying-data/final.png" alt="Final UI">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -103,8 +105,10 @@ inside the `<app-root>` tag.
|
||||
|
||||
Now run the app. It should display the title and hero name:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/displaying-data/title-and-hero.png" alt="Title and Hero">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/displaying-data/title-and-hero.png" alt="Title and Hero">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -212,8 +216,10 @@ repeat items for any [iterable](https://developer.mozilla.org/en-US/docs/Web/Jav
|
||||
Now the heroes appear in an unordered list.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/displaying-data/hero-names-list.png" alt="After ngfor">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/displaying-data/hero-names-list.png" alt="After ngfor">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -37,7 +37,7 @@ Angular docs are written in Markdown, with custom extensions for this site. Corr
|
||||
|
||||
* **Angular coding style:** Coding style for example apps and code snippets.
|
||||
Code examples are encouraged for demonstrating how to apply the concepts and features discussed.
|
||||
Angular has a custom framework that enables authors to include code snippets directly from example apps that are automatically tested as part of doc builds.
|
||||
Angular has a custom framework that enables authors to include code snippets directly from example apps that are automatically tested as part of doc builds.
|
||||
To contribute example code, you must understand Angular itself and the custom framework for Angular doc examples.
|
||||
|
||||
For each aspect of style, the following table explains where to find the primary guidelines and what this Angular Documentation Style Guide offers.
|
||||
@ -322,6 +322,7 @@ The <code class="no-auto-link">item</code> property is `true`.
|
||||
|
||||
For block code snippets, we generally prefer to display code with
|
||||
the Angular documentation _code-example_ component represented by the `<code-example>` tag.
|
||||
The `<code-example>` tag has a `header` attribute that you use to identify the file that the example comes from. The header should be used whenever possible to establish the context of the example.
|
||||
See [Code snippets and code examples](guide/docs-style-guide#code-snippets-and-code-samples) for more details.
|
||||
|
||||
<h3 class="no-toc">Inline code-snippets</h3>
|
||||
@ -348,6 +349,8 @@ user input in a command shell or the _output_ of some process.
|
||||
**Do not write inline code snippets** unless you have a good reason and the editor's permission to do so.
|
||||
In all other cases, code snippets should be generated automatically from tested code samples.
|
||||
|
||||
For hypothetical examples such as illustrations of configuration options in a JSON file, you should still use The `<code-example>` tag with the `header` attribute to identify the context.
|
||||
|
||||
{@a from-code-samples}
|
||||
|
||||
<h3 class="no-toc">Code snippets and code samples</h3>
|
||||
|
@ -183,8 +183,10 @@ Here are two sample components and the `AdComponent` interface for reference:
|
||||
## Final ad banner
|
||||
The final ad banner looks like this:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dynamic-component-loader/ads-example.gif" alt="Ads">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dynamic-component-loader/ads-example.gif" alt="Ads">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -142,7 +142,7 @@ Notice this component can present any type of question in your model.
|
||||
You only have two types of questions at this point but you can imagine many more.
|
||||
The `ngSwitch` determines which type of question to display.
|
||||
|
||||
In both components you're relying on Angular's **formGroup** to connect the template HTML to the
|
||||
In both components you're relying on Angular's **formGroup** to connect the template HTML to the
|
||||
underlying control objects, populated from the question model with display and validation rules.
|
||||
|
||||
`formControlName` and `formGroup` are directives defined in
|
||||
@ -197,8 +197,10 @@ Saving and retrieving the data is an exercise for another time.
|
||||
|
||||
The final form looks like this:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dynamic-form/dynamic-form.png" alt="Dynamic-Form">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dynamic-form/dynamic-form.png" alt="Dynamic-Form">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -40,12 +40,12 @@ After you register your configured class with the browser's custom-element regis
|
||||
<my-popup message="Use Angular!"></my-popup>
|
||||
```
|
||||
|
||||
When your custom element is placed on a page, the browser creates an instance of the registered class and adds it to the DOM. The content is provided by the component's template, which uses Angular template syntax, and is rendered using the component and DOM data. Input properties in the component correspond to input attributes for the element.
|
||||
|
||||
<figure>
|
||||
|
||||
<img src="generated/images/guide/elements/customElement1.png" alt="Custom element in browser" class="left">
|
||||
When your custom element is placed on a page, the browser creates an instance of the registered class and adds it to the DOM. The content is provided by the component's template, which uses Angular template syntax, and is rendered using the component and DOM data. Input properties in the component correspond to input attributes for the element.
|
||||
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/elements/customElement1.png" alt="Custom element in browser" class="left">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<hr class="clear">
|
||||
@ -64,10 +64,10 @@ Use a JavaScript function, `customElements.define()`, to register the configure
|
||||
and its associated custom-element tag with the browser's `CustomElementRegistry`.
|
||||
When the browser encounters the tag for the registered element, it uses the constructor to create a custom-element instance.
|
||||
|
||||
<figure>
|
||||
|
||||
<img src="generated/images/guide/elements/createElement.png" alt="Transform a component to a custom element" class="left">
|
||||
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/elements/createElement.png" alt="Transform a component to a custom element" class="left">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
### Mapping
|
||||
@ -170,7 +170,7 @@ You can download the full code for the example <live-example downloadOnly>here</
|
||||
|
||||
Generic DOM APIs, such as `document.createElement()` or `document.querySelector()`, return an element type that is appropriate for the specified arguments. For example, calling `document.createElement('a')` will return an `HTMLAnchorElement`, which TypeScript knows has an `href` property. Similarly, `document.createElement('div')` will return an `HTMLDivElement`, which TypeScript knows has no `href` property.
|
||||
|
||||
When called with unknown elements, such as a custom element name (`popup-element` in our example), the methods will return a generic type, such as `HTMLELement`, since TypeScript can't infer the correct type of the returned element.
|
||||
When called with unknown elements, such as a custom element name (`popup-element` in our example), the methods will return a generic type, such as `HTMLElement`, since TypeScript can't infer the correct type of the returned element.
|
||||
|
||||
Custom elements created with Angular extend `NgElement` (which in turn extends `HTMLElement`). Additionally, these custom elements will have a property for each input of the corresponding component. For example, our `popup-element` will have a `message` property of type `string`.
|
||||
|
||||
@ -194,7 +194,7 @@ aDialog.body = 'News'; // <-- ERROR: TypeScript knows there is no `body` proper
|
||||
|
||||
This is a good way to quickly get TypeScript features, such as type checking and autocomplete support, for you custom element. But it can get cumbersome if you need it in several places, because you have to cast the return type on every occurrence.
|
||||
|
||||
An alternative way, that only requires defining each custom element's type once, is augmenting the `HTMLELementTagNameMap`, which TypeScript uses to infer the type of a returned element based on its tag name (for DOM methods such as `document.createElement()`, `document.querySelector()`, etc.):
|
||||
An alternative way, that only requires defining each custom element's type once, is augmenting the `HTMLElementTagNameMap`, which TypeScript uses to infer the type of a returned element based on its tag name (for DOM methods such as `document.createElement()`, `document.querySelector()`, etc.):
|
||||
|
||||
```ts
|
||||
declare global {
|
||||
|
@ -4,7 +4,7 @@ An entry component is any component that Angular loads imperatively, (which mean
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
To contrast the two types of components, there are components which are included in the template, which are declarative. Additionally, there are components which you load imperatively; that is, entry components.
|
||||
To contrast the two types of components, there are components which are included in the template, which are declarative. Additionally, there are components which you load imperatively; that is, entry components.
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -103,8 +103,10 @@ Next, in the `AppComponent`, `app.component.html`, add the tag `<app-customer-da
|
||||
Now, in addition to the title that renders by default, the `CustomerDashboardComponent` template renders too:
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/feature-modules/feature-module.png" alt="feature module component">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/feature-modules/feature-module.png" alt="feature module component">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<hr />
|
||||
|
@ -58,12 +58,12 @@ This initial root-level application is the *default app* for CLI commands (unles
|
||||
|
||||
</div>
|
||||
|
||||
For a single-application workspace, the `src/` subfolder of the workspace contains the source files (application logic, data, and assets) for the root application.
|
||||
For a single-application workspace, the `src/` subfolder of the workspace contains the source files (application logic, data, and assets) for the root application.
|
||||
For a multi-project workspace, additional projects in the `projects/` folder contain a `project-name/src/` subfolder with the same structure.
|
||||
|
||||
### Application source files
|
||||
|
||||
Files at the top level of `src/` support testing and running your application. Subfolders contain the application source and application-specific configuration.
|
||||
Files at the top level of `src/` support testing and running your application. Subfolders contain the application source and application-specific configuration.
|
||||
|
||||
| APP SUPPORT FILES | PURPOSE |
|
||||
| :--------------------- | :------------------------------------------|
|
||||
@ -72,7 +72,7 @@ Files at the top level of `src/` support testing and running your application.
|
||||
| `environments/` | Contains build configuration options for particular target environments. By default there is an unnamed standard development environment and a production ("prod") environment. You can define additional target environment configurations. |
|
||||
| `favicon.ico` | An icon to use for this application in the bookmark bar. |
|
||||
| `index.html` | The main HTML page that is served when someone visits your site. The CLI automatically adds all JavaScript and CSS files when building your app, so you typically don't need to add any `<script>` or` <link>` tags here manually. |
|
||||
| `main.ts` | The main entry point for your application. Compiles the application with the [JIT compiler](https://angular.io/guide/glossary#jit) and bootstraps the application's root module (AppModule) to run in the browser. You can also use the [AOT compiler](https://angular.io/guide/aot-compiler) without changing any code by appending the `--aot` flag to the CLI `build` and `serve` commands. |
|
||||
| `main.ts` | The main entry point for your application. Compiles the application with the [JIT compiler](https://angular.io/guide/glossary#jit) and bootstraps the application's root module (AppModule) to run in the browser. You can also use the [AOT compiler](https://angular.io/guide/aot-compiler) without changing any code by appending the `--aot` flag to the CLI `build` and `serve` commands. |
|
||||
| `polyfills.ts` | Provides polyfill scripts for browser support. |
|
||||
| `styles.sass` | Lists CSS files that supply styles for a project. The extension reflects the style preprocessor you have configured for the project. |
|
||||
| `test.ts` | The main entry point for your unit tests, with some Angular-specific configuration. You don't typically need to edit this file. |
|
||||
@ -84,7 +84,7 @@ Angular components, templates, and styles go here.
|
||||
|
||||
| `src/app/` FILES | PURPOSE |
|
||||
| :-------------------------- | :------------------------------------------|
|
||||
| `app/app.component.ts` | Defines the logic for the app's root component, named `AppComponent`. The view associated with this root component becomes the root of the [view hierarchy](guide/glossary#view-hierarchy) as you add components and services to your application. |
|
||||
| `app/app.component.ts` | Defines the logic for the app's root component, named `AppComponent`. The view associated with this root component becomes the root of the [view hierarchy](guide/glossary#view-hierarchy) as you add components and services to your application. |
|
||||
| `app/app.component.html` | Defines the HTML template associated with the root `AppComponent`. |
|
||||
| `app/app.component.css` | Defines the base CSS stylesheet for the root `AppComponent`. |
|
||||
| `app/app.component.spec.ts` | Defines a unit test for the root `AppComponent`. |
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Introduction to forms in Angular
|
||||
|
||||
Handling user input with forms is the cornerstone of many common applications. Applications use forms to enable users to log in, to update a profile, to enter sensitive information, and to perform many other data-entry tasks.
|
||||
Handling user input with forms is the cornerstone of many common applications. Applications use forms to enable users to log in, to update a profile, to enter sensitive information, and to perform many other data-entry tasks.
|
||||
|
||||
Angular provides two different approaches to handling user input through forms: reactive and template-driven. Both capture user input events from the view, validate the user input, create a form model and data model to update, and provide a way to track changes.
|
||||
Angular provides two different approaches to handling user input through forms: reactive and template-driven. Both capture user input events from the view, validate the user input, create a form model and data model to update, and provide a way to track changes.
|
||||
|
||||
Reactive and template-driven forms process and manage form data differently. Each offers different advantages.
|
||||
|
||||
@ -39,7 +39,7 @@ The table below summarizes the key differences between reactive and template-dri
|
||||
|
||||
## Common foundation
|
||||
|
||||
Both reactive and template-driven forms share underlying building blocks.
|
||||
Both reactive and template-driven forms share underlying building blocks.
|
||||
|
||||
|
||||
* `FormControl` tracks the value and validation status of an individual form control.
|
||||
@ -67,11 +67,13 @@ Here's a component with an input field for a single control implemented using re
|
||||
|
||||
The source of truth provides the value and status of the form element at a given point in time. In reactive forms, the form model is the source of truth. In the example above, the form model is the `FormControl` instance.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms-overview/key-diff-reactive-forms.png" alt="Reactive forms key differences">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms-overview/key-diff-reactive-forms.png" alt="Reactive forms key differences">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
With reactive forms, the form model is explicitly defined in the component class. The reactive form directive (in this case, `FormControlDirective`) then links the existing `FormControl` instance to a specific form element in the view using a value accessor (`ControlValueAccessor` instance).
|
||||
With reactive forms, the form model is explicitly defined in the component class. The reactive form directive (in this case, `FormControlDirective`) then links the existing `FormControl` instance to a specific form element in the view using a value accessor (`ControlValueAccessor` instance).
|
||||
|
||||
### Setup in template-driven forms
|
||||
|
||||
@ -82,11 +84,13 @@ Here's the same component with an input field for a single control implemented u
|
||||
|
||||
In template-driven forms, the source of truth is the template.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms-overview/key-diff-td-forms.png" alt="Template-driven forms key differences">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms-overview/key-diff-td-forms.png" alt="Template-driven forms key differences">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The abstraction of the form model promotes simplicity over structure. The template-driven form directive `NgModel` is responsible for creating and managing the `FormControl` instance for a given form element. It's less explicit, but you no longer have direct control over the form model.
|
||||
The abstraction of the form model promotes simplicity over structure. The template-driven form directive `NgModel` is responsible for creating and managing the `FormControl` instance for a given form element. It's less explicit, but you no longer have direct control over the form model.
|
||||
|
||||
{@a data-flow-in-forms}
|
||||
|
||||
@ -98,8 +102,10 @@ When building forms in Angular, it's important to understand how the framework h
|
||||
|
||||
As described above, in reactive forms each form element in the view is directly linked to a form model (`FormControl` instance). Updates from the view to the model and from the model to the view are synchronous and aren't dependent on the UI rendered. The diagrams below use the same favorite color example to demonstrate how data flows when an input field's value is changed from the view and then from the model.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms-overview/dataflow-reactive-forms-vtm.png" alt="Reactive forms data flow - view to model" width="100%">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms-overview/dataflow-reactive-forms-vtm.png" alt="Reactive forms data flow - view to model" width="100%">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The steps below outline the data flow from view to model.
|
||||
@ -110,8 +116,10 @@ The steps below outline the data flow from view to model.
|
||||
1. The `FormControl` instance emits the new value through the `valueChanges` observable.
|
||||
1. Any subscribers to the `valueChanges` observable receive the new value.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms-overview/dataflow-reactive-forms-mtv.png" alt="Reactive forms data flow - model to view" width="100%">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms-overview/dataflow-reactive-forms-mtv.png" alt="Reactive forms data flow - model to view" width="100%">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The steps below outline the data flow from model to view.
|
||||
@ -125,8 +133,10 @@ The steps below outline the data flow from model to view.
|
||||
|
||||
In template-driven forms, each form element is linked to a directive that manages the form model internally. The diagrams below use the same favorite color example to demonstrate how data flows when an input field's value is changed from the view and then from the model.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms-overview/dataflow-td-forms-vtm.png" alt="Template-driven forms data flow - view to model" width="100%">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms-overview/dataflow-td-forms-vtm.png" alt="Template-driven forms data flow - view to model" width="100%">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The steps below outline the data flow from view to model when the input value changes from *Red* to *Blue*.
|
||||
@ -137,11 +147,13 @@ The steps below outline the data flow from view to model when the input value ch
|
||||
1. The `FormControl` instance emits the new value through the `valueChanges` observable.
|
||||
1. Any subscribers to the `valueChanges` observable receive the new value.
|
||||
1. The control value accessor also calls the `NgModel.viewToModelUpdate()` method which emits an `ngModelChange` event.
|
||||
1. Because the component template uses two-way data binding for the `favoriteColor` property, the `favoriteColor` property in the component
|
||||
is updated to the value emitted by the `ngModelChange` event (*Blue*).
|
||||
1. Because the component template uses two-way data binding for the `favoriteColor` property, the `favoriteColor` property in the component
|
||||
is updated to the value emitted by the `ngModelChange` event (*Blue*).
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms-overview/dataflow-td-forms-mtv.png" alt="Template-driven forms data flow - model to view" width="100%">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms-overview/dataflow-td-forms-mtv.png" alt="Template-driven forms data flow - model to view" width="100%">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The steps below outline the data flow from model to view when the `favoriteColor` changes from *Blue* to *Red*.
|
||||
@ -165,7 +177,7 @@ Validation is an integral part of managing any set of forms. Whether you're chec
|
||||
|
||||
For more information, see [Form Validation](guide/form-validation).
|
||||
|
||||
## Testing
|
||||
## Testing
|
||||
|
||||
Testing plays a large part in complex applications and a simpler testing strategy is useful when validating that your forms function correctly. Reactive forms and template-driven forms have different levels of reliance on rendering the UI to perform assertions based on form control and form field changes. The following examples demonstrate the process of testing forms with reactive and template-driven forms.
|
||||
|
||||
@ -237,7 +249,7 @@ The change tracking method plays a role in the efficiency of your application.
|
||||
|
||||
* **Template-driven** forms rely on mutability with two-way data binding to update the data model in the component as changes are made in the template. Because there are no unique changes to track on the data model when using two-way data binding, change detection is less efficient at determining when updates are required.
|
||||
|
||||
The difference is demonstrated in the examples above using the **favorite color** input element.
|
||||
The difference is demonstrated in the examples above using the **favorite color** input element.
|
||||
|
||||
|
||||
* With reactive forms, the **`FormControl` instance** always returns a new value when the control's value is updated.
|
||||
@ -255,7 +267,7 @@ If forms are a central part of your application, scalability is very important.
|
||||
|
||||
## Final thoughts
|
||||
|
||||
Choosing a strategy begins with understanding the strengths and weaknesses of the options presented. Low-level API and form model access, predictability, mutability, straightforward validation and testing strategies, and scalability are all important considerations in choosing the infrastructure you use to build your forms in Angular. Template-driven forms are similar to patterns in AngularJS, but they have limitations given the criteria of many modern, large-scale Angular apps. Reactive forms minimize these limitations. Reactive forms integrate with reactive patterns already present in other areas of the Angular architecture, and complement those requirements well.
|
||||
Choosing a strategy begins with understanding the strengths and weaknesses of the options presented. Low-level API and form model access, predictability, mutability, straightforward validation and testing strategies, and scalability are all important considerations in choosing the infrastructure you use to build your forms in Angular. Template-driven forms are similar to patterns in AngularJS, but they have limitations given the criteria of many modern, large-scale Angular apps. Reactive forms minimize these limitations. Reactive forms integrate with reactive patterns already present in other areas of the Angular architecture, and complement those requirements well.
|
||||
|
||||
## Next steps
|
||||
|
||||
@ -271,4 +283,3 @@ To learn more about template-driven forms, see the following guides:
|
||||
|
||||
* [Template-driven Forms](guide/forms#template-driven-forms)
|
||||
* [Form Validation](guide/form-validation#template-driven-validation)
|
||||
|
||||
|
@ -45,8 +45,10 @@ otherwise wrestle with yourself.
|
||||
|
||||
You'll learn to build a template-driven form that looks like this:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms/hero-form-1.png" alt="Clean Form">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms/hero-form-1.png" alt="Clean Form">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The *Hero Employment Agency* uses this form to maintain personal information about heroes.
|
||||
@ -56,8 +58,10 @@ Two of the three fields on this form are required. Required fields have a green
|
||||
|
||||
If you delete the hero name, the form displays a validation error in an attention-grabbing style:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms/hero-form-2.png" alt="Invalid, Name Required">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms/hero-form-2.png" alt="Invalid, Name Required">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Note that the *Submit* button is disabled, and the "required" bar to the left of the input control changes from green to red.
|
||||
@ -272,8 +276,10 @@ you display its name using the interpolation syntax.
|
||||
|
||||
Running the app right now would be disappointing.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms/hero-form-3.png" alt="Early form with no binding">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms/hero-form-3.png" alt="Early form with no binding">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -335,8 +341,10 @@ adding and deleting characters, you'd see them appear and disappear
|
||||
from the interpolated text.
|
||||
At some point it might look like this:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms/ng-model-in-action.png" alt="ngModel in action">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms/ng-model-in-action.png" alt="ngModel in action">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The diagnostic is evidence that values really are flowing from the input box to the model and
|
||||
@ -383,8 +391,10 @@ After revision, the core of the form should look like this:
|
||||
|
||||
If you run the app now and change every hero model property, the form might display like this:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms/ng-model-in-action-2.png" alt="ngModel in action">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms/ng-model-in-action-2.png" alt="ngModel in action">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The diagnostic near the top of the form
|
||||
@ -483,14 +493,18 @@ Follow these steps *precisely*:
|
||||
|
||||
The actions and effects are as follows:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms/control-state-transitions-anim.gif" alt="Control State Transition">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms/control-state-transitions-anim.gif" alt="Control State Transition">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
You should see the following transitions and class names:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms/ng-control-class-changes.png" alt="Control state transitions">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms/ng-control-class-changes.png" alt="Control state transitions">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The `ng-valid`/`ng-invalid` pair is the most interesting, because you want to send a
|
||||
@ -504,8 +518,10 @@ To create such visual feedback, add definitions for the `ng-*` CSS classes.
|
||||
You can mark required fields and invalid data at the same time with a colored bar
|
||||
on the left of the input box:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms/validity-required-indicator.png" alt="Invalid Form">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms/validity-required-indicator.png" alt="Invalid Form">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
You achieve this effect by adding these class definitions to a new `forms.css` file
|
||||
@ -525,8 +541,10 @@ Leverage the control's state to reveal a helpful message.
|
||||
|
||||
When the user deletes the name, the form should look like this:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/forms/name-required-error.png" alt="Name required">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/forms/name-required-error.png" alt="Name required">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
To achieve this effect, extend the `<input>` tag with the following:
|
||||
|
@ -111,8 +111,10 @@ directives in `CommonModule`; they don’t need to re-install app-wide providers
|
||||
If you do import `BrowserModule` into a lazy loaded feature module,
|
||||
Angular returns an error telling you to use `CommonModule` instead.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/frequent-ngmodules/browser-module-error.gif" width=750 alt="BrowserModule error">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/frequent-ngmodules/browser-module-error.gif" width=750 alt="BrowserModule error">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<hr />
|
||||
|
@ -350,7 +350,7 @@ To learn more, see [Browser Support](guide/browser-support).
|
||||
## element
|
||||
|
||||
Angular defines an `ElementRef` class to wrap render-specific native UI elements.
|
||||
In most cases, this allows you to use Angular templates and data binding to access DOM elements
|
||||
In most cases, this allows you to use Angular templates and data binding to access DOM elements
|
||||
without reference to the native element.
|
||||
|
||||
The documentation generally refers to *elements* (`ElementRef` instances), as distinct from *DOM elements*
|
||||
@ -938,7 +938,7 @@ View hierarchies can be loaded and unloaded dynamically as the user navigates th
|
||||
|
||||
## view hierarchy
|
||||
|
||||
A tree of related views that can be acted on as a unit. The root view is a component's *host view*. A host view can be the root of a tree of *embedded views*, collected in a *view container* (`ViewContainerRef`) attached to an anchor element in the hosting component. The view hierarchy is a key part of Angular change detection.
|
||||
A tree of related views that can be acted on as a unit. The root view is a component's *host view*. A host view can be the root of a tree of *embedded views*, collected in a *view container* (`ViewContainerRef`) attached to an anchor element in the hosting component. The view hierarchy is a key part of Angular change detection.
|
||||
|
||||
The view hierarchy doesn't imply a component hierarchy. Views that are embedded in the context of a particular hierarchy can be host views of other components. Those components can be in the same NgModule as the hosting component, or belong to other NgModules.
|
||||
|
||||
|
@ -108,8 +108,10 @@ The following diagram represents the relationship between the
|
||||
`root` `ModuleInjector` and its parent injectors as the
|
||||
previous paragraphs describe.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection/injectors.svg" alt="NullInjector, ModuleInjector, root injector">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection/injectors.svg" alt="NullInjector, ModuleInjector, root injector">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
While the name `root` is a special alias, other `ModuleInjector`s
|
||||
@ -364,7 +366,7 @@ templates in separate files. For the purposes of understanding
|
||||
how the injection system works, it is useful to look at them
|
||||
from the point of view of a combined logical tree. The term
|
||||
logical distinguishes it from the render tree (your application
|
||||
DOM tree). To mark the locations of where the component
|
||||
DOM tree). To mark the locations of where the component
|
||||
templates are located, this guide uses the `<#VIEW>`
|
||||
pseudo element, which doesn't actually exist in the render tree
|
||||
and is present for mental model purposes only.
|
||||
@ -515,7 +517,7 @@ In the logical tree, this would be represented as follows:
|
||||
</app-root>
|
||||
```
|
||||
|
||||
When `<app-root>` requests the `FlowerService`, it is the injector's job
|
||||
When `<app-root>` requests the `FlowerService`, it is the injector's job
|
||||
to resolve the `FlowerService` token. The resolution of the token happens
|
||||
in two phases:
|
||||
|
||||
@ -562,7 +564,7 @@ The next step is to add a binding to the `ChildComponent` template.
|
||||
</code-example>
|
||||
|
||||
To render the new values, add `<app-child>` to the bottom of
|
||||
the`AppComponent` template so the view also displays the sunflower:
|
||||
the `AppComponent` template so the view also displays the sunflower:
|
||||
|
||||
```
|
||||
Child Component
|
||||
@ -1097,8 +1099,10 @@ Each tax return component has the following characteristics:
|
||||
* Has the ability to save the changes to its tax return or cancel them.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection/hid-heroes-anim.gif" alt="Heroes in action">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection/hid-heroes-anim.gif" alt="Heroes in action">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Suppose that the `HeroTaxReturnComponent` had logic to manage and restore changes.
|
||||
@ -1168,8 +1172,10 @@ that have special capabilities suitable for whatever is going on in component (B
|
||||
Component (B) is the parent of another component (C) that defines its own, even _more specialized_ provider for `CarService`.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection/car-components.png" alt="car components">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection/car-components.png" alt="car components">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Behind the scenes, each component sets up its own injector with zero, one, or more providers defined for that component itself.
|
||||
@ -1179,8 +1185,10 @@ its injector produces an instance of `Car` resolved by injector (C) with an `Eng
|
||||
`Tires` resolved by the root injector (A).
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/dependency-injection/injector-tree.png" alt="car injector tree">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/dependency-injection/injector-tree.png" alt="car injector tree">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -816,7 +816,7 @@ import {HttpParams} from "@angular/common/http";
|
||||
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`.
|
||||
|
||||
The `HttpParams` are immutable so you'll have to save the returned value of the `.set()` method in order to update the options.
|
||||
The `HttpParams` are immutable so you'll have to save the returned value of the `.set()` method in order to update the options.
|
||||
|
||||
#### Use `fromString` to create HttpParams
|
||||
|
||||
@ -988,7 +988,7 @@ use `HttpClientXsrfModule.withOptions()` to override the defaults.
|
||||
## Testing HTTP requests
|
||||
|
||||
As for any external dependency, you must mock the HTTP backend so your tests can simulate interaction with a remote server.
|
||||
The `@angular/common/http/testing` library makes it straightforward to set up such mocking .
|
||||
The `@angular/common/http/testing` library makes it straightforward to set up such mocking.
|
||||
|
||||
Angular's HTTP testing library is designed for a pattern of testing in which the app executes code and makes requests first.
|
||||
The test then expects that certain requests have or have not been made,
|
||||
|
@ -797,7 +797,7 @@ error.
|
||||
When you use the CLI `build` or `serve` command to build your application for different locales, change the output path using the `--outputPath` command option (along with the i18n-specific command options), so that the translation files are saved to different locations.
|
||||
When you are serving a locale-specific version from a subdirectory, you can also change the base URL used by your app by specifying the `--baseHref` option.
|
||||
|
||||
For example, if the French version of your application is served from https://myapp.com/fr/, configure the build for the French version as follows.
|
||||
For example, if the French version of your application is served from https://example.com/fr/, configure the build for the French version as follows.
|
||||
|
||||
```
|
||||
"configurations": {
|
||||
|
@ -1,86 +1,86 @@
|
||||
# Angular Language Service
|
||||
|
||||
The Angular Language Service is a way to get completions, errors,
|
||||
hints, and navigation inside your Angular templates whether they
|
||||
are external in an HTML file or embedded in annotations/decorators
|
||||
in a string. The Angular Language Service autodetects that you are
|
||||
opening an Angular file, reads your `tsconfig.json` file, finds all the
|
||||
templates you have in your application, and then provides language
|
||||
services for any templates that you open.
|
||||
The Angular Language Service provides code editors with a way to get completions, errors,
|
||||
hints, and navigation inside Angular templates.
|
||||
It works with external templates in separate HTML files, and also with in-line templates.
|
||||
|
||||
## Features
|
||||
|
||||
Your editor autodetects that you are opening an Angular file.
|
||||
It then uses the Angular Language Service to read your `tsconfig.json` file, find all the
|
||||
templates you have in your application, and then provide language services for any templates that you open.
|
||||
|
||||
Language services include:
|
||||
|
||||
* Completions lists
|
||||
* AOT Diagnostic messages
|
||||
* Quick info
|
||||
* Go to definition
|
||||
|
||||
|
||||
## Autocompletion
|
||||
### Autocompletion
|
||||
|
||||
Autocompletion can speed up your development time by providing you with
|
||||
contextual possibilities and hints as you type. This example shows
|
||||
autocomplete in an interpolation. As you type it out,
|
||||
contextual possibilities and hints as you type.
|
||||
This example shows autocomplete in an interpolation. As you type it out,
|
||||
you can hit tab to complete.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/language-service/language-completion.gif" alt="autocompletion">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/language-service/language-completion.gif" alt="autocompletion">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
There are also completions within
|
||||
elements. Any elements you have as a component selector will
|
||||
There are also completions within elements. Any elements you have as a component selector will
|
||||
show up in the completion list.
|
||||
|
||||
## Error checking
|
||||
### Error checking
|
||||
|
||||
The Angular Language Service can also forewarn you of mistakes in your code.
|
||||
The Angular Language Service can forewarn you of mistakes in your code.
|
||||
In this example, Angular doesn't know what `orders` is or where it comes from.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/language-service/language-error.gif" alt="error checking">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/language-service/language-error.gif" alt="error checking">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
## Navigation
|
||||
### Quick info and navigation
|
||||
|
||||
Navigation allows you to hover to
|
||||
see where a component, directive, module, etc. is from and then
|
||||
click and press F12 to go directly to its definition.
|
||||
The quick-info feature allows you to hover to see where components, directives, modules, and so on come from.
|
||||
You can then click "Go to definition" or press F12 to go directly to the definition.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/language-service/language-navigation.gif" alt="navigation">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/language-service/language-navigation.gif" alt="navigation">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
## Angular Language Service in your editor
|
||||
|
||||
Angular Language Service is currently available for [Visual Studio Code](https://code.visualstudio.com/) and
|
||||
[WebStorm](https://www.jetbrains.com/webstorm).
|
||||
Angular Language Service is currently available as an extension for [Visual Studio Code](https://code.visualstudio.com/),
|
||||
[WebStorm](https://www.jetbrains.com/webstorm), and [Sublime Text](https://www.sublimetext.com/).
|
||||
|
||||
### Visual Studio Code
|
||||
|
||||
In Visual Studio Code, install Angular Language Service from the store,
|
||||
which is accessible from the bottom icon on the left menu pane.
|
||||
You can also use the VS Quick Open (⌘+P on Mac, CTRL+P on Windows) to search for the extension. When you've opened it,
|
||||
enter the following command:
|
||||
|
||||
```sh
|
||||
ext install Angular.ng-template
|
||||
```
|
||||
|
||||
Then click the install button to install the Angular Language Service.
|
||||
In [Visual Studio Code](https://code.visualstudio.com/), install the extension from the [Extensions: Marketplace](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template). You can open the marketplace from the editor using the Extensions icon on the left menu pane, or use VS Quick Open (⌘+P on Mac, CTRL+P on Windows) and type "? ext".
|
||||
|
||||
In the marketplace, search for Angular Language Service extension, and click the **Install** button.
|
||||
|
||||
### WebStorm
|
||||
|
||||
In webstorm, you have to install the language service as a dev dependency.
|
||||
When Angular sees this dev dependency, it provides the
|
||||
language service inside of WebStorm. Webstorm then gives you
|
||||
colorization inside the template and autocomplete in addition to the Angular Language Service.
|
||||
In [WebStorm](https://www.jetbrains.com/webstorm/), you must install the language service package as a project dependency.
|
||||
|
||||
Here's the dev dependency
|
||||
you need to have in `package.json`:
|
||||
1. Add the following to your `devDependencies` in your project's `package.json`
|
||||
|
||||
```json
|
||||
<code-example language="json" header="package.json">
|
||||
devDependencies {
|
||||
"@angular/language-service": "^6.0.0"
|
||||
}
|
||||
```
|
||||
</code-example>
|
||||
|
||||
Then in the terminal window at the root of your project,
|
||||
install the `devDependencies` with `npm` or `yarn`:
|
||||
2. In the terminal window at the root of your project, install the `devDependencies` with `npm` or `yarn`:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
@ -97,79 +97,72 @@ yarn
|
||||
yarn install
|
||||
```
|
||||
|
||||
When Angular sees this dev dependency, it provides the language service in the WebStorm environment.
|
||||
WebStorm then gives you colorization inside the template and autocomplete in addition to the Angular Language Service.
|
||||
|
||||
|
||||
### Sublime Text
|
||||
|
||||
In [Sublime Text](https://www.sublimetext.com/), you first need an extension to allow Typescript.
|
||||
Install the latest version of typescript in a local `node_modules` directory:
|
||||
In [Sublime Text](https://www.sublimetext.com/), the Language Service supports only in-line templates when installed as a plug-in.
|
||||
You need a custom Sublime plug-in (or modifications to the current plug-in) for completions in HTML files.
|
||||
|
||||
To use the Language Service for in-line templates, you must first add an extension to allow TypeScript, then install the Angular Language Service plug-in. Starting with TypeScript 2.3, TypeScript has a plug-in model that the language service can use.
|
||||
|
||||
1. Install the latest version of TypeScript in a local `node_modules` directory:
|
||||
|
||||
```sh
|
||||
npm install --save-dev typescript
|
||||
```
|
||||
|
||||
Then install the Angular Language Service in the same location:
|
||||
```sh
|
||||
npm install --save-dev @angular/language-service
|
||||
```
|
||||
|
||||
Starting with TypeScript 2.3, TypeScript has a language service plugin model that the language service can use.
|
||||
|
||||
Next, in your user preferences (`Cmd+,` or `Ctrl+,`), add:
|
||||
|
||||
```json
|
||||
"typescript-tsdk": "<path to your folder>/node_modules/typescript/lib"
|
||||
```
|
||||
|
||||
|
||||
## Installing in your project
|
||||
|
||||
You can also install Angular Language Service in your project with the
|
||||
following `npm` command:
|
||||
2. Install the Angular Language Service package in the same location:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @angular/language-service
|
||||
```
|
||||
Additionally, add the following to the `"compilerOptions"` section of
|
||||
your project's `tsconfig.json`.
|
||||
|
||||
```json
|
||||
3. Once the package is installed, add the following to the `"compilerOptions"` section of your project's `tsconfig.json`.
|
||||
|
||||
<code-example language="json" header="tsconfig.json">
|
||||
"plugins": [
|
||||
{"name": "@angular/language-service"}
|
||||
]
|
||||
```
|
||||
Note that this only provides diagnostics and completions in `.ts`
|
||||
files. You need a custom sublime plugin (or modifications to the current plugin)
|
||||
for completions in HTML files.
|
||||
</code-example>
|
||||
|
||||
4. In your editor's user preferences (`Cmd+,` or `Ctrl+,`), add the following:
|
||||
|
||||
<code-example language="json" header="Sublime Text user preferences">
|
||||
"typescript-tsdk": "<path to your folder>/node_modules/typescript/lib"
|
||||
</code-example>
|
||||
|
||||
This allows the Angular Language Service to provide diagnostics and completions in `.ts` files.
|
||||
|
||||
|
||||
|
||||
|
||||
## How the Language Service works
|
||||
|
||||
When you use an editor with a language service, there's an
|
||||
editor process which starts a separate language process/service
|
||||
to which it speaks through an [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call).
|
||||
Any time you type inside of the editor, it sends information to the other process to
|
||||
track the state of your project. When you trigger a completion list within a template, the editor process first parses the template into an HTML AST, or [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree). Then the Angular compiler interprets
|
||||
what module the template is part of, the scope you're in, and the component selector. Then it figures out where in the template AST your cursor is. When it determines the
|
||||
context, it can then determine what the children can be.
|
||||
|
||||
It's a little more involved if you are in an interpolation. If you have an interpolation of `{{data.---}}` inside a `div` and need the completion list after `data.---`, the compiler can't use the HTML AST to find the answer. The HTML AST can only tell the compiler that there is some text with the characters "`{{data.---}}`". That's when the template parser produces an expression AST, which resides within the template AST. The Angular Language Services then looks at `data.---` within its context and asks the TypeScript Language Service what the members of data are. TypeScript then returns the list of possibilities.
|
||||
|
||||
|
||||
For more in-depth information, see the
|
||||
[Angular Language Service API](https://github.com/angular/angular/blob/master/packages/language-service/src/types.ts)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
When you use an editor with a language service, the editor starts a separate language-service process
|
||||
and communicates with it through an [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call), using the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/).
|
||||
When you type into the editor, the editor sends information to the language-service process to
|
||||
track the state of your project.
|
||||
|
||||
When you trigger a completion list within a template, the editor first parses the template into an
|
||||
HTML [abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
|
||||
The Angular compiler interprets that tree to determine the context: which module the template is part of, the current scope, the component selector, and where your cursor is in the template AST. It can then determine the symbols that could potentially be at that position..
|
||||
|
||||
It's a little more involved if you are in an interpolation.
|
||||
If you have an interpolation of `{{data.---}}` inside a `div` and need the completion list after `data.---`, the compiler can't use the HTML AST to find the answer.
|
||||
The HTML AST can only tell the compiler that there is some text with the characters "`{{data.---}}`".
|
||||
That's when the template parser produces an expression AST, which resides within the template AST.
|
||||
The Angular Language Services then looks at `data.---` within its context, asks the TypeScript Language Service what the members of `data` are, and returns the list of possibilities.
|
||||
|
||||
<hr>
|
||||
|
||||
## More on Information
|
||||
## More information
|
||||
|
||||
For more information, see [Chuck Jazdzewski's presentation](https://www.youtube.com/watch?v=ez3R0Gi4z5A&t=368s) on the Angular Language
|
||||
Service from [ng-conf](https://www.ng-conf.org/) 2017.
|
||||
* For more in-depth information on the implementation, see the
|
||||
[Angular Language Service API](https://github.com/angular/angular/blob/master/packages/language-service/src/types.ts).
|
||||
|
||||
* For more on the design considerations and intentions, see [design documentation here](https://github.com/angular/vscode-ng-language-service/wiki/Design).
|
||||
|
||||
* See also [Chuck Jazdzewski's presentation](https://www.youtube.com/watch?v=ez3R0Gi4z5A&t=368s) on the Angular Language Service from [ng-conf](https://www.ng-conf.org/) 2017.
|
||||
|
@ -92,8 +92,10 @@ ng serve
|
||||
|
||||
Then go to `localhost:4200` where you should see “app works!” and three buttons.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/lazy-loading-ngmodules/three-buttons.png" width="300" alt="three buttons in the browser">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/lazy-loading-ngmodules/three-buttons.png" width="300" alt="three buttons in the browser">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
These buttons work, because the CLI automatically added the routes to the feature modules to the `routes` array in `app.module.ts`.
|
||||
@ -137,23 +139,29 @@ The other feature module's routing module is configured similarly.
|
||||
|
||||
You can check to see that a module is indeed being lazy loaded with the Chrome developer tools. In Chrome, open the dev tools by pressing `Cmd+Option+i` on a Mac or `Ctrl+Shift+j` on a PC and go to the Network Tab.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/lazy-loading-ngmodules/network-tab.png" width="600" alt="lazy loaded modules diagram">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/lazy-loading-ngmodules/network-tab.png" width="600" alt="lazy loaded modules diagram">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
Click on the Orders or Customers button. If you see a chunk appear, everything is wired up properly and the feature module is being lazy loaded. A chunk should appear for Orders and for Customers but will only appear once for each.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/lazy-loading-ngmodules/chunk-arrow.png" width="600" alt="lazy loaded modules diagram">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/lazy-loading-ngmodules/chunk-arrow.png" width="600" alt="lazy loaded modules diagram">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
To see it again, or to test after working in the project, clear everything out by clicking the circle with a line through it in the upper left of the Network Tab:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/lazy-loading-ngmodules/clear.gif" width="200" alt="lazy loaded modules diagram">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/lazy-loading-ngmodules/clear.gif" width="200" alt="lazy loaded modules diagram">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -2,8 +2,7 @@
|
||||
|
||||
A component has a lifecycle managed by Angular.
|
||||
|
||||
Angular creates it, renders it, creates and renders its children,
|
||||
checks it when its data-bound properties change, and destroys it before removing it from the DOM.
|
||||
Angular creates and renders components along with their children, checks when their data-bound properties change, and destroys them before removing them from the DOM.
|
||||
|
||||
Angular offers **lifecycle hooks**
|
||||
that provide visibility into these key life moments and the ability to act when they occur.
|
||||
@ -292,8 +291,10 @@ The peek-a-boo exists to show how Angular calls the hooks in the expected order.
|
||||
|
||||
This snapshot reflects the state of the log after the user clicked the *Create...* button and then the *Destroy...* button.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The sequence of log messages follows the prescribed hook calling order:
|
||||
@ -350,8 +351,10 @@ Here it is attached to the repeated hero `<div>`:
|
||||
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:
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Adding a hero results in a new hero `<div>`. The spy's `ngOnInit()` logs that event.
|
||||
@ -441,8 +444,10 @@ The host `OnChangesParentComponent` binds to them like this:
|
||||
|
||||
Here's the sample in action as the user makes changes.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The log entries appear as the string value of the *power* property changes.
|
||||
@ -474,8 +479,10 @@ This code inspects certain _values of interest_, capturing and comparing their c
|
||||
It writes a special message to the log when there are no substantive changes to the `hero` or the `power`
|
||||
so you can see how often `DoCheck` is called. The results are illuminating:
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it has a frightful cost.
|
||||
@ -528,8 +535,10 @@ for one turn of the browser's JavaScript cycle and that's just long enough.
|
||||
|
||||
Here's *AfterView* in action:
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/lifecycle-hooks/after-view-anim.gif' alt="AfterView">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/lifecycle-hooks/after-view-anim.gif' alt="AfterView">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Notice that Angular frequently calls `AfterViewChecked()`, often when there are no changes of interest.
|
||||
@ -573,8 +582,10 @@ The `<ng-content>` tag is a *placeholder* for the external content.
|
||||
It tells Angular where to insert that content.
|
||||
In this case, the projected content is the `<app-child>` from the parent.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/lifecycle-hooks/projected-child-view.png' alt="Projected Content">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/lifecycle-hooks/projected-child-view.png' alt="Projected Content">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
@ -1,96 +0,0 @@
|
||||
# `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()`)|
|
@ -1,138 +0,0 @@
|
||||
# Undecorated classes migration (DI)
|
||||
|
||||
This section discusses an Angular version 9 schematic that migrates
|
||||
two inheritance patterns that need to be updated to work with Ivy.
|
||||
|
||||
## What does this migration do?
|
||||
|
||||
This migration adds an empty `@Directive()` decorator to undecorated
|
||||
base classes that are extended by either directives or components.
|
||||
|
||||
Before:
|
||||
```ts
|
||||
export class BaseMenu {
|
||||
constructor(private vcr: ViewContainerRef) {}
|
||||
}
|
||||
|
||||
@Directive({selector: '[settingsMenu]'})
|
||||
export class SettingsMenu extends BaseMenu {}
|
||||
```
|
||||
|
||||
After:
|
||||
```ts
|
||||
@Directive()
|
||||
export class BaseMenu {
|
||||
constructor(private vcr: ViewContainerRef) {}
|
||||
}
|
||||
|
||||
@Directive({selector: '[settingsMenu]'})
|
||||
export class SettingsMenu extends BaseMenu {}
|
||||
```
|
||||
|
||||
The schematic also copies any inherited directive or component metadata to the derived class.
|
||||
|
||||
Before:
|
||||
```ts
|
||||
@Component({
|
||||
selector: 'base-menu',
|
||||
template: '<div></div>'
|
||||
})
|
||||
class BaseMenu {}
|
||||
|
||||
export class SettingsMenu extends BaseMenu {}
|
||||
```
|
||||
|
||||
After:
|
||||
```ts
|
||||
@Component({
|
||||
selector: 'base-menu',
|
||||
template: '<div></div>'
|
||||
})
|
||||
class BaseMenu {}
|
||||
|
||||
@Component({
|
||||
selector: 'settings-menu',
|
||||
template: '<div></div>'
|
||||
})
|
||||
export class SettingsMenu extends BaseMenu {}
|
||||
```
|
||||
|
||||
## Why is this migration necessary?
|
||||
|
||||
When a class has a `@Directive()` or `@Component()` decorator,
|
||||
the Angular compiler generates extra code to inject dependencies into
|
||||
the constructor. When using inheritance, Ivy needs both the parent class
|
||||
and the child class to apply a decorator to generate the correct code.
|
||||
|
||||
You can think of this change as two cases: a parent class is missing a
|
||||
decorator or a child class is missing a decorator. In both scenarios,
|
||||
Angular's run-time needs additional information from the compiler.
|
||||
This additional information comes from adding decorators.
|
||||
|
||||
|
||||
### Decorator missing from parent class
|
||||
|
||||
When the decorator is missing from the parent class,
|
||||
the subclass will inherit a constructor from a class for
|
||||
which the compiler did not generate special constructor
|
||||
info (because it was not decorated as a directive).
|
||||
When Angular then tries to create the subclass,
|
||||
it doesn't have the correct info
|
||||
to create it.
|
||||
|
||||
In View Engine, the compiler has global knowledge, so it
|
||||
can look up the missing data. However, the Ivy compiler
|
||||
only processes each directive in isolation. This means that
|
||||
compilation can be faster, but the compiler can't
|
||||
automatically infer the same
|
||||
information as before. Adding the `@Directive()` explicitly
|
||||
provides this information.
|
||||
|
||||
In the future, add `@Directive()` to base classes that
|
||||
do not already have decorators and are extended by directives.
|
||||
|
||||
### Decorator missing from child class
|
||||
|
||||
When the child class is missing the decorator, the
|
||||
child class inherits from the
|
||||
parent class yet has no decorators of its own.
|
||||
Without a decorator, the compiler has no way of knowing
|
||||
that the class is a `@Directive` or `@Component`, so
|
||||
it doesn't generate the proper instructions for the directive.
|
||||
|
||||
|
||||
## What does it mean to have a `@Directive()` decorator with no metadata inside of it?
|
||||
|
||||
The presence of the `@Directive` decorator causes Angular to generate
|
||||
extra code for the affected class. If that decorator includes no
|
||||
properties (metadata),
|
||||
the directive won't be matched to elements or instantiated
|
||||
directly, but other classes that _extend_ the
|
||||
directive class will inherit this generated code. You can think of
|
||||
this as an "abstract" directive.
|
||||
|
||||
Adding an abstract directive to an `NgModule` will cause an error.
|
||||
A directive must have a `selector` property defined in order to match some element in a template.
|
||||
|
||||
## When do I need a `@Directive()` decorator without a selector?
|
||||
|
||||
If you're using dependency injection, or any Angular-specific
|
||||
feature, such as `@HostBinding()`, `@ViewChild()`, or `@Input()`, you need a
|
||||
`@Directive()` or `@Component()` decorator.
|
||||
The decorator lets the compiler know to generate the correct
|
||||
instructions to create that class and any classes that extend it.
|
||||
If you don't want to use that base class as a directive directly, leave
|
||||
the selector blank. If you do want it to be usable independently,
|
||||
fill in the metadata as usual.
|
||||
|
||||
Classes that don't use Angular features don't need an Angular decorator.
|
||||
|
||||
## I'm a library author. Should I add the `@Directive()` decorator to base classes?
|
||||
|
||||
|
||||
As support for selectorless decorators is introduced in
|
||||
Angular version 9, if you want to support Angular version 8 and earlier, you
|
||||
shouldn't add a selectorless `@Directive()` decorator.
|
||||
You can either add `@Directive()` with a selector or
|
||||
add an explicit constructor to affected subclasses.
|
||||
|
@ -4,14 +4,14 @@ The Angular Framework, Angular CLI, and components used by Angular applications
|
||||
|
||||
You can download and install these npm packages by using the [npm CLI client](https://docs.npmjs.com/cli/install), which is installed with and runs as a [Node.js®](https://nodejs.org "Nodejs.org") application. By default, the Angular CLI uses the npm client.
|
||||
|
||||
Alternatively, you can use the [yarn client](https://yarnpkg.com/) for downloading and installing npm packages.
|
||||
Alternatively, you can use the [yarn client](https://yarnpkg.com/) for downloading and installing npm packages.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
See [Local Environment Setup](guide/setup-local "Setting up for Local Development") for information about the required versions and installation of `Node.js` and `npm`.
|
||||
|
||||
If you already have projects running on your machine that use other versions of Node.js and npm, consider using [nvm](https://github.com/creationix/nvm) to manage the multiple versions of Node.js and npm.
|
||||
If you already have projects running on your machine that use other versions of Node.js and npm, consider using [nvm](https://github.com/creationix/nvm) to manage the multiple versions of Node.js and npm.
|
||||
|
||||
</div>
|
||||
|
||||
@ -20,12 +20,12 @@ If you already have projects running on your machine that use other versions of
|
||||
|
||||
Both `npm` and `yarn` install the packages that are identified in a [`package.json`](https://docs.npmjs.com/files/package.json) file.
|
||||
|
||||
The CLI command `ng new` creates a `package.json` file when it creates the new workspace.
|
||||
This `package.json` is used by all projects in the workspace, including the initial app project that is created by the CLI when it creates the workspace.
|
||||
The CLI command `ng new` creates a `package.json` file when it creates the new workspace.
|
||||
This `package.json` is used by all projects in the workspace, including the initial app project that is created by the CLI when it creates the workspace.
|
||||
|
||||
Initially, this `package.json` includes _a starter set of packages_, some of which are required by Angular and others that support common application scenarios.
|
||||
You add packages to `package.json` as your application evolves.
|
||||
You may even remove some.
|
||||
You add packages to `package.json` as your application evolves.
|
||||
You may even remove some.
|
||||
|
||||
The `package.json` is organized into two groups of packages:
|
||||
|
||||
@ -35,14 +35,14 @@ The `package.json` is organized into two groups of packages:
|
||||
<div class="alert is-helpful">
|
||||
|
||||
**Library developers:** By default, the CLI command [`ng generate library`](cli/generate) creates a `package.json` for the new library. That `package.json` is used when publishing the library to npm.
|
||||
For more information, see the CLI wiki page [Library Support](https://github.com/angular/angular-cli/wiki/stories-create-library).
|
||||
For more information, see the CLI wiki page [Library Support](https://github.com/angular/angular-cli/wiki/stories-create-library).
|
||||
</div>
|
||||
|
||||
|
||||
{@a dependencies}
|
||||
## Dependencies
|
||||
|
||||
The packages listed in the `dependencies` section of `package.json` are essential to *running* applications.
|
||||
The packages listed in the `dependencies` section of `package.json` are essential to *running* applications.
|
||||
|
||||
The `dependencies` section of `package.json` contains:
|
||||
|
||||
@ -58,7 +58,7 @@ To add a new dependency, use the [`ng add`](cli/add) command.
|
||||
### Angular packages
|
||||
|
||||
The following Angular packages are included as dependencies in the default `package.json` file for a new Angular workspace.
|
||||
For a complete list of Angular packages, see the [API reference](http://angular.io/api?type=package).
|
||||
For a complete list of Angular packages, see the [API reference](http://angular.io/api?type=package).
|
||||
|
||||
Package name | Description
|
||||
---------------------------------------- | --------------------------------------------------
|
||||
@ -75,13 +75,13 @@ Package name | Description
|
||||
{@a support-packages}
|
||||
### Support packages
|
||||
|
||||
The following support packages are included as dependencies in the default `package.json` file for a new Angular workspace.
|
||||
The following support packages are included as dependencies in the default `package.json` file for a new Angular workspace.
|
||||
|
||||
|
||||
Package name | Description
|
||||
---------------------------------------- | --------------------------------------------------
|
||||
[**rxjs**](https://github.com/ReactiveX/rxjs) | Many Angular APIs return [_observables_](guide/glossary#observable). RxJS is an implementation of the proposed [Observables specification](https://github.com/tc39/proposal-observable) currently before the [TC39](https://www.ecma-international.org/memento/tc39-m.htm) committee, which determines standards for the JavaScript language.
|
||||
[**zone.js**](https://github.com/angular/zone.js) | Angular relies on zone.js to run Angular's change detection processes when native JavaScript operations raise events. Zone.js is an implementation of a [specification](https://gist.github.com/mhevery/63fdcdf7c65886051d55) currently before the [TC39](http://www.ecma-international.org/memento/TC39.htm) committee that determines standards for the JavaScript language.
|
||||
[**zone.js**](https://github.com/angular/zone.js) | Angular relies on zone.js to run Angular's change detection processes when native JavaScript operations raise events. Zone.js is an implementation of a [specification](https://gist.github.com/mhevery/63fdcdf7c65886051d55) currently before the [TC39](http://www.ecma-international.org/memento/TC39.htm) committee that determines standards for the JavaScript language.
|
||||
|
||||
|
||||
{@a polyfills}
|
||||
@ -90,10 +90,10 @@ Package name | Description
|
||||
Many browsers lack native support for some features in the latest HTML standards,
|
||||
features that Angular requires.
|
||||
[_Polyfills_](https://en.wikipedia.org/wiki/Polyfill_(programming)) can emulate the missing features.
|
||||
The [Browser Support](guide/browser-support) guide explains which browsers need polyfills and
|
||||
The [Browser Support](guide/browser-support) guide explains which browsers need polyfills and
|
||||
how you can add them.
|
||||
|
||||
The `package.json` for a new Angular workspace installs the [core-js](https://github.com/zloirock/core-js) package,
|
||||
The `package.json` for a new Angular workspace installs the [core-js](https://github.com/zloirock/core-js) package,
|
||||
which polyfills missing features for several popular browser.
|
||||
|
||||
|
||||
@ -113,7 +113,7 @@ To add a new `devDependency`, use either one of the following commands:
|
||||
yarn add --dev <package-name>
|
||||
</code-example>
|
||||
|
||||
The following `devDependencies` are provided in the default `package.json` file for a new Angular workspace.
|
||||
The following `devDependencies` are provided in the default `package.json` file for a new Angular workspace.
|
||||
|
||||
|
||||
Package name | Description
|
||||
@ -134,7 +134,7 @@ Package name | Description
|
||||
|
||||
## Related information
|
||||
|
||||
For information about how the Angular CLI handles packages see the following guides:
|
||||
|
||||
For information about how the Angular CLI handles packages see the following guides:
|
||||
|
||||
* [Building and serving](guide/build) describes how packages come together to create a development build.
|
||||
* [Deployment](guide/deployment) describes how packages come together to create a production build.
|
||||
|
@ -13,7 +13,7 @@ Because of these advantages, observables are used extensively within Angular, an
|
||||
|
||||
As a publisher, you create an `Observable` instance that defines a *subscriber* function. This is the function that is executed when a consumer calls the `subscribe()` method. The subscriber function defines how to obtain or generate values or messages to be published.
|
||||
|
||||
To execute the observable you have created and begin receiving notifications, you call its `subscribe()` method, passing an *observer*. This is a JavaScript object that defines the handlers for the notifications you receive. The `subscribe()` call returns a `Subscription` object that has an `unsubscribe()` method, which you call to stop receiving notifications.
|
||||
To execute the observable you have created and begin receiving notifications, you call its `subscribe()` method, passing an *observer*. This is a JavaScript object that defines the handlers for the notifications you receive. The `subscribe()` call returns a `Subscription` object that has an `unsubscribe()` method, which you call to stop receiving notifications.
|
||||
|
||||
Here's an example that demonstrates the basic usage model by showing how an observable could be used to provide geolocation updates.
|
||||
|
||||
@ -77,13 +77,13 @@ Now you can use this function to create an observable that publishes keydown eve
|
||||
|
||||
## Multicasting
|
||||
|
||||
A typical observable creates a new, independent execution for each subscribed observer. When an observer subscribes, the observable wires up an event handler and delivers values to that observer. When a second observer subscribes, the observable then wires up a new event handler and delivers values to that second observer in a separate execution.
|
||||
A typical observable creates a new, independent execution for each subscribed observer. When an observer subscribes, the observable wires up an event handler and delivers values to that observer. When a second observer subscribes, the observable then wires up a new event handler and delivers values to that second observer in a separate execution.
|
||||
|
||||
Sometimes, instead of starting an independent execution for each subscriber, you want each subscription to get the same values—even if values have already started emitting. This might be the case with something like an observable of clicks on the document object.
|
||||
|
||||
*Multicasting* is the practice of broadcasting to a list of multiple subscribers in a single execution. With a multicasting observable, you don't register multiple listeners on the document, but instead re-use the first listener and send values out to each subscriber.
|
||||
|
||||
When creating an observable you should determine how you want that observable to be used and whether or not you want to multicast its values.
|
||||
When creating an observable you should determine how you want that observable to be used and whether or not you want to multicast its values.
|
||||
|
||||
Let’s look at an example that counts from 1 to 3, with a one-second delay after each number emitted.
|
||||
|
||||
@ -103,7 +103,7 @@ Notice that if you subscribe twice, there will be two separate streams, each emi
|
||||
|
||||
## Error handling
|
||||
|
||||
Because observables produce values asynchronously, try/catch will not effectively catch errors. Instead, you handle errors by specifying an `error` callback on the observer. Producing an error also causes the observable to clean up subscriptions and stop producing values. An observable can either produce values (calling the `next` callback), or it can complete, calling either the `complete` or `error` callback.
|
||||
Because observables produce values asynchronously, try/catch will not effectively catch errors. Instead, you handle errors by specifying an `error` callback on the observer. Producing an error also causes the observable to clean up subscriptions and stop producing values. An observable can either produce values (calling the `next` callback), or it can complete, calling either the `complete` or `error` callback.
|
||||
|
||||
<code-example>
|
||||
myObservable.subscribe({
|
||||
|
@ -107,8 +107,10 @@ As you click the button, the displayed date alternates between
|
||||
"**<samp>Friday, April 15, 1988</samp>**".
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -186,8 +188,10 @@ Now you need a component to demonstrate the pipe.
|
||||
|
||||
<code-example path="pipes/src/app/power-booster.component.ts" header="src/app/power-booster.component.ts"></code-example>
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/pipes/power-booster.png' alt="Power Booster">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/pipes/power-booster.png' alt="Power Booster">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -230,8 +234,10 @@ your pipe and two-way data binding with `ngModel`.
|
||||
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -294,7 +300,7 @@ Notice how a hero is added:
|
||||
|
||||
|
||||
|
||||
You add the hero into the `heroes` array. The reference to the array hasn't changed.
|
||||
You add the hero into the `heroes` array. The reference to the array hasn't changed.
|
||||
It's the same array. That's all Angular cares about. From its perspective, *same array, no change, no display update*.
|
||||
|
||||
To fix that, create an array with the new hero appended and assign that to `heroes`.
|
||||
@ -307,8 +313,10 @@ The Flying Heroes application extends the
|
||||
code with checkbox switches and additional displays to help you experience these effects.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/pipes/flying-heroes-anim.gif' alt="Flying Heroes">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/pipes/flying-heroes-anim.gif' alt="Flying Heroes">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -486,8 +494,10 @@ both requesting the heroes from the `heroes.json` file.
|
||||
The component renders as the following:
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/pipes/hero-list.png' alt="Hero List">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/pipes/hero-list.png' alt="Hero List">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -64,8 +64,10 @@ The form control assigned to `name` is displayed when the component is added to
|
||||
|
||||
<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">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/reactive-forms/name-editor-1.png" alt="Name Editor">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
## Managing control values
|
||||
@ -108,8 +110,10 @@ Update the template with a button to simulate a name update. When you click the
|
||||
|
||||
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.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/reactive-forms/name-editor-2.png" alt="Name Editor Update">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/reactive-forms/name-editor-2.png" alt="Name Editor Update">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -188,8 +192,10 @@ To display the `ProfileEditor` component that contains the form, add it to a com
|
||||
|
||||
`ProfileEditor` allows you to manage the form control instances for the `firstName` and `lastName` controls within the form group instance.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/reactive-forms/profile-editor-1.png" alt="Profile Editor">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/reactive-forms/profile-editor-1.png" alt="Profile Editor">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
## Creating nested form groups
|
||||
@ -214,8 +220,10 @@ Add the `address` form group containing the `street`, `city`, `state`, and `zip`
|
||||
|
||||
The `ProfileEditor` form is displayed as one group, but the model is broken down further to represent the logical grouping areas.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/reactive-forms/profile-editor-2.png" alt="Profile Editor Update">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/reactive-forms/profile-editor-2.png" alt="Profile Editor Update">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -346,8 +354,10 @@ Display the current status of `profileForm` using interpolation.
|
||||
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="display-status" header="src/app/profile-editor/profile-editor.component.html (display status)"></code-example>
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/reactive-forms/profile-editor-3.png" alt="Profile Editor Validation">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/reactive-forms/profile-editor-3.png" alt="Profile Editor Validation">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The **Submit** button is disabled because `profileForm` is invalid due to the required `firstName` form control. After you fill out the `firstName` input, the form becomes valid and the **Submit** button is enabled.
|
||||
@ -382,7 +392,7 @@ The aliases control in the form group instance is now populated with a single co
|
||||
|
||||
A getter provides easy access to the aliases in the form array instance compared to repeating the `profileForm.get()` method to get each instance. The form array instance represents an undefined number of controls in an array. It's convenient to access a control through a getter, and this approach is easy to repeat for additional controls.
|
||||
|
||||
Use the getter syntax to create an `aliases` class property to retrieve the alias's form array control from the parent form group.
|
||||
Use the getter syntax to create an `aliases` class property to retrieve the alias's form array control from the parent form group.
|
||||
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.ts" region="aliases-getter" header="src/app/profile-editor/profile-editor.component.ts (aliases getter)">
|
||||
|
||||
@ -412,8 +422,10 @@ Add the template HTML below after the `<div>` closing the `formGroupName` elemen
|
||||
|
||||
The `*ngFor` directive iterates over each form control instance provided by the aliases form array instance. Because form array elements are unnamed, you assign the index to the `i` variable and pass it to each control to bind it to the `formControlName` input.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/reactive-forms/profile-editor-4.png" alt="Profile Editor Aliases">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/reactive-forms/profile-editor-4.png" alt="Profile Editor Aliases">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Each time a new alias instance is added, the new form array instance is provided its control based on the index. This allows you to track each individual control when calculating the status and value of the root control.
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
A basic understanding of the following concepts:
|
||||
A basic understanding of the following concepts:
|
||||
|
||||
* [Introduction to Angular animations](guide/animations)
|
||||
* [Transition and triggers](guide/transition-and-triggers)
|
||||
|
@ -25,8 +25,10 @@ Let's illustrate a router transition animation by navigating between two routes,
|
||||
|
||||
</br>
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/animations/route-animation.gif" alt="Animations in action" width="440">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/animations/route-animation.gif" alt="Animations in action" width="440">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
## Route configuration
|
||||
@ -63,7 +65,7 @@ The `<router-outlet>` container has an attribute directive that contains data ab
|
||||
|
||||
<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.
|
||||
`AppComponent` defines a method that can detect when a view changes. The method assigns an animation state value to the animation trigger (`@routeAnimation`) based on the route configuration `data` property value. Here's an example of an `AppComponent` method that detects when a route change happens.
|
||||
|
||||
<code-example path="animations/src/app/app.component.ts" header="src/app/app.component.ts" region="prepare-router-outlet" language="typescript"></code-example>
|
||||
|
||||
|
@ -765,16 +765,20 @@ Once the app warms up, you'll see a row of navigation buttons
|
||||
and the *Heroes* view with its list of heroes.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/router/hero-list.png' alt="Hero List">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/router/hero-list.png' alt="Hero List">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
Select one hero and the app takes you to a hero editing screen.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/router/hero-detail.png' alt="Crisis Center Detail">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/router/hero-detail.png' alt="Crisis Center Detail">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -790,8 +794,10 @@ Angular app navigation updates the browser history as normal web navigation does
|
||||
Now click the *Crisis Center* link for a list of ongoing crises.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/router/crisis-center-list.png' alt="Crisis Center List">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/router/crisis-center-list.png' alt="Crisis Center List">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -803,8 +809,10 @@ Alter the name of a crisis.
|
||||
Notice that the corresponding name in the crisis list does _not_ change.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/router/crisis-center-detail.png' alt="Crisis Center Detail">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/router/crisis-center-detail.png' alt="Crisis Center Detail">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -819,8 +827,10 @@ Click the browser back button or the "Heroes" link instead.
|
||||
Up pops a dialog box.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/router/confirm-dialog.png' alt="Confirm Dialog">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/router/confirm-dialog.png' alt="Confirm Dialog">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -842,8 +852,10 @@ Proceed to the first application milestone.
|
||||
Begin with a simple version of the app that navigates between two empty views.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/router/router-1-anim.gif' alt="App in action">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/router/router-1-anim.gif' alt="App in action">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
{@a import}
|
||||
@ -928,8 +940,10 @@ Registering the `RouterModule.forRoot()` in the `AppModule` imports makes the `R
|
||||
The root `AppComponent` is the application shell. It has a title, a navigation bar with two links, and a router outlet where the router swaps components on and off the page. Here's what you get:
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/router/shell-and-outlet.png' alt="Shell">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/router/shell-and-outlet.png' alt="Shell">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The router outlet serves as a placeholder when the routed components will be rendered below it.
|
||||
@ -1264,7 +1278,7 @@ The **Routing Module** has several characteristics:
|
||||
|
||||
The sample routing application does not include routing by default.
|
||||
When you use the [Angular CLI](cli) to create a project that will use routing, set the `--routing` option for the project or app, and for each NgModule.
|
||||
When you create or initialize a new project (using the CLI [`ng new`](cli/new) command) or a new app (using the [`ng generate app`](cli/generate) command), specify the `--routing` option. This tells the CLI to include the `@angular/router` npm package and create a file named `app-routing.module.ts`.
|
||||
When you create or initialize a new project (using the CLI [`ng new`](cli/new) command) or a new app (using the [`ng generate app`](cli/generate) command), specify the `--routing` option. This tells the CLI to include the `@angular/router` npm package and create a file named `app-routing.module.ts`.
|
||||
You can then use routing in any NgModule that you add to the project or app.
|
||||
|
||||
For example, the following command generates an NgModule that can use routing.
|
||||
@ -1363,8 +1377,10 @@ from the <live-example name="toh-pt4" title="Tour of Heroes: Services example co
|
||||
Here's how the user will experience this version of the app:
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/router/router-2-anim.gif' alt="App in action">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/router/router-2-anim.gif' alt="App in action">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -1944,8 +1960,10 @@ For example, when returning to the hero-detail.component.ts list from the hero d
|
||||
it would be nice if the viewed hero was preselected in the list.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/router/selected-hero.png' alt="Selected hero">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/router/selected-hero.png' alt="Selected hero">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -2129,8 +2147,10 @@ Add some styles to apply when the list item is selected.
|
||||
|
||||
When the user navigates from the heroes list to the "Magneta" hero and back, "Magneta" appears selected:
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/router/selected-hero.png' alt="Selected List">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/router/selected-hero.png' alt="Selected List">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -2523,8 +2543,10 @@ to conform to the following recommended pattern for Angular applications:
|
||||
If your app had many feature areas, the app component trees might look like this:
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/router/component-tree.png' alt="Component Tree">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/router/component-tree.png' alt="Component Tree">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -2789,8 +2811,10 @@ It displays a simple form with a header, an input box for the message,
|
||||
and two buttons, "Send" and "Cancel".
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/router/contact-popup.png' alt="Contact popup">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/router/contact-popup.png' alt="Contact popup">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -3626,7 +3650,7 @@ The `CrisisDetailResolverService` doesn't inherit from a base class.
|
||||
The router looks for that method and calls it if found.
|
||||
|
||||
1. Rely on the router to call the resolver.
|
||||
Don't worry about all the ways that the user could navigate away.
|
||||
Don't worry about all the ways that the user could navigate away.
|
||||
That's the router's job. Write this class and let the router take it from there.
|
||||
|
||||
The relevant *Crisis Center* code for this milestone follows.
|
||||
@ -3861,7 +3885,7 @@ and remove the `AdminModule` from the NgModule's `imports` array.
|
||||
|
||||
You're already protecting the `AdminModule` with a `CanActivate` guard that prevents unauthorized users from
|
||||
accessing the admin feature area.
|
||||
It redirects to the login page if the user is not authorized.
|
||||
It redirects to the login page if the user is not authorized.
|
||||
|
||||
But the router is still loading the `AdminModule` even if the user can't visit any of its components.
|
||||
Ideally, you'd only load the `AdminModule` if the user is logged in.
|
||||
@ -4285,7 +4309,7 @@ Here's the *Crisis Center* URL in this "HTML5 pushState" style:
|
||||
Older browsers send page requests to the server when the location URL changes
|
||||
_unless_ the change occurs after a "#" (called the "hash").
|
||||
Routers can take advantage of this exception by composing in-application route
|
||||
URLs with hashes. Here's a "hash URL" that routes to the *Crisis Center*.
|
||||
URLs with hashes. Here's a "hash URL" that routes to the *Crisis Center*.
|
||||
|
||||
|
||||
<code-example format="nocode">
|
||||
|
@ -30,9 +30,9 @@ The context also defines a *merge strategy* that determines how changes are merg
|
||||
### Defining rules and actions
|
||||
|
||||
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`.
|
||||
A `RuleFactory` object defines a higher-order function that creates a `Rule`.
|
||||
|
||||
<code-example language="TypeScript">
|
||||
<code-example language="TypeScript" header="index.ts">
|
||||
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
|
||||
|
||||
// You don't have to export the function as default.
|
||||
@ -49,7 +49,7 @@ You need a rule, for example, to define how a template in the schematic is to be
|
||||
|
||||
Rules can make use of utilities provided with the `@schematics/angular` package. Look for helper functions for working with modules, dependencies, TypeScript, AST, JSON, Angular CLI workspaces and projects, and more.
|
||||
|
||||
<code-example language="none">
|
||||
<code-example language="TypeScript" header="index.ts">
|
||||
|
||||
import {
|
||||
JsonAstObject,
|
||||
@ -69,8 +69,191 @@ Rules can collect option values from the caller and inject them into templates.
|
||||
The options available to your rules, with their allowed values and defaults, are defined in the schematic's JSON schema file, `<schematic>/schema.json`.
|
||||
You can define variable or enumerated data types for the schema using TypeScript interfaces.
|
||||
|
||||
The schema defines the types and default values of variables used in the schematic.
|
||||
For example, the hypothetical "Hello World" schematic might have the following schema.
|
||||
|
||||
<code-example language="json" header="src/hello-world/schema.json">
|
||||
|
||||
{
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"default": "world"
|
||||
},
|
||||
"useColor": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
</code-example>
|
||||
|
||||
|
||||
You can see examples of schema files for the Angular CLI command schematics in [`@schematics/angular`](https://github.com/angular/angular-cli/blob/7.0.x/packages/schematics/angular/application/schema.json).
|
||||
|
||||
### Schematic prompts
|
||||
|
||||
Schematic *prompts* introduce user interaction into schematic execution.
|
||||
You can configure schematic options to display a customizable question to the user.
|
||||
The prompts are displayed before the execution of the schematic, which then uses the response as the value for the option.
|
||||
This allows users to direct the operation of the schematic without requiring in-depth knowledge of the full spectrum of available options.
|
||||
|
||||
The "Hello World" schematic might, for example, ask the user for their name, and display that name in place of the default name "world". To define such a prompt, add an `x-prompt` property to the schema for the `name` variable.
|
||||
|
||||
Similarly, you can add a prompt to allow the user to decide whether the schematic will use color when executing its hello action. The schema with both prompts would be as follows.
|
||||
|
||||
<code-example language="json" header="src/hello-world/schema.json">
|
||||
|
||||
{
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"default": "world",
|
||||
"x-prompt": "What is your name?"
|
||||
},
|
||||
"useColor": {
|
||||
"type": "boolean",
|
||||
"x-prompt": "Would you like the response in color?"
|
||||
}
|
||||
}
|
||||
}
|
||||
</code-example>
|
||||
|
||||
#### Prompt short-form syntax
|
||||
|
||||
These examples use a shorthand form of the prompt syntax, supplying only the text of the question.
|
||||
In most cases, this is all that is required.
|
||||
Notice however, that the two prompts expect different types of input.
|
||||
When using the shorthand form, the most appropriate type is automatically selected based on the property's schema.
|
||||
In the example, the `name` prompt uses the `input` type because it it is a string property.
|
||||
The `useColor` prompt uses a `confirmation` type because it is a Boolean property.
|
||||
In this case, "yes" corresponds to `true` and "no" corresponds to `false`.
|
||||
|
||||
There are three supported input types.
|
||||
|
||||
| Input type | Description |
|
||||
| :----------- | :-------------------|
|
||||
| confirmation | A yes or no question; ideal for Boolean options. |
|
||||
| input | Textual input; ideal for string or number options. |
|
||||
| list | A predefined set of allowed values. |
|
||||
|
||||
In the short form, the type is inferred from the property's type and constraints.
|
||||
|
||||
| Property Schema | Prompt Type |
|
||||
| :--------------- | :------------- |
|
||||
| "type": "boolean" | confirmation ("yes"=`true`, "no"=`false`) |
|
||||
| "type": "string" | input |
|
||||
| "type": "number" | input (only valid numbers accepted) |
|
||||
| "type": "integer" | input (only valid numbers accepted) |
|
||||
| "enum": [...] | list (enum members become list selections) |
|
||||
|
||||
In the following example, the property takes an enumerated value, so the schematic automatically chooses the list type, and creates a menu from the possible values.
|
||||
|
||||
<code-example language="json" header="schema.json">
|
||||
|
||||
"style": {
|
||||
"description": "The file extension or preprocessor to use for style files.",
|
||||
"type": "string",
|
||||
"default": "css",
|
||||
"enum": [
|
||||
"css",
|
||||
"scss",
|
||||
"sass",
|
||||
"less",
|
||||
"styl"
|
||||
],
|
||||
"x-prompt": "Which stylesheet format would you like to use?"
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
The prompt runtime automatically validates the provided response against the constraints provided in the JSON schema.
|
||||
If the value is not acceptable, the user is prompted for a new value.
|
||||
This ensures that any values passed to the schematic meet the expectations of the schematic's implementation, so that you do not need to add additional checks within the schematic's code.
|
||||
|
||||
#### Prompt long-form syntax
|
||||
|
||||
The `x-prompt` field syntax supports a long form for cases where you require additional customization and control over the prompt.
|
||||
In this form, the `x-prompt` field value is a JSON object with subfields that customize the behavior of the prompt.
|
||||
|
||||
| Field | Data Value |
|
||||
| :----------- | :------ |
|
||||
| type | `confirmation`, `input`, or `list` (selected automatically in short form) |
|
||||
| message | string (required) |
|
||||
| items | string and/or label/value object pair (only valid with type `list`) |
|
||||
|
||||
The following example of the long form is from the JSON schema for the schematic that the CLI uses to [generate applications](https://github.com/angular/angular-cli/blob/ba8a6ea59983bb52a6f1e66d105c5a77517f062e/packages/schematics/angular/application/schema.json#L56).
|
||||
It defines the prompt that allows users to choose which style preprocessor they want to use for the application being created.
|
||||
By using the long form, the schematic can provide more explicit formatting of the menu choices.
|
||||
|
||||
<code-example language="json" header="package/schematics/angular/application/schema.json">
|
||||
|
||||
"style": {
|
||||
"description": "The file extension or preprocessor to use for style files.",
|
||||
"type": "string",
|
||||
"default": "css",
|
||||
"enum": [
|
||||
"css",
|
||||
"scss",
|
||||
"sass",
|
||||
"less",
|
||||
"styl"
|
||||
],
|
||||
"x-prompt": {
|
||||
"message": "Which stylesheet format would you like to use?",
|
||||
"type": "list",
|
||||
"items": [
|
||||
{ "value": "css", "label": "CSS" },
|
||||
{ "value": "scss", "label": "SCSS [ https://sass-lang.com/documentation/syntax#scss ]" },
|
||||
{ "value": "sass", "label": "Sass [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]" },
|
||||
{ "value": "less", "label": "Less [ http://lesscss.org ]" },
|
||||
{ "value": "styl", "label": "Stylus [ http://stylus-lang.com ]" }
|
||||
]
|
||||
},
|
||||
},
|
||||
</code-example>
|
||||
|
||||
#### x-prompt schema
|
||||
|
||||
The JSON schema that defines a schematic's options supports extensions to allow the declarative definition of prompts and their respective behavior.
|
||||
No additional logic or changes are required to the code of a schematic to support the prompts.
|
||||
The following JSON schema is a complete description of the long-form syntax for the `x-prompt` field.
|
||||
|
||||
<code-example language="json" header="x-prompt schema">
|
||||
|
||||
{
|
||||
"oneOf": [
|
||||
{ "type": "string" },
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": { "type": "string" },
|
||||
"message": { "type": "string" },
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{ "type": "string" },
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"label": { "type": "string" },
|
||||
"value": { }
|
||||
},
|
||||
"required": [ "value" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [ "message" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
{@a cli}
|
||||
|
||||
## Schematics CLI
|
||||
@ -166,7 +349,7 @@ Each schematic is created with a name, description, and factory function.
|
||||
* The `factory` property points to the generated entry function. In this example, you invoke the `hello-world` schematic by calling the `helloWorld()` factory function.
|
||||
* The optional `schema` property points to a JSON schema file that defines the command-line options available to the schematic.
|
||||
* The optional `aliases` array specifies one or more strings that can be used to invoke the schematic.
|
||||
For example, the schematic for the Angular CLI “generate” command has an alias “g”, allowing you to use the command `ng g`.
|
||||
For example, the schematic for the Angular CLI “generate” command has an alias “g”, allowing you to use the command `ng g`.
|
||||
|
||||
### Named schematics
|
||||
|
||||
@ -176,8 +359,10 @@ When you add a new named schematic to this collection, it is automatically added
|
||||
In addition to the name and description, each schematic has a `factory` property that identifies the schematic’s entry point.
|
||||
In the example, you invoke the schematic's defined functionality by calling the `helloWorld()` function in the main file, `hello-world/index.ts`.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/schematics/collection-files.gif" alt="overview">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/schematics/collection-files.gif" alt="overview">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Each named schematic in the collection has the following main parts.
|
||||
|
@ -76,7 +76,7 @@ To tell the library how to build the schematics, add a `tsconfig.schematics.json
|
||||
|
||||
* The `rootDir` specifies that your `schematics/` folder contains the input files to be compiled.
|
||||
|
||||
* The `outDir` maps to the library's output folder. By default, this is the `dist/my-lib` folder at the root of your workspace.
|
||||
* The `outDir` maps to the library's output folder. By default, this is the `dist/my-lib` folder at the root of your workspace.
|
||||
|
||||
1. To make sure your schematics source files get compiled into the library bundle, add the following scripts to the `package.json` file in your library project's root folder (`projects/my-lib`).
|
||||
|
||||
@ -201,7 +201,7 @@ The user can specify the project on the command line, or allow it to default.
|
||||
In either case, your code needs to identify the specific project to which this schematic is being applied, so that you can retrieve information from the project configuration.
|
||||
|
||||
You can do this using the `Tree` object that is passed in to the factory function.
|
||||
The `Tree` methods give you access to the complete file tree in your workspace, allowing you to read and write files during the execution of the schematic.
|
||||
The `Tree` methods give you access to the complete file tree in your workspace, allowing you to read and write files during the execution of the schematic.
|
||||
|
||||
### Get the project configuration
|
||||
|
||||
@ -249,7 +249,7 @@ A `Rule` can use external template files, transform them, and return another `Ru
|
||||
* The `url()` method reads source files from your filesystem, relative to the schematic.
|
||||
* The `applyTemplates()` method receives an argument of methods and properties you want make available to the schematic template and the schematic filenames. It returns a `Rule`. This is where you define the `classify()` and `dasherize()` methods, and the `name` property.
|
||||
* The `classify()` method takes a value and returns the value in title case. For example, if the provided name is `my service`, it is returned as `MyService`
|
||||
* The `dasherize()` method takes a value and returns the value in dashed and lowercase. For example, if the provided name is MyService, it is returned as `my-service.
|
||||
* The `dasherize()` method takes a value and returns the value in dashed and lowercase. For example, if the provided name is MyService, it is returned as `my-service.
|
||||
* The `move` method moves the provided source files to their destination when the schematic is applied.
|
||||
|
||||
1. Finally, the rule factory must return a rule.
|
||||
|
@ -42,7 +42,7 @@ As a library developer, you can create your own collections of custom schematics
|
||||
|
||||
* An *add schematic* allows developers to install your library in an Angular workspace using `ng add`.
|
||||
|
||||
* *Generation schematics* can tell the `ng generate` subcommands how to modify projects, add configurations and scripts, and scaffold artifacts that are defined in your library.
|
||||
* *Generation schematics* can tell the `ng generate` subcommands how to modify projects, add configurations and scripts, and scaffold artifacts that are defined in your library.
|
||||
|
||||
* An *update schematic* can tell the `ng update` command how to update your library's dependencies and adjust for breaking changes when you release a new version.
|
||||
|
||||
@ -62,7 +62,7 @@ Partner and third party libraries also support the Angular CLI with add schemati
|
||||
For example, `@ng-bootstrap/schematics` adds [ng-bootstrap](https://ng-bootstrap.github.io/) to an app, and `@clr/angular` installs and sets up [Clarity from VMWare](https://vmware.github.io/clarity/documentation/v1.0/get-started).
|
||||
|
||||
An add schematic can also update a project with configuration changes, add additional dependencies (such as polyfills), or scaffold package-specific initialization code.
|
||||
For example, the `@angular/pwa` schematic turns your application into a PWA by adding an app manifest and service worker, and the `@angular/elements` schematic adds the `document-register-element.js` polyfill and dependencies for Angular Elements.
|
||||
For example, the `@angular/pwa` schematic turns your application into a PWA by adding an app manifest and service worker, and the `@angular/elements` schematic adds the `document-register-element.js` polyfill and dependencies for Angular Elements.
|
||||
|
||||
### Generation schematics
|
||||
|
||||
|
@ -118,8 +118,10 @@ Angular recognizes the value as unsafe and automatically sanitizes it, which rem
|
||||
tag but keeps safe content such as the `<b>` element.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/security/binding-inner-html.png' alt='A screenshot showing interpolated and bound HTML values'>
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/security/binding-inner-html.png' alt='A screenshot showing interpolated and bound HTML values'>
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -209,8 +211,10 @@ this, mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` cal
|
||||
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/security/bypass-security-component.png' alt='A screenshot showing an alert box created from a trusted URL'>
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/security/bypass-security-component.png' alt='A screenshot showing an alert box created from a trusted URL'>
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -207,6 +207,9 @@ There are two possible degraded states:
|
||||
clean copy of the latest known version of the app. Older cached
|
||||
versions are safe to use, so existing tabs continue to run from
|
||||
cache, but new loads of the app will be served from the network.
|
||||
The service worker will try to recover from this state when a new
|
||||
version of the application is detected and installed (that is,
|
||||
when a new `ngsw.json` is available).
|
||||
|
||||
* `SAFE_MODE`: the service worker cannot guarantee the safety of
|
||||
using cached data. Either an unexpected error occurred or all
|
||||
@ -216,6 +219,12 @@ network, running as little service worker code as possible.
|
||||
In both cases, the parenthetical annotation provides the
|
||||
error that caused the service worker to enter the degraded state.
|
||||
|
||||
Both states are temporary; they are saved only for the lifetime of the [ServiceWorker
|
||||
instance](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope).
|
||||
The browser sometimes terminates an idle service worker to conserve memory and
|
||||
processor power, and creates a new service worker instance in response to
|
||||
network events. The new instance starts in the `NORMAL` mode, regardless of the
|
||||
state of the previous instance.
|
||||
|
||||
#### Latest manifest hash
|
||||
|
||||
@ -335,6 +344,24 @@ This script can be used both to deactivate `@angular/service-worker`
|
||||
as well as any other Service Workers which might have been served in
|
||||
the past on your site.
|
||||
|
||||
### Changing your app's location
|
||||
|
||||
It is important to note that service workers don't work behind redirect. You
|
||||
may have already encountered the error `The script resource is behind a redirect, which is disallowed`.
|
||||
|
||||
This can be a problem if you have to change your app's location. If you setup
|
||||
a redirect from the old location (for example `example.com`) to the new
|
||||
location (for example `www.example.com`) the worker will stop working.
|
||||
Also, the redirect won't even trigger for users who are loading the site
|
||||
entirely from Service Worker. The old worker (registered at `example.com`)
|
||||
tries to update and sends requests to the old location `example.com` which
|
||||
get redirected to the new location `www.example.com` and create the error
|
||||
`The script resource is behind a redirect, which is disallowed`.
|
||||
|
||||
To remedy this, you may need to kill the old worker using one of the above
|
||||
techniques ([Fail-safe](#fail-safe) or [Safety Worker](#safety-worker)).
|
||||
|
||||
|
||||
## More on Angular service workers
|
||||
|
||||
You may also be interested in the following:
|
||||
|
@ -74,8 +74,10 @@ To simulate a network issue, disable network interaction for your application. I
|
||||
2. Go to the **Network tab**.
|
||||
3. Check the **Offline box**.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/service-worker/offline-checkbox.png" alt="The offline checkbox in the Network tab is checked">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/service-worker/offline-checkbox.png" alt="The offline checkbox in the Network tab is checked">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Now the app has no access to network interaction.
|
||||
@ -86,8 +88,10 @@ With the addition of an Angular service worker, the application behavior changes
|
||||
|
||||
If you look at the Network tab, you can verify that the service worker is active.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/service-worker/sw-active.png" alt="Requests are marked as from ServiceWorker">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/service-worker/sw-active.png" alt="Requests are marked as from ServiceWorker">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Notice that under the "Size" column, the requests state is `(from ServiceWorker)`. This means that the resources are not being loaded from the network. Instead, they are being loaded from the service worker's cache.
|
||||
@ -142,8 +146,10 @@ Now look at how the browser and service worker handle the updated application.
|
||||
|
||||
1. Open http://localhost:8080 again in the same window. What happens?
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/service-worker/welcome-msg-en.png" alt="It still says Welcome to Service Workers!">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/service-worker/welcome-msg-en.png" alt="It still says Welcome to Service Workers!">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
What went wrong? Nothing, actually. The Angular service worker is doing its job and serving the version of the application that it has **installed**, even though there is an update available. In the interest of speed, the service worker doesn't wait to check for updates before it serves the application that it has cached.
|
||||
@ -152,8 +158,10 @@ If you look at the `http-server` logs, you can see the service worker requesting
|
||||
|
||||
2. Refresh the page.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/service-worker/welcome-msg-fr.png" alt="The text has changed to say Bienvenue à app!">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/service-worker/welcome-msg-fr.png" alt="The text has changed to say Bienvenue à app!">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The service worker installed the updated version of your app *in the background*, and the next time the page is loaded or reloaded, the service worker switches to the latest version.
|
||||
|
@ -25,8 +25,8 @@ That's dirty and undermines your chances of running the app outside of a browser
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Running your app outside a browser means that you can take advantage of server-side
|
||||
pre-rendering for near-instant first app render times and for SEO. It means you could run from
|
||||
inside a Web Worker to improve your app's responsiveness by using multiple threads. And it
|
||||
pre-rendering for near-instant first app render times and for SEO. It means you could run from
|
||||
inside a Web Worker to improve your app's responsiveness by using multiple threads. And it
|
||||
means that you could run your app inside Electron.js or Windows Universal to deliver it to the desktop.
|
||||
|
||||
</div>
|
||||
@ -47,8 +47,10 @@ You can inject the `Title` service into the root `AppComponent` and expose a bin
|
||||
|
||||
Bind that method to three anchor tags and voilà!
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/set-document-title/set-title-anim.gif" alt="Set title">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/set-document-title/set-title-anim.gif" alt="Set title">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Here's the complete solution:
|
||||
@ -72,4 +74,3 @@ If you bootstrap your application into a different platform,
|
||||
you'll have to provide a different `Title` service that understands
|
||||
the concept of a "document title" for that specific platform.
|
||||
Ideally, the application itself neither knows nor cares about the runtime environment.
|
||||
|
||||
|
@ -104,8 +104,10 @@ to `http://localhost:4200/`.
|
||||
Your app greets you with a message:
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/setup-local/app-works.png' alt="Welcome to my-app!">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/setup-local/app-works.png' alt="Welcome to my-app!">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -125,8 +125,10 @@ The `ngIf` directive doesn't hide elements with CSS. It adds and removes them ph
|
||||
Confirm that fact using browser developer tools to inspect the DOM.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/structural-directives/element-not-in-dom.png' alt="ngIf=false element not in DOM">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/structural-directives/element-not-in-dom.png' alt="ngIf=false element not in DOM">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -151,8 +153,10 @@ A directive could hide the unwanted paragraph instead by setting its `display` s
|
||||
While invisible, the element remains in the DOM.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/structural-directives/element-display-in-dom.png' alt="hidden element still in DOM">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/structural-directives/element-display-in-dom.png' alt="hidden element still in DOM">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -211,8 +215,10 @@ Internally, Angular translates the `*ngIf` _attribute_ into a `<ng-template>` _e
|
||||
The first form is not actually rendered, only the finished product ends up in the DOM.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -297,7 +303,7 @@ describes additional `NgFor` directive properties and context properties.
|
||||
|
||||
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`.
|
||||
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.
|
||||
|
||||
@ -559,8 +565,10 @@ That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!".
|
||||
Angular erases the middle "Hip!", leaving the cheer a bit less enthusiastic.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/structural-directives/template-rendering.png' alt="template tag rendering">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/structural-directives/template-rendering.png' alt="template tag rendering">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -617,8 +625,10 @@ You also have a CSS style rule that happens to apply to a `<span>` within a `<p>
|
||||
The constructed paragraph renders strangely.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -639,8 +649,10 @@ When you try this,
|
||||
the drop down is empty.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/structural-directives/bad-select.png' alt="spanned options don't work">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/structural-directives/bad-select.png' alt="spanned options don't work">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -662,8 +674,10 @@ Here's the conditional paragraph again, this time using `<ng-container>`.
|
||||
It renders properly.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -678,8 +692,10 @@ Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
|
||||
The drop down works properly.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -828,8 +844,10 @@ When the `condition` is falsy, the top (A) paragraph appears and the bottom (B)
|
||||
When the `condition` is truthy, the top (A) paragraph is removed and the bottom (B) paragraph appears.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/structural-directives/unless-anim.gif' alt="UnlessDirective in action">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/structural-directives/unless-anim.gif' alt="UnlessDirective in action">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -635,7 +635,7 @@ which is the attribute, spelled with a lowercase `s`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html"></code-example>
|
||||
|
||||
For more details, see the [MDN HTMLTableCellElment](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) documentation.
|
||||
For more details, see the [MDN HTMLTableCellElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) documentation.
|
||||
|
||||
<!-- Add link when Attribute Binding updates are merged:
|
||||
For more about `colSpan` and `colspan`, see (Attribute Binding)[guide/template-syntax]. -->
|
||||
@ -804,7 +804,7 @@ Imagine the following malicious content.
|
||||
|
||||
In the component template, the content might be used with interpolation:
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="malicious-interpolated" header="src/app/app.component.ts"></code-example>
|
||||
<code-example path="property-binding/src/app/app.component.html" region="malicious-interpolated" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Fortunately, Angular data binding is on alert for dangerous HTML. In the above case,
|
||||
the HTML displays as is, and the Javascript does not execute. Angular **does not**
|
||||
@ -938,7 +938,7 @@ followed by a dot (`.`) and the name of a CSS style property: `[style.style-prop
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="style-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Some style binding styles have a unit extension.
|
||||
The following example conditionally sets the font size in “em” and “%” units .
|
||||
The following example conditionally sets the font size in “em” and “%” units.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="style-binding-condition" header="src/app/app.component.html"></code-example>
|
||||
|
||||
@ -969,8 +969,10 @@ template statement on the right.
|
||||
The following event binding listens for the button's click events, calling
|
||||
the component's `onSave()` method whenever a click occurs:
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/template-syntax/syntax-diagram.svg' alt="Syntax diagram">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/template-syntax/syntax-diagram.svg' alt="Syntax diagram">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
### Target event
|
||||
@ -1306,8 +1308,10 @@ for example, the following changes the `<input>` value to uppercase:
|
||||
|
||||
Here are all variations in action, including the uppercase version:
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/built-in-directives/ng-model-anim.gif' alt="NgModel variations">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/built-in-directives/ng-model-anim.gif' alt="NgModel variations">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<hr/>
|
||||
@ -1512,8 +1516,10 @@ Here is an illustration of the `trackBy` effect.
|
||||
* With no `trackBy`, both buttons trigger complete DOM element replacement.
|
||||
* With `trackBy`, only changing the `id` triggers element replacement.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/built-in-directives/ngfor-trackby.gif" alt="Animation of trackBy">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/built-in-directives/ngfor-trackby.gif" alt="Animation of trackBy">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -1538,8 +1544,10 @@ Angular puts only the selected element into the DOM.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgSwitch" header="src/app/app.component.html"></code-example>
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/built-in-directives/ngswitch.gif" alt="Animation of NgSwitch">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/built-in-directives/ngswitch.gif" alt="Animation of NgSwitch">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
`NgSwitch` is the controller directive. Bind it to an expression that returns
|
||||
@ -1610,8 +1618,8 @@ by HTML.
|
||||
|
||||
The reference value of itemForm, without the ngForm attribute value, would be
|
||||
the [HTMLFormElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement).
|
||||
There is, however, a difference between a Component and a Directive in that a `Component
|
||||
`will be referenced without specifying the attribute value, and a `Directive` will not
|
||||
There is, however, a difference between a Component and a Directive in that a `Component`
|
||||
will be referenced without specifying the attribute value, and a `Directive` will not
|
||||
change the implicit reference (that is, the element).
|
||||
|
||||
|
||||
@ -1695,8 +1703,10 @@ child component. So an `@Input()` allows data to be input _into_ the
|
||||
child component from the parent component.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/inputs-outputs/input.svg" alt="Input data flow diagram">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/inputs-outputs/input.svg" alt="Input data flow diagram">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
To illustrate the use of `@Input()`, edit these parts of your app:
|
||||
@ -1742,8 +1752,10 @@ With `@Input()`, Angular passes the value for `currentItem` to the child so that
|
||||
|
||||
The following diagram shows this structure:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/inputs-outputs/input-diagram-target-source.svg" alt="Property binding diagram">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/inputs-outputs/input-diagram-target-source.svg" alt="Property binding diagram">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The target in the square brackets, `[]`, is the property you decorate
|
||||
@ -1776,8 +1788,10 @@ the child _out_ to the parent.
|
||||
An `@Output()` property should normally be initialized to an Angular [`EventEmitter`](api/core/EventEmitter) with values flowing out of the component as [events](#event-binding).
|
||||
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/inputs-outputs/output.svg" alt="Output diagram">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/inputs-outputs/output.svg" alt="Output diagram">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Just like with `@Input()`, you can use `@Output()`
|
||||
@ -1918,8 +1932,10 @@ The target, `item`, which is an `@Input()` property in the child component class
|
||||
The following diagram is of an `@Input()` and an `@Output()` on the same
|
||||
child component and shows the different parts of each:
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/inputs-outputs/input-output-diagram.svg" alt="Input/Output diagram">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/inputs-outputs/input-output-diagram.svg" alt="Input/Output diagram">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
As the diagram shows, use inputs and outputs together in the same manner as using them separately. Here, the child selector is `<app-input-output>` with `item` and `deleteRequest` being `@Input()` and `@Output()`
|
||||
|
@ -41,8 +41,10 @@ It shows that Karma ran three tests that all passed.
|
||||
|
||||
A chrome browser also opens and displays the test output in the "Jasmine HTML Reporter" like this.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/testing/initial-jasmine-html-reporter.png' alt="Jasmine HTML Reporter in the browser">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/testing/initial-jasmine-html-reporter.png' alt="Jasmine HTML Reporter in the browser">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Most people find this browser output easier to read than the console log.
|
||||
@ -186,7 +188,7 @@ When the CLI commands `ng test` and `ng e2e` are generally running the CI tests
|
||||
|
||||
There are configuration files for both the [Karma JavaScript test runner](https://karma-runner.github.io/latest/config/configuration-file.html)
|
||||
and [Protractor](https://www.protractortest.org/#/api-overview) end-to-end testing tool,
|
||||
which you must adjust to start Chrome without sandboxing.
|
||||
which you must adjust to start Chrome without sandboxing.
|
||||
|
||||
We'll be using [Headless Chrome](https://developers.google.com/web/updates/2017/04/headless-chrome#cli) in these examples.
|
||||
|
||||
@ -233,7 +235,7 @@ Now you can run the following commands to use the `--no-sandbox` flag:
|
||||
## Enable code coverage reports
|
||||
|
||||
The CLI can run unit tests and create code coverage reports.
|
||||
Code coverage reports show you any parts of our code base that may not be properly tested by your unit tests.
|
||||
Code coverage reports show you any parts of our code base that may not be properly tested by your unit tests.
|
||||
|
||||
To generate a coverage report run the following command in the root of your project.
|
||||
|
||||
@ -241,7 +243,7 @@ To generate a coverage report run the following command in the root of your proj
|
||||
ng test --no-watch --code-coverage
|
||||
</code-example>
|
||||
|
||||
When the tests are complete, the command creates a new `/coverage` folder in the project. Open the `index.html` file to see a report with your source code and code coverage values.
|
||||
When the tests are complete, the command creates a new `/coverage` folder in the project. Open the `index.html` file to see a report with your source code and code coverage values.
|
||||
|
||||
If you want to create code-coverage reports every time you test, you can set the following option in the CLI configuration file, `angular.json`:
|
||||
|
||||
@ -2251,8 +2253,10 @@ tests with the `RouterTestingModule`.
|
||||
|
||||
The `HeroDetailComponent` is a simple view with a title, two hero fields, and two buttons.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/testing/hero-detail.component.png' alt="HeroDetailComponent in action">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/testing/hero-detail.component.png' alt="HeroDetailComponent in action">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
But there's plenty of template complexity even in this simple form.
|
||||
@ -2680,8 +2684,10 @@ A better solution is to create an artificial test component that demonstrates al
|
||||
|
||||
<code-example path="testing/src/app/shared/highlight.directive.spec.ts" region="test-component" header="app/shared/highlight.directive.spec.ts (TestComponent)"></code-example>
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/testing/highlight-directive-spec.png' alt="HighlightDirective spec in action">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/testing/highlight-directive-spec.png' alt="HighlightDirective spec in action">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -2761,8 +2767,10 @@ Debug specs in the browser in the same way that you debug an application.
|
||||
1. Set a breakpoint in the test.
|
||||
1. Refresh the browser, and it stops at the breakpoint.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/testing/karma-1st-spec-debug.png' alt="Karma debugging">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/testing/karma-1st-spec-debug.png' alt="Karma debugging">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
<hr>
|
||||
@ -2955,7 +2963,7 @@ appropriate to the method, that is, the parameter of an `@NgModule`,
|
||||
{@a testbed-methods}
|
||||
{@a testbed-api-summary}
|
||||
|
||||
The `TestBed` API consists of static class methods that either update or reference a _global_ instance of the`TestBed`.
|
||||
The `TestBed` API consists of static class methods that either update or reference a _global_ instance of the `TestBed`.
|
||||
|
||||
Internally, all static methods cover methods of the current runtime `TestBed` instance,
|
||||
which is also returned by the `getTestBed()` function.
|
||||
|
@ -14,8 +14,10 @@ An asterisk `*` or *wildcard* matches any animation state. This is useful for de
|
||||
|
||||
For example, a transition of `open => *` applies when the element's state changes from open to anything else.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/animations/wildcard-state-500.png" alt="wildcard state expressions">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/animations/wildcard-state-500.png" alt="wildcard state expressions">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Here's another code sample using the wildcard state together with our previous example using the `open` and `closed` states. Instead of defining each state-to-state transition pair, we're now saying that any transition to `closed` takes 1 second, and any transition to `open` takes 0.5 seconds.
|
||||
@ -32,8 +34,10 @@ Use a double arrow syntax to specify state-to-state transitions in both directio
|
||||
|
||||
In our two-state button example, the wildcard isn't that useful because there are only two possible states, `open` and `closed`. Wildcard states are better when an element in one particular state has multiple potential states that it can change to. If our button can change from `open` to either `closed` or something like `inProgress`, using a wildcard state could reduce the amount of coding needed.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/animations/wildcard-3-states.png" alt="wildcard state with 3 states">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/animations/wildcard-3-states.png" alt="wildcard state with 3 states">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -123,7 +127,7 @@ The `transition()` function takes additional selector values, `:increment` and `
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
**Note:** The following example uses `query()` and `stagger()` methods, which is discussed in the [complex sequences](guide/complex-animation-sequences#complex-sequence) page.
|
||||
**Note:** The following example uses `query()` and `stagger()` methods, which is discussed in the [complex sequences](guide/complex-animation-sequences#complex-sequence) page.
|
||||
|
||||
</div>
|
||||
|
||||
@ -217,8 +221,10 @@ In the previous section, we saw a simple two-state transition. Now we'll create
|
||||
|
||||
Angular's `keyframe()` function is similar to keyframes in CSS. Keyframes allow several style changes within a single timing segment. For example, our button, instead of fading, could change color several times over a single 2-second timespan.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/animations/keyframes-500.png" alt="keyframes">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/animations/keyframes-500.png" alt="keyframes">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The code for this color change might look like this.
|
||||
@ -231,8 +237,10 @@ Keyframes include an *offset* that defines the point in the animation where each
|
||||
|
||||
Defining offsets for keyframes is optional. If you omit them, evenly spaced offsets are automatically assigned. For example, three keyframes without predefined offsets receive offsets of 0, 0.5, and 1. Specifying an offset of 0.8 for the middle transition in the above example might look like this.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/animations/keyframes-offset-500.png" alt="keyframes with offset">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/animations/keyframes-offset-500.png" alt="keyframes with offset">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The code with offsets specified would be as follows.
|
||||
@ -252,8 +260,10 @@ Here's an example of using keyframes to create a pulse effect:
|
||||
|
||||
* A keyframes sequence inserted in the middle that causes the button to appear to pulsate irregularly over the course of that same 1-second timeframe
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/animations/keyframes-pulsation.png" alt="keyframes with irregular pulsation">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/animations/keyframes-pulsation.png" alt="keyframes with irregular pulsation">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The code snippet for this animation might look like this.
|
||||
|
@ -34,7 +34,6 @@ The initial `tsconfig.json` for an Angular app typically looks like this example
|
||||
<code-example lang="json" header="tsconfig.json" linenums="false">
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
@ -54,6 +53,7 @@ The initial `tsconfig.json` for an Angular app typically looks like this example
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
||||
</code-example>
|
||||
|
||||
|
||||
|
@ -39,7 +39,7 @@ To check your app's version of Angular: From within your project directory, use
|
||||
|
||||
The most recent stable released version of Angular appears in the [Angular documentation](https://angular.io/docs "Angular documentation") at the bottom of the left side navigation. For example, `stable (v5.2.9)`.
|
||||
|
||||
You can also find the most current version of Angular by using the CLI command [`ng update`](cli/update). By default, `ng update` (without additional arguments) lists the updates that are available to you.
|
||||
You can also find the most current version of Angular by using the CLI command [`ng update`](cli/update). By default, [`ng update`](cli/update)(without additional arguments) lists the updates that are available to you.
|
||||
|
||||
|
||||
{@a updating}
|
||||
@ -49,7 +49,7 @@ To make updating easy, we provide complete instructions in the interactive [Angu
|
||||
|
||||
The Angular Update Guide provides customized update instructions, based on the current and target versions that you specify. It includes basic and advanced update paths, to match the complexity of your applications. It also includes troubleshooting information and any recommended manual changes to help you get the most out of the new release.
|
||||
|
||||
For simple updates, the CLI command [`ng update`](cli/update) is all you need. Without additional arguments, `ng update` lists the updates that are available to you and provides recommended steps to update your application to the most current version.
|
||||
For simple updates, the CLI command [`ng update`](cli/update) is all you need. Without additional arguments, [`ng update`](cli/update) lists the updates that are available to you and provides recommended steps to update your application to the most current version.
|
||||
|
||||
[Angular Versioning and Releases](guide/releases#versioning "Angular Release Practices, Versioning") describes the level of change that you can expect based a release's version number. It also describes supported update paths.
|
||||
|
||||
@ -69,4 +69,4 @@ For simple updates, the CLI command [`ng update`](cli/update) is all you need. W
|
||||
|
||||
* Versioning, release, support, and deprecation practices: [Angular versioning and releases](guide/releases "Angular versioning and releases")
|
||||
|
||||
* Release schedule: [Angular versioning and releases](guide/releases#schedule "Angular versioning and releases")
|
||||
* Release schedule: [Angular versioning and releases](guide/releases#schedule "Angular versioning and releases")
|
||||
|
@ -315,7 +315,7 @@ Angular app. The setup for a hybrid app is mostly the same as described in the
|
||||
`main-aot.ts`.
|
||||
|
||||
AOT needs to load any AngularJS files that are in the `<script>` tags in the AngularJS `index.html`.
|
||||
An easy way to copy them is to add each to the `copy-dist-files.js`file.
|
||||
An easy way to copy them is to add each to the `copy-dist-files.js` file.
|
||||
|
||||
You also need to pass the generated `MainAngularModuleFactory` to `downgradeModule()` instead of the
|
||||
custom bootstrap function:
|
||||
|
@ -266,8 +266,10 @@ everything work seamlessly:
|
||||
When you register a downgraded service, you must explicitly specify a *string token* that you want to
|
||||
use in AngularJS.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/upgrade/injectors.png" alt="The two injectors in a hybrid application">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/upgrade/injectors.png" alt="The two injectors in a hybrid application">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
#### Components and the DOM
|
||||
@ -302,8 +304,10 @@ ways:
|
||||
bridges the related concepts of AngularJS transclusion and Angular content
|
||||
projection together.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/upgrade/dom.png" alt="DOM element ownership in a hybrid application">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/upgrade/dom.png" alt="DOM element ownership in a hybrid application">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Whenever you use a component that belongs to the other framework, a
|
||||
@ -347,12 +351,14 @@ AngularJS and Angular approaches. Here's what happens:
|
||||
every turn of the Angular zone. This also triggers AngularJS change
|
||||
detection after every event.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/upgrade/change_detection.png" alt="Change detection in a hybrid application">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/upgrade/change_detection.png" alt="Change detection in a hybrid application">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
In practice, you do not need to call `$apply()`,
|
||||
regardless of whether it is in AngularJS on Angular. The
|
||||
regardless of whether it is in AngularJS or Angular. The
|
||||
`UpgradeModule` does it for us. You *can* still call `$apply()` so there
|
||||
is no need to remove such calls from existing code. Those calls just trigger
|
||||
additional AngularJS change detection checks in a hybrid application.
|
||||
@ -962,7 +968,7 @@ Once you introduce the Angular Router, using the Angular Router triggers navigat
|
||||
|
||||
You can take advantage of Ahead-of-time (AOT) compilation on hybrid apps just like on any other
|
||||
Angular application.
|
||||
The setup for an hybrid app is mostly the same as described in
|
||||
The setup for a hybrid app is mostly the same as described in
|
||||
[the Ahead-of-time Compilation chapter](guide/aot-compiler)
|
||||
save for differences in `index.html` and `main-aot.ts`
|
||||
|
||||
@ -1500,7 +1506,7 @@ This is something you'll do to all components as you upgrade them. Simultaneousl
|
||||
with the AngularJS to Angular upgrade you're also migrating code from scripts to modules.
|
||||
|
||||
At this point, you can switch the two components to use the new service
|
||||
instead of the old one. While you `$inject` it as the downgraded `phone` factory,
|
||||
instead of the old one. While you `$inject` it as the downgraded `phone` factory,
|
||||
it's really an instance of the `Phone` class and you annotate its type accordingly:
|
||||
|
||||
<code-example path="upgrade-phonecat-2-hybrid/app/phone-list/phone-list.component.ajs.ts" header="app/phone-list/phone-list.component.ts">
|
||||
|
@ -69,7 +69,7 @@ In this case, `target` refers to the [`<input>` element](https://developer.mozil
|
||||
`event.target.value` returns the current contents of that element.
|
||||
|
||||
After each call, the `onKey()` method appends the contents of the input box value to the list
|
||||
in the component's `values` property, followed by a separator character (|).
|
||||
in the component's `values` property, followed by a separator character (|).
|
||||
The [interpolation](guide/template-syntax#interpolation)
|
||||
displays the accumulating input box changes from the `values` property.
|
||||
|
||||
@ -82,8 +82,10 @@ Here's what the UI displays:
|
||||
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/user-input/keyup1-anim.gif' alt="key up 1">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/user-input/keyup1-anim.gif' alt="key up 1">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -161,8 +163,10 @@ and the component does nothing.
|
||||
Type something in the input box, and watch the display update with each keystroke.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/user-input/keyup-loop-back-anim.gif' alt="loop back">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/user-input/keyup-loop-back-anim.gif' alt="loop back">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -211,8 +215,10 @@ Then Angular calls the event handler only when the user presses _Enter_.
|
||||
|
||||
Here's how it works.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/user-input/keyup3-anim.gif' alt="key up 3">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/user-input/keyup3-anim.gif' alt="key up 3">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -243,8 +249,10 @@ The user can add a hero by typing the hero's name in the input box and
|
||||
clicking **Add**.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -268,7 +276,7 @@ get the input box value and pass *that* to `addHero`.
|
||||
|
||||
* **Keep template statements simple** —
|
||||
The `(blur)` event is bound to two JavaScript statements.
|
||||
The first statement calls `addHero`. The second statement, `newHero.value=''`,
|
||||
The first statement calls `addHero`. The second statement, `newHero.value=''`,
|
||||
clears the input box after a new hero is added to the list.
|
||||
|
||||
|
||||
|
@ -121,7 +121,7 @@ Some scripts extend other libraries; for instance with JQuery plugins:
|
||||
$('.test').myPlugin();
|
||||
```
|
||||
|
||||
In this case, the installed `@types/jquery` doesn't include `myPlugin`, so you need to add an interface in `src/typings.d.ts`. For example:
|
||||
In this case, the installed `@types/jquery` doesn't include `myPlugin`, so you need to add an interface in `src/typings.d.ts`. For example:
|
||||
|
||||
```
|
||||
interface JQuery {
|
||||
|
@ -11,7 +11,7 @@ This cookbook describes the steps required to set up and use Angular app files i
|
||||
|
||||
|
||||
There is no *live example* for this cookbook because it describes Visual Studio, not
|
||||
the Angular application itself. It uses the starter Angular application created by the CLI command [`ng new`](cli/new) as an example.
|
||||
the Angular application itself. It uses the starter Angular application created by the CLI command [`ng new`](cli/new) as an example.
|
||||
|
||||
|
||||
</div>
|
||||
@ -30,8 +30,8 @@ Visual Studio 2015, follow these steps:
|
||||
|
||||
If you prefer a `File | New Project` experience and are using **ASP.NET Core**,
|
||||
then consider the _experimental_
|
||||
<a href="http://blog.stevensanderson.com/2016/10/04/angular2-template-for-visual-studio/">ASP.NET Core + Angular template for Visual Studio 2015</a>.
|
||||
Note that the resulting code does not map to the docs. Adjust accordingly.
|
||||
<a href="http://blog.stevensanderson.com/2016/10/04/angular2-template-for-visual-studio/">ASP.NET Core + Angular template for Visual Studio 2015</a>.
|
||||
Note that the resulting code does not map to the docs. Adjust accordingly.
|
||||
|
||||
|
||||
</div>
|
||||
@ -47,7 +47,7 @@ Note that the resulting code does not map to the docs. Adjust accordingly.
|
||||
|
||||
Install **[Node.js® and npm](https://nodejs.org/en/download/)**
|
||||
if they are not already on your machine.
|
||||
See [Local Environment Setup](guide/setup-local "Setting up for Local Development") for supported versions and instructions.
|
||||
See [Local Environment Setup](guide/setup-local "Setting up for Local Development") for supported versions and instructions.
|
||||
|
||||
|
||||
|
||||
@ -115,10 +115,10 @@ restart it to make sure everything is clean.
|
||||
Step 1: Create a starter Angular app
|
||||
</h2>
|
||||
|
||||
|
||||
Follow the instructions in [Local Environment Setup](guide/setup-local "Setting up for Local Development") to create a starter Angular app using the CLI command [`ng new`](cli/new).
|
||||
|
||||
|
||||
Follow the instructions in [Local Environment Setup](guide/setup-local "Setting up for Local Development") to create a starter Angular app using the CLI command [`ng new`](cli/new).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -165,7 +165,7 @@ Include the files in the Visual Studio project as follows:
|
||||
* src/index.html
|
||||
* package.json
|
||||
* src/tsconfig.json
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id='restore'>
|
||||
@ -177,7 +177,7 @@ Include the files in the Visual Studio project as follows:
|
||||
Restore the packages required for an Angular application as follows:
|
||||
|
||||
* Right-click on the `package.json` file in Solution Explorer and select `Restore Packages`.
|
||||
<br>This uses `npm` to install all of the packages defined in the `package.json` file.
|
||||
<br>This uses `npm` to install all of the packages defined in the `package.json` file.
|
||||
It may take some time.
|
||||
* If desired, open the Output window (`View` | `Output`) to watch the npm commands execute.
|
||||
* Ignore the warnings.
|
||||
@ -199,12 +199,12 @@ Right-click `index.html` in Solution Explorer and select option `Set As Start Pa
|
||||
|
||||
### To run in VS with F5
|
||||
|
||||
Most Visual Studio developers like to press the F5 key and see the IIS server come up.
|
||||
To use the IIS server with the Getting Started app, you must make the following three changes.
|
||||
Most Visual Studio developers like to press the F5 key and see the IIS server come up.
|
||||
To use the IIS server with the Getting Started app, you must make the following three changes.
|
||||
|
||||
1. In `index.html`, change base href from `<base href="/">` to `<base href="/src/">`.
|
||||
2. Also in `index.html`, change the scripts to use `/node_modules` with a slash
|
||||
instead of `node_modules` without the slash.
|
||||
2. Also in `index.html`, change the scripts to use `/node_modules` with a slash
|
||||
instead of `node_modules` without the slash.
|
||||
3. In `src/systemjs.config.js`, near the top of the file,
|
||||
change the npm `path` to `/node_modules/` with a slash.
|
||||
|
||||
@ -227,7 +227,7 @@ If your app uses routing, you need to teach the server to always return
|
||||
`index.html` when the user asks for an HTML page
|
||||
for reasons explained in the [Deployment](guide/deployment#fallback) guide.
|
||||
|
||||
Everything seems fine while you move about _within_ the app.
|
||||
Everything seems fine while you move about _within_ the app.
|
||||
But you'll see the problem right away if you refresh the browser
|
||||
or paste a link to an app page (called a "deep link") into the browser address bar.
|
||||
|
||||
@ -241,11 +241,11 @@ This section walks through the steps to adapt the Getting Started application.
|
||||
|
||||
#### Configure IIS rewrite rules
|
||||
|
||||
Visual Studio ships with IIS Express, which has the rewrite module baked in.
|
||||
Visual Studio ships with IIS Express, which has the rewrite module baked in.
|
||||
However, if you're using regular IIS you'll have to install the rewrite
|
||||
module.
|
||||
module.
|
||||
|
||||
Tell Visual Studio how to handle requests for route app pages by adding these
|
||||
Tell Visual Studio how to handle requests for route app pages by adding these
|
||||
rewrite rules near the bottom of the `web.config`:
|
||||
|
||||
|
||||
@ -274,7 +274,7 @@ rewrite rules near the bottom of the `web.config`:
|
||||
|
||||
|
||||
The match url, `<match url=".*" />`, will rewrite every request. You'll have to adjust this if
|
||||
you want some requests to get through, such as web API requests.
|
||||
you want some requests to get through, such as web API requests.
|
||||
|
||||
The URL in `<action type="Rewrite" url="/src/"/>` should
|
||||
match the base href in `index.html`.
|
||||
@ -300,4 +300,4 @@ It's faster to run without the debugger by pressing `Ctrl-F5`.
|
||||
The default browser opens and displays the Getting Started sample application.
|
||||
|
||||
Try editing any of the project files. Save and refresh the browser to
|
||||
see the changes.
|
||||
see the changes.
|
||||
|
@ -136,13 +136,13 @@ See the example in [Build target](#build-target) below.
|
||||
* The `architect/build` section configures defaults for options of the `ng build` command.
|
||||
See [Build target](#build-target) below for more information.
|
||||
|
||||
* The `architect/serve` section overrides build defaults and supplies additional serve defaults for the `ng serve` command. In addition to the options available for the `ng build` command, it adds options related to serving the app.
|
||||
* The `architect/serve` section overrides build defaults and supplies additional serve defaults for the `ng serve` command. In addition to the options available for the `ng build` command, it adds options related to serving the app.
|
||||
|
||||
* The `architect/e2e` section overrides build-option defaults for building end-to-end testing apps using the `ng e2e` command.
|
||||
|
||||
* The `architect/test` section overrides build-option defaults for test builds and supplies additional test-running defaults for the `ng test` command.
|
||||
|
||||
* The `architect/lint` section configures defaults for options of the `ng lint` command, which performs code analysis on project source files. The default linting tool for Angular is [TSLint](https://palantir.github.io/tslint/).
|
||||
* The `architect/lint` section configures defaults for options of the `ng lint` command, which performs code analysis on project source files. The default linting tool for Angular is [TSLint](https://palantir.github.io/tslint/).
|
||||
|
||||
* The `architect/extract-i18n` section configures defaults for options of the `ng-xi18n` tool used by the `ng xi18n` command, which extracts marked message strings from source code and outputs translation files.
|
||||
|
||||
@ -184,7 +184,7 @@ Some additional options can only be set through the configuration file, either b
|
||||
|
||||
| OPTIONS PROPERTIES | DESCRIPTION |
|
||||
| :------------------------- | :---------------------------- |
|
||||
| `assets` | An object containing paths to static assets to add to the global context of the project. The default paths point to the project's icon file and its `assets` folder. See more in [Assets configuration](#asset-config) below. |
|
||||
| `assets` | An object containing paths to static assets to add to the global context of the project. The default paths point to the project's icon file and its `assets` folder. See more in [Assets configuration](#asset-config) below. |
|
||||
| `styles` | An array of style files to add to the global context of the project. Angular CLI supports CSS imports and all major CSS preprocessors: [sass/scss](http://sass-lang.com/), [less](http://lesscss.org/), and [stylus](http://stylus-lang.com/). See more in [Styles and scripts configuration](#style-script-config) below. |
|
||||
| `stylePreprocessorOptions` | An object containing option-value pairs to pass to style preprocessors. See more in [Styles and scripts configuration](#style-script-config) below. |
|
||||
| `scripts` | An object containing JavaScript script files to add to the global context of the project. The scripts are loaded exactly as if you had added them in a `<script>` tag inside `index.html`. See more in [Styles and scripts configuration](#style-script-config) below. |
|
||||
|
@ -66,6 +66,13 @@
|
||||
"rev": true,
|
||||
"title": "Happy Angular Podcast",
|
||||
"url": "https://happy-angular.de/"
|
||||
},
|
||||
"ngruair": {
|
||||
"desc": "Russian language video podcast about Angular.",
|
||||
"logo": "",
|
||||
"rev": true,
|
||||
"title": "NgRuAir",
|
||||
"url": "https://github.com/ngRuAir/ngruair"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -667,6 +674,13 @@
|
||||
"rev": true,
|
||||
"title": "MDB Angular Boilerplate",
|
||||
"url": "https://github.com/mdbootstrap/Angular-Bootstrap-Boilerplate"
|
||||
},
|
||||
"dotnettricks": {
|
||||
"desc": "Online videos and training for Angular.",
|
||||
"logo": "",
|
||||
"rev": true,
|
||||
"title": "DotNetTricks",
|
||||
"url": "https://www.dotnettricks.com/courses/angular"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -20,7 +20,7 @@ Services are the place where you share data between parts of your application. F
|
||||
|
||||
Up to this point, users can view product information, and simulate sharing and being notified about product changes. They cannot, however, buy products.
|
||||
|
||||
In this section, you'll add a "Buy" button the product details page.
|
||||
In this section, you'll add a "Buy" button to the product details page.
|
||||
You'll also set up a cart service to store information about products in the cart.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -89,7 +89,7 @@ When the "Buy" button is clicked, you'll use the cart service to add the current
|
||||
|
||||
The `addToCart()` method:
|
||||
* Receives the current `product`
|
||||
* Uses the cart service's `#addToCart()` method to add the product the cart
|
||||
* Uses the cart service's `#addToCart()` method to add the product to the cart
|
||||
* Displays a message that the product has been added to the cart
|
||||
|
||||
<code-example path="getting-started/src/app/product-details/product-details.component.ts" region="add-to-cart"></code-example>
|
||||
@ -105,14 +105,18 @@ When the "Buy" button is clicked, you'll use the cart service to add the current
|
||||
|
||||
1. To see the new "Buy" button, refresh the application and click on a product's name to display its details.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/start/product-details-buy.png' alt="Display details for selected product with a Buy button">
|
||||
</figure>
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/start/product-details-buy.png' alt="Display details for selected product with a Buy button">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
1. Click the "Buy" button. The product is added to the stored list of items in the cart, and a message is displayed.
|
||||
1. Click the "Buy" button. The product is added to the stored list of items in the cart, and a message is displayed.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/start/buy-alert.png' alt="Display details for selected product with a Buy button">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/start/buy-alert.png' alt="Display details for selected product with a Buy button">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -150,8 +154,10 @@ We'll create the cart page in two steps:
|
||||
|
||||
(Note: The "Checkout" button that we provided in the top-bar component was already configured with a `routerLink` for `/cart`.)
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/start/cart-works.png' alt="Display cart page before customizing">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/start/cart-works.png' alt="Display cart page before customizing">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -204,8 +210,10 @@ Services can be used to share data across components:
|
||||
1. Click "Checkout" to see the cart.
|
||||
1. To add another product, click "My Store" to return to the product list. Repeat the steps above.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/start/cart-page-full.png' alt="Cart page with products added">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/start/cart-page-full.png' alt="Cart page with products added">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -363,14 +371,18 @@ Now that your app can retrieve shipping data, you'll create a shipping component
|
||||
|
||||
Click on the "Checkout" button to see the updated cart. (Remember that changing the app causes the preview to refresh, which empties the cart.)
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/start/cart-empty-with-shipping-prices.png' alt="Cart with link to shipping prices">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/start/cart-empty-with-shipping-prices.png' alt="Cart with link to shipping prices">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Click on the link to navigate to the shipping prices.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/start/shipping-prices.png' alt="Display shipping prices">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/start/shipping-prices.png' alt="Display shipping prices">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -50,6 +50,12 @@ ng build --prod
|
||||
|
||||
This will produce the files that you need to deploy.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
If the above `ng build` command throws an error about missing packages, append the missing dependencies in your local project's `package.json` file to match the one in the downloaded StackBlitz project.
|
||||
|
||||
</div>
|
||||
|
||||
#### Hosting the built project
|
||||
|
||||
The files in the `dist/my-project-name` folder are static and can be hosted on any web server capable of serving files (`Node.js`, Java, .NET) or any backend (Firebase, Google Cloud, App Engine, others).
|
||||
@ -62,8 +68,11 @@ One of the easiest ways to get your site live is to host it using Firebase.
|
||||
1. Create a new project, giving it any name you like.
|
||||
1. Install the `firebase-tools` CLI that will handle your deployment using `npm install -g firebase-tools`.
|
||||
1. Connect your CLI to your Firebase account and initialize the connection to your project using `firebase login` and `firebase init`.
|
||||
1. Follow the prompts to select the `Firebase` project you creating for hosting.
|
||||
1. Deploy your application with `firebase deploy` because StackBlitz has created a `firebase.json` that tells Firebase how to serve your app.
|
||||
1. Follow the prompts to select the `Firebase` project you are creating for hosting.
|
||||
- Select the `Hosting` option on the first prompt.
|
||||
- Select the project you previously created on Firebase.
|
||||
- Select `dist/my-project-name` as the public directory.
|
||||
1. Deploy your application with `firebase deploy`, because the command `firebase init` has created a `firebase.json` file that tells Firebase how to serve your app.
|
||||
1. Once deployed, visit https://your-firebase-project-name.firebaseapp.com to see it live!
|
||||
|
||||
### Hosting an Angular app anywhere else
|
||||
|
@ -66,15 +66,17 @@ Next, you'll add a checkout form at the bottom of the "Cart" page.
|
||||
<code-example path="getting-started/src/app/cart/cart.component.html" region="checkout-form-1">
|
||||
</code-example>
|
||||
|
||||
1. Add input fields for `name` and `address`. Use the `formControlName` attribute binding to bind the `checkoutForm` form controls for `name` and `address` to their input fields. The final complete component is shown below:
|
||||
1. Add input fields for `name` and `address`. Use the `formControlName` attribute binding to bind the `checkoutForm` form controls for `name` and `address` to their input fields. The final complete component is shown below:
|
||||
|
||||
<code-example path="getting-started/src/app/cart/cart.component.html" region="checkout-form-2">
|
||||
</code-example>
|
||||
|
||||
After putting a few items in the cart, users can now review their items, enter name and address, and submit their purchase:
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/start/cart-with-items-and-form.png' alt="Cart page with checkout form">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/start/cart-with-items-and-form.png' alt="Cart page with checkout form">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -83,4 +85,3 @@ After putting a few items in the cart, users can now review their items, enter n
|
||||
Congratulations! You have a complete online store application with a product catalog, a shopping cart, and a checkout function.
|
||||
|
||||
[Continue to the "Deployment" section](start/deployment "Getting Started: Deployment") to move to local development, or deploy your app to Firebase or your own server.
|
||||
|
||||
|
@ -15,7 +15,7 @@ This guide uses the StackBlitz Generator to show you a ready-made, simple applic
|
||||
<header>New to web development?</header>
|
||||
|
||||
|
||||
There are many resources to complement the Angular docs. Mozilla's MDN docs include both [HTML](https://developer.mozilla.org/en-US/docs/Learn/HTML "Learning HTML: Guides and tutorials") and [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript "JavaScript") introductions. [TypeScript's docs](https://www.typescriptlang.org/docs/home.html "TypeScript documentation") include a 5-minute tutorial. Various online course platforms, such as [Udemy](http://www.udemy.com "Udemy online courses") and [Codecademy](https://www.codecademy.com/ "Codeacademy online courses"), also cover web development basics.
|
||||
There are many resources to complement the Angular docs. Mozilla's MDN docs include both [HTML](https://developer.mozilla.org/en-US/docs/Learn/HTML "Learning HTML: Guides and tutorials") and [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript "JavaScript") introductions. [TypeScript's docs](https://www.typescriptlang.org/docs/home.html "TypeScript documentation") include a 5-minute tutorial. Various online course platforms, such as [Udemy](http://www.udemy.com "Udemy online courses") and [Codecademy](https://www.codecademy.com/ "Codecademy online courses"), also cover web development basics.
|
||||
|
||||
|
||||
</div>
|
||||
@ -34,8 +34,10 @@ bar—containing the store name and
|
||||
checkout icon—and the title for a product list.
|
||||
|
||||
|
||||
<figure>
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/start/new-app.png" alt="Starter online store app">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -96,8 +98,10 @@ file `product-list.component.html`.
|
||||
|
||||
The preview pane immediately updates to display the name of each product in the list.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/start/template-syntax-product-names.png" alt="Product names added to list">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/start/template-syntax-product-names.png" alt="Product names added to list">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
1. To make each product name a link to product details, add the `<a>` element and set its title to be the product's name by using the property binding `[ ]` syntax, as follows:
|
||||
@ -112,37 +116,45 @@ file `product-list.component.html`.
|
||||
property value as text; property binding `[ ]` lets you
|
||||
use the property value in a template expression.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/start/template-syntax-product-anchor.png" alt="Product name anchor text is product name property">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/start/template-syntax-product-anchor.png" alt="Product name anchor text is product name property">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
1. Add the product descriptions. On the `<p>` element, use an `*ngIf` directive so that Angular only creates the `<p>` element if the current product has a description.
|
||||
4. Add the product descriptions. On the `<p>` element, use an `*ngIf` directive so that Angular only creates the `<p>` element if the current product has a description.
|
||||
|
||||
<code-example path="getting-started/src/app/product-list/product-list.component.3.html" header="src/app/product-list/product-list.component.html">
|
||||
</code-example>
|
||||
|
||||
The app now displays the name and description of each product in the list. Notice that the final product does not have a description paragraph. Because the product's description property is empty, Angular doesn't create the `<p>` element—including the word "Description".
|
||||
|
||||
<figure>
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/start/template-syntax-product-description.png" alt="Product descriptions added to list">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
1. Add a button so users can share a product with friends. Bind the button's `click` event to the `share()` method (in `product-list.component.ts`). Event binding uses a set of parentheses, `( )`, around the event, as in the following `<button>` element:
|
||||
5. Add a button so users can share a product with friends. Bind the button's `click` event to the `share()` method (in `product-list.component.ts`). Event binding uses a set of parentheses, `( )`, around the event, as in the following `<button>` element:
|
||||
|
||||
<code-example path="getting-started/src/app/product-list/product-list.component.4.html" header="src/app/product-list/product-list.component.html">
|
||||
</code-example>
|
||||
|
||||
Each product now has a "Share" button:
|
||||
|
||||
<figure>
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/start/template-syntax-product-share-button.png" alt="Share button added for each product">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Test the "Share" button:
|
||||
|
||||
<figure>
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/start/template-syntax-product-share-alert.png" alt="Alert box indicating product has been shared">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The app now has a product list and sharing feature.
|
||||
@ -198,8 +210,10 @@ An Angular application comprises a tree of components, in which each Angular com
|
||||
|
||||
Currently, the example app has three components:
|
||||
|
||||
<figure>
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/start/app-components.png" alt="Online store with three components">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
* `app-root` (orange box) is the application shell. This is the first component to load and the parent of all other components. You can think of it as the base page.
|
||||
@ -228,8 +242,10 @@ The next step is to create a new alert feature that takes a product as an input.
|
||||
|
||||
1. Right click on the `app` folder and use the `Angular Generator` to generate a new component named `product-alerts`.
|
||||
|
||||
<figure>
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/start/generate-component.png" alt="StackBlitz command to generate component">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
The generator creates starter files for all three parts of the component:
|
||||
@ -277,11 +293,12 @@ The next step is to create a new alert feature that takes a product as an input.
|
||||
|
||||
The new product alert component takes a product as input from the product list. With that input, it shows or hides the "Notify Me" button, based on the price of the product. The Phone XL price is over $700, so the "Notify Me" button appears on that product.
|
||||
|
||||
<figure>
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/start/product-alert-button.png" alt="Product alert button added to products over $700">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
See [Component Interaction](guide/component-interaction "Components & Templates > Component Interaction") for more information about passing data from a parent to child component, intercepting and acting upon a value from the parent, and detecting and acting on changes to input property values.
|
||||
@ -311,7 +328,7 @@ To make the "Notify Me" button work, you need to configure two things:
|
||||
|
||||
<code-example header="src/app/product-alerts/product-alerts.component.html" path="getting-started/src/app/product-alerts/product-alerts.component.html"></code-example>
|
||||
|
||||
1. Next, define the behavior that should happen when the user clicks the button. Recall that it's the parent, product list component—not the product alerts component—that's acts when the child raises the event. In `product-list.component.ts`, define an `onNotify()` method, similar to the `share()` method:
|
||||
1. Next, define the behavior that should happen when the user clicks the button. Recall that it's the parent, product list component—not the product alerts component—that acts when the child raises the event. In `product-list.component.ts`, define an `onNotify()` method, similar to the `share()` method:
|
||||
|
||||
<code-example header="src/app/product-list/product-list.component.ts" path="getting-started/src/app/product-list/product-list.component.ts" region="on-notify"></code-example>
|
||||
|
||||
@ -323,8 +340,10 @@ To make the "Notify Me" button work, you need to configure two things:
|
||||
|
||||
1. Try the "Notify Me" button:
|
||||
|
||||
<figure>
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/start/product-alert-notification.png" alt="Product alert notification confirmation dialog">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -347,4 +366,3 @@ You've also learned how the component class and template interact, and how compo
|
||||
To continue exploring Angular, choose either of the following options:
|
||||
* [Continue to the "Routing" section](start/routing "Getting Started: Routing") to create a product details page that can be accessed by clicking a product name and that has its own URL pattern.
|
||||
* [Skip ahead to the "Deployment" section](start/deployment "Getting Started: Deployment") to move to local development, or deploy your app to Firebase or your own server.
|
||||
|
||||
|
@ -54,8 +54,10 @@ The app is already set up to use the Angular router and to use routing to naviga
|
||||
|
||||
Notice that the URL in the preview window changes. The final segment is `products/1`.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/start/product-details-works.png" alt="Product details page with updated URL">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/start/product-details-works.png" alt="Product details page with updated URL">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
@ -90,8 +92,16 @@ The product details component handles the display of each product. The Angular R
|
||||
<code-example path="getting-started/src/app/product-details/product-details.component.1.ts" region="get-product">
|
||||
</code-example>
|
||||
|
||||
Angular calls `ngOnInit()` shortly after creating a component.
|
||||
|
||||
The route parameters correspond to the path variables defined in the route. The `productId` is provided from
|
||||
the URL that was matched to the route. You use the `productId` to display the details for each unique product.
|
||||
the URL that was matched to the route. You use the `productId` to display the details for each unique product.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For more information on `ngOnInit()`, see [Lifecycle hooks](guide/lifecycle-hooks).
|
||||
|
||||
</div>
|
||||
|
||||
1. Update the template to display product details information inside an `*ngIf`.
|
||||
|
||||
@ -100,8 +110,10 @@ The product details component handles the display of each product. The Angular R
|
||||
|
||||
Now, when the user clicks on a name in the product list, the router navigates you to the distinct URL for the product, swaps out the product list component for the product details component, and displays the product details.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/start/product-details-routed.png" alt="Product details page with updated URL and full details displayed">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src="generated/images/guide/start/product-details-routed.png" alt="Product details page with updated URL and full details displayed">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
|
||||
|
@ -49,8 +49,10 @@ After completing all tutorial steps, the final app will look like this: <live-ex
|
||||
Here's a visual idea of where this tutorial leads, beginning with the "Dashboard"
|
||||
view and the most heroic heroes:
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/toh/heroes-dashboard-1.png' alt="Output of heroes dashboard">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/toh/heroes-dashboard-1.png' alt="Output of heroes dashboard">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
You can click the two links above the dashboard ("Dashboard" and "Heroes")
|
||||
@ -59,8 +61,10 @@ to navigate between this Dashboard view and a Heroes view.
|
||||
If you click the dashboard hero "Magneta," the router opens a "Hero Details" view
|
||||
where you can change the hero's name.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/toh/hero-details-1.png' alt="Details of hero in app">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/toh/hero-details-1.png' alt="Details of hero in app">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Clicking the "Back" button returns you to the Dashboard.
|
||||
@ -68,8 +72,10 @@ Links at the top take you to either of the main views.
|
||||
If you click "Heroes," the app displays the "Heroes" master list view.
|
||||
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/toh/heroes-list-2.png' alt="Output of heroes list app">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/toh/heroes-list-2.png' alt="Output of heroes list app">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
When you click a different hero name, the read-only mini detail beneath the list reflects the new choice.
|
||||
@ -79,12 +85,16 @@ editable details of the selected hero.
|
||||
|
||||
The following diagram captures all of the navigation options.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/toh/nav-diagram.png' alt="View navigations">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/toh/nav-diagram.png' alt="View navigations">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
Here's the app in action:
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/toh/toh-anim.gif' alt="Tour of Heroes in Action">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/toh/toh-anim.gif' alt="Tour of Heroes in Action">
|
||||
</div>
|
||||
</figure>
|
||||
|
@ -55,7 +55,7 @@ Go to the workspace directory and launch the application.
|
||||
The `ng serve` command builds the app, starts the development server,
|
||||
watches the source files, and rebuilds the app as you make changes to those files.
|
||||
|
||||
The `--open` flag opens a browser to `http://localhost:4200/`.
|
||||
The `--open` flag opens a browser to `http://localhost:4200/`.
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -196,10 +196,10 @@ It's difficult to identify the _selected hero_ in the list when all `<li>` eleme
|
||||
|
||||
If the user clicks "Magneta", that hero should render with a distinctive but subtle background color like this:
|
||||
|
||||
<figure>
|
||||
|
||||
<img src='generated/images/guide/toh/heroes-list-selected.png' alt="Selected hero">
|
||||
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/toh/heroes-list-selected.png' alt="Selected hero">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
That _selected hero_ coloring is the work of the `.selected` CSS class in the [styles you added earlier](#styles).
|
||||
@ -208,7 +208,7 @@ You just have to apply the `.selected` class to the `<li>` when the user clicks
|
||||
The Angular [class binding](guide/template-syntax#class-binding) makes it easy to add and remove a CSS class conditionally.
|
||||
Just add `[class.some-css-class]="some-condition"` to the element you want to style.
|
||||
|
||||
Add the following `[class.selected]` binding to the `<li>` in the `HeroesComponent` template:
|
||||
Add the following `[class.selected]` binding to the `<li>` in the `HeroesComponent` template:
|
||||
|
||||
<code-example path="toh-pt2/src/app/heroes/heroes.component.1.html" region="class-selected" header="heroes.component.html (toggle the 'selected' CSS class)"></code-example>
|
||||
|
||||
|
@ -95,7 +95,7 @@ The `HeroService` is now ready to plug into the `HeroesComponent`.
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
This is an interim code sample that will allow you to provide and use the `HeroService`. At this point, the code will differ from the `HeroService` in the ["final code review"](#final-code-review).
|
||||
This is an interim code sample that will allow you to provide and use the `HeroService`. At this point, the code will differ from the `HeroService` in the ["final code review"](#final-code-review).
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -9,10 +9,10 @@ There are new requirements for the Tour of Heroes app:
|
||||
|
||||
When you’re done, users will be able to navigate the app like this:
|
||||
|
||||
<figure>
|
||||
|
||||
<img src='generated/images/guide/toh/nav-diagram.png' alt="View navigations">
|
||||
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/toh/nav-diagram.png' alt="View navigations">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
## Add the `AppRoutingModule`
|
||||
@ -44,7 +44,7 @@ Replace it with the following:
|
||||
<code-example path="toh-pt5/src/app/app-routing.module.1.ts" header="src/app/app-routing.module.ts (updated)">
|
||||
</code-example>
|
||||
|
||||
First, `AppRoutingModule` imports `RouterModule` and `Routes` so the app can have routing functionality. The next import, `HeroesComponent`, will give the Router somewhere to go once you configure the routes.
|
||||
First, `AppRoutingModule` imports `RouterModule` and `Routes` so the app can have routing functionality. The next import, `HeroesComponent`, will give the Router somewhere to go once you configure the routes.
|
||||
|
||||
Notice that the `CommonModule` references and `declarations` array are unnecessary, so are no
|
||||
longer part of `AppRoutingModule`. The following sections explain the rest of the `AppRoutingModule` in more detail.
|
||||
|
@ -272,7 +272,7 @@ That header is in the `httpOptions` constant defined in the `HeroService`. Add t
|
||||
</code-example>
|
||||
|
||||
Refresh the browser, change a hero name and save your change. The `save()`
|
||||
method in `HeroDetailComponent`navigates to the previous view.
|
||||
method in `HeroDetailComponent` navigates to the previous view.
|
||||
The hero now appears in the list with the changed name.
|
||||
|
||||
|
||||
@ -324,7 +324,7 @@ The HTML for the list of heroes should look like this:
|
||||
<code-example path="toh-pt6/src/app/heroes/heroes.component.html" region="list" header="src/app/heroes/heroes.component.html (list of heroes)"></code-example>
|
||||
|
||||
To position the delete button at the far right of the hero entry,
|
||||
add some CSS to the `heroes.component.css`. You'll find that CSS
|
||||
add some CSS to the `heroes.component.css`. You'll find that CSS
|
||||
in the [final review code](#heroescomponent) below.
|
||||
|
||||
Add the `delete()` handler to the component class.
|
||||
@ -513,8 +513,10 @@ That's the job of the [`AsyncPipe`](#asyncpipe) in the template.
|
||||
Run the app again. In the *Dashboard*, enter some text in the search box.
|
||||
If you enter characters that match any existing hero names, you'll see something like this.
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/toh/toh-hero-search.png' alt="Hero Search Component">
|
||||
<figure class="lightbox">
|
||||
<div class="card">
|
||||
<img src='generated/images/guide/toh/toh-hero-search.png' alt="Hero Search Component">
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
## Final code review
|
||||
|
@ -19,11 +19,11 @@
|
||||
"build-local": "yarn ~~build",
|
||||
"prebuild-local-ci": "yarn setup-local --no-build-packages",
|
||||
"build-local-ci": "yarn ~~build --progress=false",
|
||||
"prebuild-with-ivy": "yarn setup-local && node scripts/switch-to-ivy",
|
||||
"build-with-ivy": "yarn ~~build",
|
||||
"prebuild-with-ivy-ci": "yarn setup-local --no-build-packages && node scripts/switch-to-ivy",
|
||||
"build-with-ivy-ci": "yarn ~~build --progress=false",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js e21aeeecd",
|
||||
"prebuild-local-with-ivy": "yarn setup-local && node scripts/switch-to-ivy",
|
||||
"build-local-with-ivy": "yarn ~~build",
|
||||
"prebuild-local-with-ivy-ci": "yarn setup-local --no-build-packages && node scripts/switch-to-ivy",
|
||||
"build-local-with-ivy-ci": "yarn ~~build --progress=false",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 5bf754195",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
||||
"test": "yarn check-env && ng test",
|
||||
"pree2e": "yarn check-env && yarn update-webdriver",
|
||||
@ -115,7 +115,7 @@
|
||||
"chalk": "^2.1.0",
|
||||
"chrome-launcher": "^0.10.7",
|
||||
"cjson": "^0.5.0",
|
||||
"codelyzer": "^5.0.0",
|
||||
"codelyzer": "^5.1.1",
|
||||
"cross-spawn": "^5.1.0",
|
||||
"css-selector-parser": "^1.3.0",
|
||||
"dgeni": "^0.4.11",
|
||||
|
@ -2,11 +2,8 @@
|
||||
"aio": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es5": 3042,
|
||||
"runtime-es2015": 3048,
|
||||
"main-es5": 511052,
|
||||
"main-es2015": 450562,
|
||||
"polyfills-es5": 129161,
|
||||
"polyfills-es2015": 53295
|
||||
}
|
||||
}
|
||||
@ -14,11 +11,8 @@
|
||||
"aio-local": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es5": 3042,
|
||||
"runtime-es2015": 3048,
|
||||
"main-es5": 499085,
|
||||
"main-es2015": 438296,
|
||||
"polyfills-es5": 129161,
|
||||
"polyfills-es2015": 53295
|
||||
}
|
||||
}
|
||||
@ -26,11 +20,8 @@
|
||||
"aio-local-ivy": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es5": 2932,
|
||||
"runtime-es2015": 2938,
|
||||
"main-es5": 555102,
|
||||
"main-es2015": 572938,
|
||||
"polyfills-es5": 129161,
|
||||
"polyfills-es2015": 53295
|
||||
}
|
||||
}
|
||||
|
@ -464,14 +464,14 @@ describe('AppComponent', () => {
|
||||
let scrollSpy: jasmine.Spy;
|
||||
let scrollToTopSpy: jasmine.Spy;
|
||||
let scrollAfterRenderSpy: jasmine.Spy;
|
||||
let removeStoredScrollPositionSpy: jasmine.Spy;
|
||||
let removeStoredScrollInfoSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
scrollService = fixture.debugElement.injector.get<ScrollService>(ScrollService);
|
||||
scrollSpy = spyOn(scrollService, 'scroll');
|
||||
scrollToTopSpy = spyOn(scrollService, 'scrollToTop');
|
||||
scrollAfterRenderSpy = spyOn(scrollService, 'scrollAfterRender');
|
||||
removeStoredScrollPositionSpy = spyOn(scrollService, 'removeStoredScrollPosition');
|
||||
removeStoredScrollInfoSpy = spyOn(scrollService, 'removeStoredScrollInfo');
|
||||
});
|
||||
|
||||
it('should not scroll immediately when the docId (path) changes', () => {
|
||||
@ -516,9 +516,9 @@ describe('AppComponent', () => {
|
||||
expect(scrollSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should call `removeStoredScrollPosition` when call `onDocRemoved` directly', () => {
|
||||
it('should call `removeStoredScrollInfo` when call `onDocRemoved` directly', () => {
|
||||
component.onDocRemoved();
|
||||
expect(removeStoredScrollPositionSpy).toHaveBeenCalled();
|
||||
expect(removeStoredScrollInfoSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call `scrollAfterRender` when call `onDocInserted` directly', (() => {
|
||||
|
@ -202,7 +202,7 @@ export class AppComponent implements OnInit {
|
||||
}
|
||||
|
||||
onDocRemoved() {
|
||||
this.scrollService.removeStoredScrollPosition();
|
||||
this.scrollService.removeStoredScrollInfo();
|
||||
}
|
||||
|
||||
onDocInserted() {
|
||||
|
@ -7,10 +7,11 @@ import { LocationService } from 'app/shared/location.service';
|
||||
selector: `aio-contributor-list`,
|
||||
template: `
|
||||
<div class="flex-center group-buttons">
|
||||
<a *ngFor="let name of groupNames"
|
||||
[class.selected]="name == selectedGroup.name"
|
||||
class="button mat-button filter-button"
|
||||
(click)="selectGroup(name)">{{name}}</a>
|
||||
<a *ngFor="let name of groupNames"
|
||||
[class.selected]="name == selectedGroup.name"
|
||||
class="button mat-button filter-button"
|
||||
(click)="selectGroup(name)"
|
||||
(keyup.enter)="selectGroup(name)">{{name}}</a>
|
||||
</div>
|
||||
<section *ngIf="selectedGroup" class="grid-fluid">
|
||||
<div class="contributor-group">
|
||||
|
@ -8,7 +8,7 @@ import { CONTENT_URL_PREFIX } from 'app/documents/document.service';
|
||||
template: `
|
||||
<div [ngClass]="{ 'flipped': person.isFlipped }" class="contributor-card">
|
||||
|
||||
<div class="card-front" (click)="flipCard(person)">
|
||||
<div class="card-front" (click)="flipCard(person)" (keyup.enter)="flipCard(person)">
|
||||
<h3>{{person.name}}</h3>
|
||||
|
||||
<div class="contributor-image" [style.background-image]="'url('+pictureBase+(person.picture || noPicture)+')'">
|
||||
@ -28,7 +28,7 @@ import { CONTENT_URL_PREFIX } from 'app/documents/document.service';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-back" *ngIf="person.isFlipped" (click)="flipCard(person)">
|
||||
<div class="card-back" *ngIf="person.isFlipped" (click)="flipCard(person)" (keyup.enter)="flipCard(person)">
|
||||
<h3>{{person.name}}</h3>
|
||||
<p class="contributor-bio">{{person.bio}}</p>
|
||||
</div>
|
||||
|
@ -4,6 +4,7 @@ import { PlatformLocation } from '@angular/common';
|
||||
import { Category } from './resource.model';
|
||||
import { ResourceService } from './resource.service';
|
||||
|
||||
/* tslint:disable:template-accessibility-elements-content */
|
||||
@Component({
|
||||
selector: 'aio-resource-list',
|
||||
templateUrl: 'resource-list.component.html'
|
||||
|
@ -1,4 +1,4 @@
|
||||
<span class="content" (click)="contentClick()">
|
||||
<span class="content" (click)="contentClick()" (keyup.enter)="contentClick()">
|
||||
<ng-content></ng-content>
|
||||
</span>
|
||||
|
||||
|
@ -296,11 +296,11 @@ describe('LocationService', () => {
|
||||
it('should do a "full page navigation" and remove the stored scroll position when navigating to ' +
|
||||
'internal URLs only if a ServiceWorker update has been activated', () => {
|
||||
const goExternalSpy = spyOn(service, 'goExternal');
|
||||
const removeStoredScrollPositionSpy = spyOn(scrollService, 'removeStoredScrollPosition');
|
||||
const removeStoredScrollInfoSpy = spyOn(scrollService, 'removeStoredScrollInfo');
|
||||
|
||||
// Internal URL - No ServiceWorker update
|
||||
service.go('some-internal-url');
|
||||
expect(removeStoredScrollPositionSpy).not.toHaveBeenCalled();
|
||||
expect(removeStoredScrollInfoSpy).not.toHaveBeenCalled();
|
||||
expect(goExternalSpy).not.toHaveBeenCalled();
|
||||
expect(location.path(true)).toEqual('some-internal-url');
|
||||
|
||||
@ -308,24 +308,24 @@ describe('LocationService', () => {
|
||||
swUpdates.updateActivated.next('foo');
|
||||
service.go('other-internal-url');
|
||||
expect(goExternalSpy).toHaveBeenCalledWith('other-internal-url');
|
||||
expect(removeStoredScrollPositionSpy).toHaveBeenCalled();
|
||||
expect(removeStoredScrollInfoSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not remove the stored scroll position when navigating to external URLs', () => {
|
||||
const removeStoredScrollPositionSpy = spyOn(scrollService, 'removeStoredScrollPosition');
|
||||
const removeStoredScrollInfoSpy = spyOn(scrollService, 'removeStoredScrollInfo');
|
||||
const goExternalSpy = spyOn(service, 'goExternal');
|
||||
const externalUrl = 'http://some/far/away/land';
|
||||
const otherExternalUrl = 'http://some/far/far/away/land';
|
||||
|
||||
// External URL - No ServiceWorker update
|
||||
service.go(externalUrl);
|
||||
expect(removeStoredScrollPositionSpy).not.toHaveBeenCalled();
|
||||
expect(removeStoredScrollInfoSpy).not.toHaveBeenCalled();
|
||||
expect(goExternalSpy).toHaveBeenCalledWith(externalUrl);
|
||||
|
||||
// External URL - ServiceWorker update
|
||||
swUpdates.updateActivated.next('foo');
|
||||
service.go(otherExternalUrl);
|
||||
expect(removeStoredScrollPositionSpy).not.toHaveBeenCalled();
|
||||
expect(removeStoredScrollInfoSpy).not.toHaveBeenCalled();
|
||||
expect(goExternalSpy).toHaveBeenCalledWith(otherExternalUrl);
|
||||
});
|
||||
|
||||
@ -633,7 +633,7 @@ class MockSwUpdatesService {
|
||||
}
|
||||
|
||||
class MockScrollService {
|
||||
removeStoredScrollPosition() { }
|
||||
removeStoredScrollInfo() { }
|
||||
}
|
||||
|
||||
class TestGaService {
|
||||
|
@ -49,7 +49,7 @@ export class LocationService {
|
||||
} else if (this.swUpdateActivated) {
|
||||
// (Do a "full page navigation" if a ServiceWorker update has been activated)
|
||||
// We need to remove stored Position in order to be sure to scroll to the Top position
|
||||
this.scrollService.removeStoredScrollPosition();
|
||||
this.scrollService.removeStoredScrollInfo();
|
||||
this.goExternal(url);
|
||||
} else {
|
||||
this.location.go(url);
|
||||
|
@ -58,6 +58,10 @@ export class ScrollService implements OnDestroy {
|
||||
.pipe(debounceTime(250), takeUntil(this.onDestroy))
|
||||
.subscribe(() => this.updateScrollPositionInHistory());
|
||||
|
||||
fromEvent(window, 'beforeunload')
|
||||
.pipe(takeUntil(this.onDestroy))
|
||||
.subscribe(() => this.updateScrollLocationHref());
|
||||
|
||||
// Change scroll restoration strategy to `manual` if it's supported
|
||||
if (this.supportManualScrollRestoration) {
|
||||
history.scrollRestoration = 'manual';
|
||||
@ -70,13 +74,18 @@ export class ScrollService implements OnDestroy {
|
||||
} else {
|
||||
// Navigating with the forward/back button, we have to remove the position from the
|
||||
// session storage in order to avoid a race-condition.
|
||||
this.removeStoredScrollPosition();
|
||||
this.removeStoredScrollInfo();
|
||||
// The `popstate` event is always triggered by a browser action such as clicking the
|
||||
// forward/back button. It can be followed by a `hashchange` event.
|
||||
this.poppedStateScrollPosition = event.state ? event.state.scrollPosition : null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If this was not a reload, discard the stored scroll info.
|
||||
if (window.location.href !== this.getStoredScrollLocationHref()) {
|
||||
this.removeStoredScrollInfo();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@ -170,6 +179,10 @@ export class ScrollService implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
updateScrollLocationHref(): void {
|
||||
window.sessionStorage.setItem('scrollLocationHref', window.location.href);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the state with scroll position into history.
|
||||
*/
|
||||
@ -181,6 +194,11 @@ export class ScrollService implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
getStoredScrollLocationHref(): string | null {
|
||||
const href = window.sessionStorage.getItem('scrollLocationHref');
|
||||
return href || null;
|
||||
}
|
||||
|
||||
getStoredScrollPosition(): ScrollPosition | null {
|
||||
const position = window.sessionStorage.getItem('scrollPosition');
|
||||
if (!position) { return null; }
|
||||
@ -189,7 +207,8 @@ export class ScrollService implements OnDestroy {
|
||||
return [+x, +y];
|
||||
}
|
||||
|
||||
removeStoredScrollPosition() {
|
||||
removeStoredScrollInfo() {
|
||||
window.sessionStorage.removeItem('scrollLocationHref');
|
||||
window.sessionStorage.removeItem('scrollPosition');
|
||||
}
|
||||
|
||||
|
@ -65,10 +65,10 @@ export class TocService {
|
||||
div.innerHTML = heading.innerHTML;
|
||||
|
||||
// Remove any `.github-links` or `.header-link` elements (along with their content).
|
||||
div.querySelectorAll('.github-links, .header-link').forEach(removeNode);
|
||||
querySelectorAll(div, '.github-links, .header-link').forEach(removeNode);
|
||||
|
||||
// Remove any remaining `a` elements (but keep their content).
|
||||
div.querySelectorAll('a').forEach(anchorLink => {
|
||||
querySelectorAll(div, 'a').forEach(anchorLink => {
|
||||
// We want to keep the content of this anchor, so move it into its parent.
|
||||
const parent = anchorLink.parentNode!;
|
||||
while (anchorLink.childNodes.length) {
|
||||
@ -88,10 +88,11 @@ export class TocService {
|
||||
}
|
||||
|
||||
private findTocHeadings(docElement: Element): HTMLHeadingElement[] {
|
||||
const headings = docElement.querySelectorAll('h1,h2,h3');
|
||||
// const headings = querySelectorAll(docElement, 'h1,h2,h3');
|
||||
const headings = querySelectorAll<HTMLHeadingElement>(docElement, 'h1,h2,h3');
|
||||
const skipNoTocHeadings = (heading: HTMLHeadingElement) => !/(?:no-toc|notoc)/i.test(heading.className);
|
||||
|
||||
return Array.prototype.filter.call(headings, skipNoTocHeadings);
|
||||
return headings.filter(skipNoTocHeadings);
|
||||
}
|
||||
|
||||
private resetScrollSpyInfo() {
|
||||
@ -127,6 +128,16 @@ export class TocService {
|
||||
}
|
||||
|
||||
// Helpers
|
||||
function querySelectorAll<K extends keyof HTMLElementTagNameMap>(parent: Element, selector: K): HTMLElementTagNameMap[K][];
|
||||
function querySelectorAll<K extends keyof SVGElementTagNameMap>(parent: Element, selector: K): SVGElementTagNameMap[K][];
|
||||
function querySelectorAll<E extends Element = Element>(parent: Element, selector: string): E[];
|
||||
function querySelectorAll(parent: Element, selector: string) {
|
||||
// Wrap the `NodeList` as a regular `Array` to have access to array methods.
|
||||
// NOTE: IE11 does not even support some methods of `NodeList`, such as
|
||||
// [NodeList#forEach()](https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach).
|
||||
return Array.from(parent.querySelectorAll(selector));
|
||||
}
|
||||
|
||||
function removeNode(node: Node): void {
|
||||
if (node.parentNode !== null) {
|
||||
// We cannot use `Node.remove()` because of IE11.
|
||||
|
@ -49,8 +49,8 @@ aio-shell.page-resources mat-toolbar.mat-toolbar {
|
||||
aio-shell.folder-api mat-toolbar.mat-toolbar,
|
||||
aio-shell.folder-cli mat-toolbar.mat-toolbar,
|
||||
aio-shell.folder-docs mat-toolbar.mat-toolbar,
|
||||
aio-shell.folder-getting-started mat-toolbar.mat-toolbar,
|
||||
aio-shell.folder-guide mat-toolbar.mat-toolbar,
|
||||
aio-shell.folder-start mat-toolbar.mat-toolbar,
|
||||
aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
|
||||
@media (min-width: 992px) {
|
||||
.hamburger.mat-button {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user