mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
177 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d24cbcea70 | |||
| 4fd388b084 | |||
| 74c497289f | |||
| 892ded5865 | |||
| 87bcb3f183 | |||
| 0382f863db | |||
| 0a51212d0e | |||
| dd79d317a4 | |||
| c9718897a6 | |||
| 618a9c76f7 | |||
| 4e57cab6d4 | |||
| a23942ef53 | |||
| b671a37ba8 | |||
| a4dc5da4ee | |||
| 97eed3e83c | |||
| 9c99afdc7c | |||
| 6b248556e8 | |||
| aea63fcc17 | |||
| f4ea80a3c1 | |||
| 494d056c48 | |||
| 22c5f35a8c | |||
| 7307ea33bd | |||
| f544a53f5f | |||
| a468213f34 | |||
| 8f7ceae5df | |||
| fce7bb8daf | |||
| c50abe57c8 | |||
| 3084df8b64 | |||
| 6662fcfa12 | |||
| 40abf0307d | |||
| d1321a1fe7 | |||
| f5d236b248 | |||
| 78d0bda39e | |||
| 609579c8f2 | |||
| 57e6339cd1 | |||
| 4c22339bcb | |||
| 5049124b25 | |||
| 6d7c2e0e71 | |||
| 7120a008c8 | |||
| 50b2d273b8 | |||
| c359da412b | |||
| 3f480a1836 | |||
| 8292c6636b | |||
| 6ee479d8eb | |||
| 2fa48e5f88 | |||
| 96b99c443b | |||
| 3faef7180d | |||
| 12b7f4490f | |||
| 620a705065 | |||
| 6ff9798268 | |||
| f225bfde80 | |||
| 8ae99821d6 | |||
| 59ca52fd4f | |||
| 4ba1a249ad | |||
| 8d89b2853c | |||
| a2ce1da85b | |||
| 7de7dc89bf | |||
| b7be4cfc02 | |||
| cfc509c48a | |||
| ec23ae99d5 | |||
| 0dbe1c695a | |||
| 081a4c2722 | |||
| c44d1cbd58 | |||
| 97b6f6843f | |||
| 1571c2b58f | |||
| 269feceb67 | |||
| fa7693f784 | |||
| 8e13c351ac | |||
| 59e56e79d0 | |||
| 940eafa351 | |||
| 15bdd0d48f | |||
| 087fae5ea9 | |||
| 8277c38bd8 | |||
| 7c5d642106 | |||
| 8178a3028b | |||
| 9968a8ad82 | |||
| e569a9c1fe | |||
| 096ebb2830 | |||
| 97fc9de2c4 | |||
| 6dc4d83f37 | |||
| 23928a02d3 | |||
| 72f6877cb0 | |||
| adc68b100b | |||
| 5d031848d3 | |||
| bbbf678bed | |||
| 22009f62df | |||
| 0039b712c0 | |||
| 665466def3 | |||
| ae52bf1df3 | |||
| b31a9a75ad | |||
| 7d33af0400 | |||
| aef01389e1 | |||
| 9d9346c57a | |||
| 812a33df8c | |||
| aafde79a71 | |||
| 57bf6b7cbd | |||
| 5e562b66d4 | |||
| 04b4209d71 | |||
| f1e18493ca | |||
| 442f520e48 | |||
| 90ac77051d | |||
| 959f563fc9 | |||
| 730511b948 | |||
| c3f677dbe6 | |||
| b75ee8d810 | |||
| b489ae5ecd | |||
| c882b9fa8b | |||
| fc6519c25a | |||
| 14b492df26 | |||
| cbde9edace | |||
| dc6909ad3e | |||
| dbcc4bebee | |||
| db57e391cc | |||
| 8c91e33d5d | |||
| ebc9b55dcf | |||
| 017748c477 | |||
| ee2678ca42 | |||
| 0983eec7ab | |||
| 70c14ed66d | |||
| 772e08d14e | |||
| 12dfc0e991 | |||
| 426a3ecae7 | |||
| e3b4abe3db | |||
| 7e5268174b | |||
| 91e73c4505 | |||
| 68cdcd3259 | |||
| de5a63ba40 | |||
| 5f6e568dd6 | |||
| 39cb30e2f2 | |||
| 342d642301 | |||
| 1c3afc9658 | |||
| 6113813300 | |||
| b5eccde447 | |||
| 7ba9591a1d | |||
| 80449f4570 | |||
| b155bf71d0 | |||
| 567c8fad42 | |||
| 780082c025 | |||
| c6496350a9 | |||
| 098d7ade70 | |||
| ad832900b7 | |||
| db01894791 | |||
| 663690fc86 | |||
| 42f93418b6 | |||
| af31781e10 | |||
| 39f625a4e9 | |||
| 154dda2eed | |||
| 27ba8033dc | |||
| 8e9e27a1df | |||
| 05c03117bc | |||
| 73c1570237 | |||
| 791b6f47e4 | |||
| a589aff3e4 | |||
| 562137e16b | |||
| 44f3eeb32d | |||
| 631769ca85 | |||
| 7396021e4b | |||
| 6b9045213d | |||
| 2a67227ab3 | |||
| ef1a70fc53 | |||
| bfb2b81556 | |||
| bc36814f0d | |||
| 1869474386 | |||
| 978ac81af5 | |||
| 7778cb3680 | |||
| af11d3b721 | |||
| a64adabf65 | |||
| e2bf4e442a | |||
| 6194696340 | |||
| d049a61f2a | |||
| 7e858f362c | |||
| 747553dd68 | |||
| 06a87c4a65 | |||
| f8ae6ff08e | |||
| aef18bcbd6 | |||
| 325fafad70 | |||
| d977701a43 |
@@ -84,9 +84,8 @@ query --deleted_packages=integration/bazel,integration/bazel/src,integration/baz
|
||||
################################
|
||||
# Temporary Settings for Ivy #
|
||||
################################
|
||||
# To determine if the compiler used should be Ivy instead of ViewEngine, one can use `--config=ivy`
|
||||
# on any bazel target. This is a temporary flag until codebase is permanently switched to Ivy.
|
||||
build --define=angular_ivy_enabled=False
|
||||
# Set the default compiler used for bazel builds to be Ivy.
|
||||
build --define=angular_ivy_enabled=True
|
||||
|
||||
build:view-engine --define=angular_ivy_enabled=False
|
||||
build:ivy --define=angular_ivy_enabled=True
|
||||
|
||||
+37
-154
@@ -24,19 +24,19 @@ version: 2.1
|
||||
# **NOTE 2 **: If you change the cache key prefix, also sync the cache_key_fallback to match.
|
||||
# **NOTE 3 **: 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 v5-angular-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "aio/yarn.lock" }}
|
||||
var_3: &cache_key v6-angular-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "aio/yarn.lock" }}
|
||||
# We invalidate the cache if the Bazel version changes because otherwise the `bazelisk` cache
|
||||
# folder will contain all previously used versions and ultimately cause the cache restoring to
|
||||
# be slower due to its growing size.
|
||||
var_4: &cache_key_fallback v5-angular-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}
|
||||
var_4: &cache_key_fallback v6-angular-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}
|
||||
|
||||
# Windows needs its own cache key because binaries in node_modules are different.
|
||||
var_3_win: &cache_key_win v5-angular-win-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "aio/yarn.lock" }}
|
||||
var_4_win: &cache_key_win_fallback v5-angular-win-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}
|
||||
var_3_win: &cache_key_win v6-angular-win-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "aio/yarn.lock" }}
|
||||
var_4_win: &cache_key_win_fallback v6-angular-win-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}
|
||||
|
||||
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
|
||||
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
|
||||
var_5: &components_repo_unit_tests_cache_key v1-angular-components-{{ checksum "month.txt" }}-db18b4093f23ae5c0ef76f6a9699c670ee5d55f7
|
||||
var_5: &components_repo_unit_tests_cache_key v1-angular-components-{{ checksum "month.txt" }}-83da73fb621720fbe801d3a503d50fd82b3da0c8
|
||||
var_6: &components_repo_unit_tests_cache_key_fallback v1-angular-components-{{ checksum "month.txt" }}
|
||||
|
||||
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages`.
|
||||
@@ -86,7 +86,17 @@ executors:
|
||||
type: string
|
||||
default: medium
|
||||
docker:
|
||||
- image: circleci/node:14.16.1@sha256:951e12268fe692615bfd155c327bd910e10c99db98404badd03879f984c32b31
|
||||
- image: cimg/node:14.17.6@sha256:589b5e494173bfb48923fa8d29b010b4d17079ac98d08de95dd4a78e57f5aa0b
|
||||
resource_class: << parameters.resource_class >>
|
||||
working_directory: ~/ng
|
||||
|
||||
test-browser-executor:
|
||||
parameters:
|
||||
resource_class:
|
||||
type: string
|
||||
default: medium
|
||||
docker:
|
||||
- image: cimg/node:14.17.6-browsers@sha256:aa6d08a04d13dd8aa18ae94be70a703c14dde0e99ea572e885e548f47f95eaa9
|
||||
resource_class: << parameters.resource_class >>
|
||||
working_directory: ~/ng
|
||||
|
||||
@@ -118,22 +128,6 @@ commands:
|
||||
- attach_workspace:
|
||||
at: *workspace_location
|
||||
|
||||
# Install shared libs used by Chrome that is either provisioned by
|
||||
# rules_webtesting or by puppeteer.
|
||||
install_chrome_libs:
|
||||
description: Install shared Chrome libs
|
||||
steps:
|
||||
- run:
|
||||
name: Install shared Chrome libs
|
||||
command: |
|
||||
sudo apt-get update
|
||||
# Install GTK+ graphical user interface (libgtk-3-0), advanced linux sound architecture (libasound2)
|
||||
# and network security service libraries (libnss3) & X11 Screen Saver extension library (libssx1)
|
||||
# which are dependencies of chrome & needed for karma & protractor headless chrome tests.
|
||||
# This is a very small install which takes around 7s in comparing to using the full
|
||||
# circleci/node:x.x.x-browsers image.
|
||||
sudo apt-get -y install libgtk-3-0 libasound2 libnss3 libxss1
|
||||
|
||||
# Install java runtime which is required by some integration tests such as
|
||||
# //integration:hello_world__closure_test, //integration:i18n_test and
|
||||
# //integration:ng_elements_test to run the closure compiler
|
||||
@@ -298,7 +292,7 @@ jobs:
|
||||
|
||||
test:
|
||||
executor:
|
||||
name: default-executor
|
||||
name: test-browser-executor
|
||||
# Now that large integration tests are running locally in parallel (they can't run on RBE yet
|
||||
# as they require network access for yarn install), this test is running out of memory
|
||||
# consistently with the xlarge machine.
|
||||
@@ -307,28 +301,13 @@ jobs:
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
- install_java
|
||||
- run:
|
||||
command: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
|
||||
# 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
|
||||
# measurements.
|
||||
command: yarn test --symlink_prefix=dist/ //...
|
||||
no_output_timeout: 20m
|
||||
|
||||
# Temporary job to test what will happen when we flip the Ivy flag to true
|
||||
test_ivy_aot:
|
||||
executor:
|
||||
name: default-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
# 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
|
||||
# measurements.
|
||||
- run:
|
||||
command: yarn test-ivy-aot //... --symlink_prefix=dist/
|
||||
no_output_timeout: 20m
|
||||
|
||||
# Publish bundle artifacts which will be used to calculate the size change. **Note**: Make
|
||||
# sure that the size plugin from the Angular robot fetches the artifacts from this CircleCI
|
||||
# job (see .github/angular-robot.yml). Additionally any artifacts need to be stored with the
|
||||
@@ -349,7 +328,7 @@ jobs:
|
||||
destination: core/todo/bundle.br
|
||||
|
||||
# NOTE: This is currently limited to master builds only. See the `monitoring` configuration.
|
||||
saucelabs_view_engine:
|
||||
saucelabs:
|
||||
executor:
|
||||
name: default-executor
|
||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
||||
@@ -361,47 +340,22 @@ jobs:
|
||||
- init_environment
|
||||
- init_saucelabs_environment
|
||||
- run:
|
||||
name: Run Bazel tests on Saucelabs with ViewEngine
|
||||
name: Run Bazel tests on Saucelabs
|
||||
# See /tools/saucelabs/README.md for more info
|
||||
command: |
|
||||
yarn bazel run //tools/saucelabs:sauce_service_setup
|
||||
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "ivy-only", ...) except attr("tags", "fixme-saucelabs-ve", ...)')
|
||||
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "view-engine-only", ...) except attr("tags", "fixme-saucelabs", ...)')
|
||||
yarn bazel test --config=saucelabs ${TESTS}
|
||||
yarn bazel run //tools/saucelabs:sauce_service_stop
|
||||
no_output_timeout: 40m
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
|
||||
# NOTE: This is currently limited to master builds only. See the `monitoring` configuration.
|
||||
saucelabs_ivy:
|
||||
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:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- init_saucelabs_environment
|
||||
- run:
|
||||
name: Run Bazel tests on Saucelabs with Ivy
|
||||
# See /tools/saucelabs/README.md for more info
|
||||
command: |
|
||||
yarn bazel run //tools/saucelabs:sauce_service_setup
|
||||
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "no-ivy-aot", ...) except attr("tags", "fixme-saucelabs-ivy", ...)')
|
||||
yarn bazel test --config=saucelabs --config=ivy ${TESTS}
|
||||
yarn bazel run //tools/saucelabs:sauce_service_stop
|
||||
no_output_timeout: 40m
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
|
||||
test_aio:
|
||||
executor: default-executor
|
||||
executor: test-browser-executor
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
# Build aio
|
||||
- run: yarn --cwd aio build --progress=false
|
||||
# Lint the code
|
||||
@@ -420,23 +374,19 @@ jobs:
|
||||
- run: yarn --cwd aio redirects-test
|
||||
|
||||
deploy_aio:
|
||||
executor: default-executor
|
||||
executor: test-browser-executor
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
# Deploy angular.io to production (if necessary)
|
||||
- run: setPublicVar_CI_STABLE_BRANCH
|
||||
- run: yarn --cwd aio deploy-production
|
||||
|
||||
test_aio_local:
|
||||
executor: default-executor
|
||||
environment:
|
||||
RXJS_VERSION_7: '7.4.0'
|
||||
executor: test-browser-executor
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
# Build aio (with local Angular packages)
|
||||
- run: yarn --cwd aio build-local-ci
|
||||
# Run unit tests
|
||||
@@ -447,17 +397,6 @@ jobs:
|
||||
- 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
|
||||
# Run tests with RxJS v7.
|
||||
- run: yarn --cwd aio add rxjs@$RXJS_VERSION_7 && yarn --cwd aio aio-use-local
|
||||
- run:
|
||||
name: Ensure we use RxJS v7
|
||||
command: >-
|
||||
node --eval "
|
||||
const expected = process.env.RXJS_VERSION_7;
|
||||
const actual = require('./aio/node_modules/rxjs/package.json').version;
|
||||
assert(actual === expected, 'Expected RxJS v' + expected + ', but found v' + actual + '.');
|
||||
"
|
||||
- run: yarn --cwd aio test --progress=false --watch=false
|
||||
|
||||
test_aio_tools:
|
||||
executor: default-executor
|
||||
@@ -473,33 +412,18 @@ jobs:
|
||||
|
||||
test_docs_examples:
|
||||
executor:
|
||||
name: default-executor
|
||||
name: test-browser-executor
|
||||
resource_class: xlarge
|
||||
parallelism: 5
|
||||
environment:
|
||||
RXJS_VERSION_7: '7.4.0'
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
# Install aio
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive --cache-folder ~/.cache/yarn
|
||||
# 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 --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL}
|
||||
# Run tests with RxJS v7.
|
||||
- run: yarn --cwd aio/tools/examples/shared add rxjs@$RXJS_VERSION_7 && yarn --cwd aio example-use-local && yarn --cwd aio boilerplate:add
|
||||
- run:
|
||||
name: Ensure we use RxJS v7
|
||||
command: >-
|
||||
node --eval "
|
||||
const expected = process.env.RXJS_VERSION_7;
|
||||
const actual = require('./aio/tools/examples/shared/node_modules/rxjs/package.json').version;
|
||||
assert(actual === expected, 'Expected RxJS v' + expected + ', but found v' + actual + '.');
|
||||
"
|
||||
# Exclude some examples that are not yet compatible with v7.
|
||||
- run: yarn --cwd aio example-e2e --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --exclude=practical-observable-usage --exclude=upgrade-module --exclude=upgrade-phonecat
|
||||
|
||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||
aio_preview:
|
||||
@@ -519,11 +443,10 @@ jobs:
|
||||
|
||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||
test_aio_preview:
|
||||
executor: default-executor
|
||||
executor: test-browser-executor
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive --cache-folder ~/.cache/yarn
|
||||
- run:
|
||||
name: Wait for preview and run tests
|
||||
@@ -535,8 +458,6 @@ jobs:
|
||||
# the bazel packages more than once. Even though we have a remote cache, these jobs will
|
||||
# typically run in parallel so up-to-date outputs will not be available at the time the build
|
||||
# starts.
|
||||
|
||||
# Build the view engine npm packages. No new jobs should depend on this.
|
||||
build-npm-packages:
|
||||
executor:
|
||||
name: default-executor
|
||||
@@ -631,11 +552,10 @@ jobs:
|
||||
command: yarn node aio/scripts/test-external-urls.js
|
||||
|
||||
aio_monitoring_stable:
|
||||
executor: default-executor
|
||||
executor: test-browser-executor
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
- run: setPublicVar_CI_STABLE_BRANCH
|
||||
- run:
|
||||
name: Check out `aio/` and yarn from the stable branch
|
||||
@@ -654,11 +574,10 @@ jobs:
|
||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
|
||||
aio_monitoring_next:
|
||||
executor: default-executor
|
||||
executor: test-browser-executor
|
||||
steps:
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- install_chrome_libs
|
||||
- run:
|
||||
name: Run tests against https://next.angular.io/
|
||||
command: ./aio/scripts/test-production.sh https://next.angular.io/ $CI_AIO_MIN_PWA_SCORE
|
||||
@@ -804,31 +723,13 @@ jobs:
|
||||
- setup_win
|
||||
- run:
|
||||
name: Build all windows CI targets
|
||||
command: bazel build --build_tag_filters=-ivy-only //packages/compiler-cli/...
|
||||
command: bazel build --build_tag_filters=-view-engine-only //packages/compiler-cli/...
|
||||
no_output_timeout: 15m
|
||||
- run:
|
||||
name: Test all windows CI targets
|
||||
command: bazel test --test_tag_filters="-ivy-only,-browser:chromium-local" //packages/compiler-cli/...
|
||||
command: bazel test --test_tag_filters="-view-engine-only,-browser:chromium-local" //packages/compiler-cli/...
|
||||
no_output_timeout: 15m
|
||||
|
||||
test_ivy_aot_win:
|
||||
executor: windows-executor
|
||||
steps:
|
||||
- setup_win
|
||||
- run:
|
||||
name: Build all windows CI targets
|
||||
command: bazel build --config=ivy --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot //packages/compiler-cli/...
|
||||
no_output_timeout: 15m
|
||||
- run:
|
||||
name: Test all windows CI targets
|
||||
command: bazel test --config=ivy --test_tag_filters="-no-ivy-aot,-fixme-ivy-aot,-browser:chromium-local" //packages/compiler-cli/... //packages/localize/...
|
||||
no_output_timeout: 15m
|
||||
# Save dependencies to use on subsequent runs.
|
||||
- save_cache:
|
||||
key: *cache_key_win
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
default_workflow:
|
||||
@@ -843,9 +744,6 @@ workflows:
|
||||
- test:
|
||||
requires:
|
||||
- setup
|
||||
- test_ivy_aot:
|
||||
requires:
|
||||
- setup
|
||||
- build-npm-packages:
|
||||
requires:
|
||||
- setup
|
||||
@@ -883,7 +781,6 @@ workflows:
|
||||
requires:
|
||||
# Only publish if tests and integration tests pass
|
||||
- test
|
||||
- test_ivy_aot
|
||||
# Only publish if `aio`/`docs` tests using the locally built Angular packages pass
|
||||
- test_aio_local
|
||||
- test_docs_examples
|
||||
@@ -891,20 +788,15 @@ workflows:
|
||||
# since the publishing script expects the legacy outputs layout.
|
||||
- build-npm-packages
|
||||
- legacy-unit-tests-saucelabs
|
||||
# TODO(devversion): re-enable once the Angular components repo has been updated to deal
|
||||
# with partial compilation APF v13 package output (i.e. with the linker processing).
|
||||
#- components-repo-unit-tests:
|
||||
# requires:
|
||||
# - build-npm-packages
|
||||
- components-repo-unit-tests:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- test_zonejs:
|
||||
requires:
|
||||
- setup
|
||||
- test_win:
|
||||
requires:
|
||||
- setup
|
||||
- test_ivy_aot_win:
|
||||
requires:
|
||||
- setup
|
||||
|
||||
monitoring:
|
||||
jobs:
|
||||
@@ -918,16 +810,7 @@ workflows:
|
||||
- aio_monitoring_next:
|
||||
requires:
|
||||
- setup
|
||||
- saucelabs_ivy:
|
||||
# Testing saucelabs via Bazel currently taking longer than the legacy saucelabs job as it
|
||||
# each karma_web_test target is provisioning and tearing down browsers which is adding
|
||||
# a lot of overhead. Running once daily on master only to avoid wasting resources and
|
||||
# slowing down CI for PRs.
|
||||
# TODO: Run this job on all branches (including PRs) once karma_web_test targets can
|
||||
# share provisioned browsers and we can remove the legacy saucelabs job.
|
||||
requires:
|
||||
- setup
|
||||
- saucelabs_view_engine:
|
||||
- saucelabs:
|
||||
# Testing saucelabs via Bazel currently taking longer than the legacy saucelabs job as it
|
||||
# each karma_web_test target is provisioning and tearing down browsers which is adding
|
||||
# a lot of overhead. Running once daily on master only to avoid wasting resources and
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo"
|
||||
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
|
||||
setPublicVar COMPONENTS_REPO_BRANCH "master"
|
||||
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
|
||||
setPublicVar COMPONENTS_REPO_COMMIT "db18b4093f23ae5c0ef76f6a9699c670ee5d55f7"
|
||||
setPublicVar COMPONENTS_REPO_COMMIT "83da73fb621720fbe801d3a503d50fd82b3da0c8"
|
||||
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -41,8 +41,8 @@ copy .circleci\bazel.windows.rc ${Env:USERPROFILE}\.bazelrc
|
||||
####################################################################################################
|
||||
# Install specific version of node.
|
||||
####################################################################################################
|
||||
nvm install 14.16.1
|
||||
nvm use 14.16.1
|
||||
nvm install 14.17.6
|
||||
nvm use 14.17.6
|
||||
|
||||
# These Bazel prereqs aren't needed because the CircleCI image already includes them.
|
||||
# choco install yarn --version 1.16.0 --no-progress
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Image metadata and config.
|
||||
# Ideally, the Node.js version should match what we use on CI.
|
||||
# See `executors > default-executor` in `.circleci/config.yml`.
|
||||
FROM circleci/node:14-browsers
|
||||
FROM cimg/node:14-browsers
|
||||
|
||||
|
||||
LABEL name="Angular dev environment" \
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
name: Security Issue Disclosure
|
||||
about: Report a security issue in Angular Framework, Material, or CLI
|
||||
---
|
||||
|
||||
Please read https://angular.io/guide/security#report-issues on how to disclose security related issues.
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
name: "Support Request"
|
||||
about: Questions and requests for support
|
||||
---
|
||||
|
||||
Please do not file questions or support requests on the GitHub issues tracker.
|
||||
|
||||
You can get your questions answered using other communication channels. Please see:
|
||||
https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||
|
||||
Thank you!
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
name: "Angular CLI"
|
||||
about: Issues and feature requests for Angular CLI
|
||||
---
|
||||
|
||||
Please file any Angular CLI issues at: https://github.com/angular/angular-cli/issues/new
|
||||
|
||||
For the time being, we keep Angular CLI issues in a separate repository.
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
name: "Angular Components"
|
||||
about: Issues and feature requests for Angular Components
|
||||
---
|
||||
|
||||
Please file any Angular Components issues at: https://github.com/angular/components/issues/new
|
||||
|
||||
For the time being, we keep Angular Components issues in a separate repository.
|
||||
@@ -0,0 +1,14 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Security issue disclosure
|
||||
url: https://angular.io/guide/security#report-issues
|
||||
about: Report a security issue in Angular Framework, CDK, Material, or CLI
|
||||
- name: Angular CLI
|
||||
url: https://github.com/angular/angular/issues/new/choose
|
||||
about: Issues and feature requests for Angular CLI
|
||||
- name: Angular Material and Components Development Kit (CDK)
|
||||
url: https://github.com/angular/components/issues/new/choose
|
||||
about: Issues and feature requests for Angular Material
|
||||
- name: Support Request
|
||||
url: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||
about: Questions and requests for support
|
||||
@@ -4,7 +4,7 @@
|
||||
size:
|
||||
disabled: false
|
||||
maxSizeIncrease: 2000
|
||||
circleCiStatusName: 'ci/circleci: test_ivy_aot'
|
||||
circleCiStatusName: 'ci/circleci: test'
|
||||
|
||||
# options for the merge plugin
|
||||
merge:
|
||||
|
||||
@@ -5,10 +5,10 @@ on:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
breaking-changes-label:
|
||||
labels:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: angular/dev-infra/github-actions/breaking-changes-label@861dc90572784e714aeaa9dfb20ceebeb57cdb07
|
||||
- uses: angular/dev-infra/github-actions/commit-message-based-labels@7b66fb5dc44b92fb4ecbd6a7d52b67a3e15339eb
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -10,6 +10,6 @@ jobs:
|
||||
if: github.repository == 'angular/angular'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/feature-request@f83903fe1ac848407ef81465f66588e5accb6537
|
||||
- uses: angular/dev-infra/github-actions/feature-request@7b66fb5dc44b92fb4ecbd6a7d52b67a3e15339eb
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -10,6 +10,6 @@ jobs:
|
||||
if: github.repository == 'angular/angular'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/lock-closed@7679cff885633cd33bf5ac7922a5304e8971a5a6
|
||||
- uses: angular/dev-infra/github-actions/lock-closed@7b66fb5dc44b92fb4ecbd6a7d52b67a3e15339eb
|
||||
with:
|
||||
lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }}
|
||||
|
||||
+8
-5
@@ -544,7 +544,7 @@ groups:
|
||||
'packages/compiler-cli/src/extract_i18n.ts',
|
||||
'packages/localize/**',
|
||||
'aio/content/guide/i18n-overview.md',
|
||||
'aio/content/guide/i18n-common.md',
|
||||
'aio/content/guide/i18n-common-overview.md',
|
||||
'aio/content/guide/i18n-common-add-package.md',
|
||||
'aio/content/guide/i18n-common-locale-id.md',
|
||||
'aio/content/guide/i18n-common-format-data-locale.md',
|
||||
@@ -553,8 +553,8 @@ groups:
|
||||
'aio/content/guide/i18n-common-merge.md',
|
||||
'aio/content/guide/i18n-common-deploy.md',
|
||||
'aio/content/guide/i18n-example.md',
|
||||
'aio/content/guide/i18n-optional.md',
|
||||
'aio/content/guide/i18n-optional-manual-source-locale.md',
|
||||
'aio/content/guide/i18n-optional-overview.md',
|
||||
'aio/content/guide/i18n-optional-manual-runtime-locale.md',
|
||||
'aio/content/guide/i18n-optional-import-global-variants.md',
|
||||
'aio/content/guide/i18n-optional-manage-marked-text.md',
|
||||
'aio/content/examples/i18n/**'
|
||||
@@ -983,6 +983,7 @@ groups:
|
||||
'aio/content/guide/updating.md',
|
||||
'aio/content/guide/deprecations.md',
|
||||
'aio/content/guide/migration-legacy-message-id.md',
|
||||
'aio/content/examples/deprecation-guide/**',
|
||||
'aio/content/guide/migration-renderer.md',
|
||||
'aio/content/guide/migration-undecorated-classes.md',
|
||||
'aio/content/guide/migration-dynamic-flag.md',
|
||||
@@ -990,7 +991,7 @@ groups:
|
||||
'aio/content/guide/migration-localize.md',
|
||||
'aio/content/guide/migration-module-with-providers.md',
|
||||
'aio/content/guide/static-query-migration.md',
|
||||
'aio/content/guide/updating-to-version-12.md',
|
||||
'aio/content/guide/update-to-latest-version.md',
|
||||
'aio/content/guide/ivy-compatibility.md',
|
||||
'aio/content/guide/ivy-compatibility-examples.md'
|
||||
])
|
||||
@@ -1090,7 +1091,8 @@ groups:
|
||||
contains_any_globs(files, [
|
||||
'aio/content/guide/creating-libraries.md',
|
||||
'aio/content/guide/libraries.md',
|
||||
'aio/content/guide/using-libraries.md'
|
||||
'aio/content/guide/using-libraries.md',
|
||||
'aio/content/guide/angular-package-format.md'
|
||||
])
|
||||
reviewers:
|
||||
users:
|
||||
@@ -1222,6 +1224,7 @@ groups:
|
||||
contains_any_globs(files.exclude("goldens/public-api/manage.js"), [
|
||||
'goldens/public-api/**',
|
||||
'docs/NAMING.md',
|
||||
'aio/content/guide/angular-package-format.md',
|
||||
'aio/content/errors/*.md',
|
||||
'aio/content/guide/glossary.md',
|
||||
'aio/content/guide/styleguide.md',
|
||||
|
||||
Vendored
+2
-2
@@ -12,9 +12,9 @@ To use the recommended configurations follow the steps below:
|
||||
- copy (or link) `.vscode/recommended-tasks.json` to `.vscode/tasks.json`
|
||||
- restart the editor
|
||||
|
||||
If you already have your custom workspace settings you should instead manually merge the file contents.
|
||||
If you already have your custom workspace settings, you should instead manually merge the file contents.
|
||||
|
||||
This isn't an automatic process so you will need to repeat it when settings are updated.
|
||||
This isn't an automatic process, so you will need to repeat it when settings are updated.
|
||||
|
||||
To see the recommended extensions select "Extensions: Show Recommended Extensions" in the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette).
|
||||
|
||||
|
||||
Vendored
-3
@@ -39,7 +39,6 @@
|
||||
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test/acceptance",
|
||||
"--config=debug"
|
||||
],
|
||||
@@ -57,7 +56,6 @@
|
||||
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test/render3",
|
||||
"--config=debug"
|
||||
],
|
||||
@@ -75,7 +73,6 @@
|
||||
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test",
|
||||
"--config=debug"
|
||||
],
|
||||
|
||||
Vendored
-4
@@ -9,7 +9,6 @@
|
||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test",
|
||||
"packages/core/test/acceptance",
|
||||
"packages/core/test/render3",
|
||||
@@ -42,7 +41,6 @@
|
||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test/acceptance",
|
||||
],
|
||||
"group": "test",
|
||||
@@ -71,7 +69,6 @@
|
||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test",
|
||||
],
|
||||
"group": "test",
|
||||
@@ -100,7 +97,6 @@
|
||||
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
|
||||
"args": [
|
||||
"test",
|
||||
"--config=ivy",
|
||||
"packages/core/test/render3",
|
||||
],
|
||||
"group": "test",
|
||||
|
||||
+411
-184
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -58,9 +58,9 @@ If you would like to *implement* a new feature, please consider the size of the
|
||||
|
||||
### <a name="submit-issue"></a> Submitting an Issue
|
||||
|
||||
Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
|
||||
Before you submit an issue, please search the issue tracker. An issue for your problem might already exist and the discussion might inform you of workarounds readily available.
|
||||
|
||||
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it.
|
||||
We want to fix all the issues as soon as possible, but before fixing a bug, we need to reproduce and confirm it.
|
||||
In order to reproduce bugs, we require that you provide a minimal reproduction.
|
||||
Having a minimal reproducible scenario gives us a wealth of important information without going back and forth to you with additional questions.
|
||||
|
||||
@@ -259,7 +259,7 @@ The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is opti
|
||||
Must be one of the following:
|
||||
|
||||
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
* **ci**: Changes to our CI configuration files and scripts (example scopes: Circle, BrowserStack, SauceLabs)
|
||||
* **ci**: Changes to our CI configuration files and scripts (examples: CircleCi, SauceLabs)
|
||||
* **docs**: Documentation only changes
|
||||
* **feat**: A new feature
|
||||
* **fix**: A bug fix
|
||||
|
||||
@@ -8,8 +8,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
# Fetch rules_nodejs so we can install our npm dependencies
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
sha256 = "3635797a96c7bfcd0d265dacd722a07335e64d6ded9834af8d3f1b7ba5a25bba",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/4.3.0/rules_nodejs-4.3.0.tar.gz"],
|
||||
sha256 = "4501158976b9da216295ac65d872b1be51e3eeb805273e68c516d2eb36ae1fbb",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/4.4.1/rules_nodejs-4.4.1.tar.gz"],
|
||||
)
|
||||
|
||||
# The PKG rules are needed to build tar packages for integration tests. The builtin
|
||||
@@ -32,7 +32,7 @@ check_rules_nodejs_version(minimum_version_string = "2.2.0")
|
||||
|
||||
# Setup the Node.js toolchain
|
||||
node_repositories(
|
||||
node_version = "14.16.1",
|
||||
node_version = "14.17.6",
|
||||
package_json = ["//:package.json"],
|
||||
)
|
||||
|
||||
@@ -71,13 +71,7 @@ load("//packages/common/locales/generate-locales-tool:cldr-data.bzl", "cldr_data
|
||||
|
||||
cldr_data_repository(
|
||||
name = "cldr_data",
|
||||
# Since we use the Github archives for CLDR 37, we need to specify a path
|
||||
# to the available locales. This wouldn't be needed with CLDR 39 as that
|
||||
# comes with an official JSON archive not containing a version suffix.
|
||||
available_locales_path = "cldr-core-37.0.0/availableLocales.json",
|
||||
urls = {
|
||||
"https://github.com/unicode-cldr/cldr-core/archive/37.0.0.zip": "32b5c49c3874aa342b90412c207b42e7aefb2435295891fb714c34ce58b3c706",
|
||||
"https://github.com/unicode-cldr/cldr-dates-full/archive/37.0.0.zip": "e1c410dd8ad7d75df4a5393efaf5d28f0d56c0fa126c5d66e171a3f21a988a1e",
|
||||
"https://github.com/unicode-cldr/cldr-numbers-full/archive/37.0.0.zip": "a921b90cf7f436e63fbdd55880f96e39a203acd9e174b0ceafa20a02c242a12e",
|
||||
"https://github.com/unicode-org/cldr-json/releases/download/39.0.0/cldr-39.0.0-json-full.zip": "a631764b6bb7967fab8cc351aff3ffa3f430a23646899976dd9d65801446def6",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -6,4 +6,4 @@ TODO (gkalpak): Add docs. Mention:
|
||||
Relevant files: `aio/aio-builds-setup/scripts/test.sh`
|
||||
- Deploying from CI.
|
||||
Relevant files: `.circleci/config.yml`, `scripts/ci/deploy.sh`, `aio/scripts/build-artifacts.sh`,
|
||||
`aio/scripts/deploy-to-firebase.js`
|
||||
`aio/scripts/deploy-to-firebase/index.mjs`
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
# Persistent disk cache
|
||||
Angular CLI saves a number of cachable operations on disk by default.
|
||||
|
||||
When you re-run the same build, the build system restores the state of the previous build and re-uses previously performed operations, which decreases the time taken to build and test your applications and libraries.
|
||||
|
||||
To amend the default cache settings, add the `cli.cache` object to your [Workspace Configuration](guide/workspace-config).
|
||||
The object goes under `cli.cache` at the top level of the file, outside the `projects` sections.
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"cli": {
|
||||
"cache": {
|
||||
...
|
||||
}
|
||||
},
|
||||
"projects": {}
|
||||
}
|
||||
```
|
||||
|
||||
For more information, see [cache options](guide/workspace-config#cache-options).
|
||||
|
||||
### Enabling and disabling the cache
|
||||
Caching is enabled by default. To disable caching run the following command:
|
||||
|
||||
```bash
|
||||
ng config cli.cache.enabled false
|
||||
```
|
||||
|
||||
To re-enable caching, set `cli.cache.enabled` to `true`.
|
||||
|
||||
### Cache environments
|
||||
By default, disk cache is only enabled for local environments.
|
||||
|
||||
To enable caching for all environments, run the following command:
|
||||
|
||||
```bash
|
||||
ng config cli.cache.environment all
|
||||
```
|
||||
|
||||
For more information, see `environment` in [cache options](guide/workspace-config#cache-options).
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The Angular CLI checks for the presence and value of the `CI` environment variable to determine in which environment it is running.
|
||||
|
||||
</div>
|
||||
|
||||
### Cache path
|
||||
|
||||
By default, `.angular/cache` is used as a base directory to store cache results. To change this path, run the following command:
|
||||
|
||||
```bash
|
||||
ng config cli.cache.path ".cache/ng"
|
||||
```
|
||||
|
||||
### Clearing the cache
|
||||
|
||||
To clear the cache, run one of the following commands.
|
||||
|
||||
To clear the cache on Unix-based operating systems:
|
||||
|
||||
```bash
|
||||
rm -rf .angular/cache
|
||||
```
|
||||
|
||||
To clear the cache on Windows:
|
||||
|
||||
```bash
|
||||
rmdir /s /q .angular/cache
|
||||
```
|
||||
|
||||
For more information, see [rm command](https://man7.org/linux/man-pages/man1/rm.1.html) and [rmdir command](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/rmdir).
|
||||
@@ -27,7 +27,7 @@ ng config --global cli.analyticsSharing.tracking UA-123456-12
|
||||
To turn off this feature, run the following command:
|
||||
|
||||
<code-example language="sh">
|
||||
ng config --global --remove cli.analyticsSharing
|
||||
ng config --global cli.analyticsSharing undefined
|
||||
</code-example>
|
||||
|
||||
## Per user tracking
|
||||
@@ -36,11 +36,11 @@ You can add a custom user ID to the global configuration, in order to identify u
|
||||
If that user enables CLI analytics for their own project, your analytics display tracks and labels their individual usage.
|
||||
|
||||
<code-example language="sh">
|
||||
ng config --global cli.analyticsSharing.user SOME_USER_NAME
|
||||
ng config --global cli.analyticsSharing.uuid SOME_USER_NAME
|
||||
</code-example>
|
||||
|
||||
To generate a new random user ID, run the following command:
|
||||
|
||||
<code-example language="sh">
|
||||
ng config --global cli.analyticsSharing.user ""
|
||||
ng config --global cli.analyticsSharing.uuid ""
|
||||
</code-example>
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
**/tsconfig.json
|
||||
**/tsconfig.app.json
|
||||
**/tsconfig.spec.json
|
||||
# TODO: remove tslint.json after linting has been removed from systemjs examples
|
||||
**/tslint.json
|
||||
**/karma-test-shim.js
|
||||
**/browser-test-shim.js
|
||||
**/node_modules
|
||||
@@ -104,3 +102,6 @@ upgrade-phonecat-3-final/rollup-config.js
|
||||
#angular-compiler-options
|
||||
!angular-compiler-options/tsconfig.json
|
||||
!angular-compiler-options/tsconfig.app.json
|
||||
|
||||
# deprecated-example
|
||||
!deprecation-guide/angular.package.json
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
"outDir": "./dist/out-tsc",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"sourceMap": true,
|
||||
@@ -20,7 +22,7 @@
|
||||
"target": "es2017",
|
||||
"module": "es2020",
|
||||
"lib": [
|
||||
"es2018",
|
||||
"es2020",
|
||||
"dom"
|
||||
]
|
||||
// #docregion angular-compiler-options
|
||||
|
||||
@@ -36,7 +36,7 @@ export class AppComponent {
|
||||
|
||||
// #docregion prepare-router-outlet
|
||||
prepareRoute(outlet: RouterOutlet) {
|
||||
return outlet?.activatedRouteData?.animation;
|
||||
return outlet?.activatedRouteData?.['animation'];
|
||||
}
|
||||
|
||||
// #enddocregion prepare-router-outlet
|
||||
|
||||
@@ -10,9 +10,9 @@ import { LoggerService } from './logger.service';
|
||||
})
|
||||
export class DateLoggerService extends LoggerService
|
||||
{
|
||||
logInfo(msg: any) { super.logInfo(stamp(msg)); }
|
||||
logDebug(msg: any) { super.logInfo(stamp(msg)); }
|
||||
logError(msg: any) { super.logError(stamp(msg)); }
|
||||
override logInfo(msg: any) { super.logInfo(stamp(msg)); }
|
||||
override logDebug(msg: any) { super.logInfo(stamp(msg)); }
|
||||
override logError(msg: any) { super.logError(stamp(msg)); }
|
||||
}
|
||||
|
||||
function stamp(msg: any) { return msg + ' at ' + new Date(); }
|
||||
|
||||
+1
-1
@@ -153,7 +153,7 @@ export class BethComponent implements Parent {
|
||||
export class AlexComponent extends Base
|
||||
// #enddocregion alex-class-signature
|
||||
{
|
||||
name = 'Alex';
|
||||
override name = 'Alex';
|
||||
}
|
||||
// #enddocregion alex-1
|
||||
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ export class SortedHeroesComponent extends HeroesBaseComponent {
|
||||
super(heroService);
|
||||
}
|
||||
|
||||
protected afterGetHeroes() {
|
||||
protected override afterGetHeroes() {
|
||||
this.heroes = this.heroes.sort((h1, h2) => {
|
||||
return h1.name < h2.name ? -1 :
|
||||
(h1.name > h2.name ? 1 : 0);
|
||||
|
||||
@@ -25,8 +25,8 @@ export function superCar() {
|
||||
}
|
||||
|
||||
/////////// example 3 //////////
|
||||
class MockEngine extends Engine { cylinders = 8; }
|
||||
class MockTires extends Tires { make = 'YokoGoodStone'; }
|
||||
class MockEngine extends Engine { override cylinders = 8; }
|
||||
class MockTires extends Tires { override make = 'YokoGoodStone'; }
|
||||
|
||||
export function testCar() {
|
||||
// Test car with 8 cylinders and YokoGoodStone tires.
|
||||
|
||||
@@ -75,7 +75,7 @@ export class Provider4Component {
|
||||
export class EvenBetterLogger extends Logger {
|
||||
constructor(private userService: UserService) { super(); }
|
||||
|
||||
log(message: string) {
|
||||
override log(message: string) {
|
||||
const name = this.userService.user.name;
|
||||
super.log(`Message to ${name}: ${message}`);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { AppPage } from './app.po';
|
||||
import { browser, logging } from 'protractor';
|
||||
|
||||
describe('workspace-project App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
});
|
||||
|
||||
// Add your e2e tests here
|
||||
|
||||
afterEach(async () => {
|
||||
// Assert that there are no errors emitted from the browser
|
||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||
expect(logs).not.toContain(jasmine.objectContaining({
|
||||
level: logging.Level.SEVERE,
|
||||
} as logging.Entry));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
<select>
|
||||
<!--#docregion two-way-template-deprecated-->
|
||||
<option *ngFor="let optionName of options" [(value)]="optionName"></option>
|
||||
<!--#enddocregion two-way-template-deprecated-->
|
||||
</select>
|
||||
@@ -0,0 +1,25 @@
|
||||
<h1>Deprecation</h1>
|
||||
|
||||
<!--#docregion deprecated-example-->
|
||||
|
||||
<input [formControl]="control" [(ngModel)]="value">
|
||||
|
||||
<!--#enddocregion deprecated-example-->
|
||||
|
||||
<!--#docregion reactive-form-example-->
|
||||
|
||||
<input [formControl]="control">
|
||||
|
||||
<!--#enddocregion reactive-form-example-->
|
||||
|
||||
<!--#docregion template-driven-form-example-->
|
||||
|
||||
<input [(ngModel)]="value">
|
||||
|
||||
<!--#enddocregion template-driven-form-example-->
|
||||
|
||||
<select>
|
||||
<!--#docregion valid-template-bind-->
|
||||
<option *ngFor="let optionName of options" [value]="optionName"></option>
|
||||
<!--#enddocregion valid-template-bind-->
|
||||
</select>
|
||||
@@ -0,0 +1,20 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
// Add your unit tests here
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
// #docplaster
|
||||
import { Component, ContentChild, Injector, Input, OnInit, StaticProvider, TemplateRef } from '@angular/core';
|
||||
import { ReflectiveInjector } from '@angular/core';
|
||||
import {
|
||||
FormControl,
|
||||
} from '@angular/forms';
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
title = 'example';
|
||||
value = 'initial value';
|
||||
control: FormControl = new FormControl('');
|
||||
options = [
|
||||
'repeat-value'
|
||||
];
|
||||
|
||||
/*
|
||||
// #docregion template-with-input-deprecated
|
||||
@Input() @ContentChild(TemplateRef) tpldeprecated !: TemplateRef<any>;
|
||||
// #enddocregion template-with-input-deprecated
|
||||
*/
|
||||
|
||||
// #docregion template-with-input
|
||||
@Input() tpl !: TemplateRef<any>;
|
||||
@ContentChild(TemplateRef) inlineTemplate !: TemplateRef<any>;
|
||||
// #enddocregion template-with-input
|
||||
|
||||
ngOnInit() {
|
||||
// #docregion deprecated-example, template-driven-form-example
|
||||
|
||||
this.value = 'some value';
|
||||
|
||||
// #enddocregion deprecated-example, template-driven-form-example
|
||||
this.setValue();
|
||||
}
|
||||
|
||||
setValue(): void {
|
||||
// #docregion reactive-form-example
|
||||
|
||||
this.control.setValue('some value');
|
||||
|
||||
// #enddocregion reactive-form-example
|
||||
}
|
||||
}
|
||||
|
||||
class InjectorExample {}
|
||||
|
||||
const Provider = [InjectorExample];
|
||||
const providers: StaticProvider[] = [Provider];
|
||||
|
||||
// #docregion reflective-injector-deprecated-example
|
||||
ReflectiveInjector.resolveAndCreate(providers);
|
||||
// #enddocregion reflective-injector-deprecated-example
|
||||
|
||||
// #docregion static-injector-example
|
||||
Injector.create({providers});
|
||||
// #enddocregion static-injector-example
|
||||
@@ -0,0 +1,75 @@
|
||||
// #docplaster
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { SubmitButtonComponent } from './submit-button/submit-button.component';
|
||||
|
||||
// #docregion lazyload-syntax, lazyload-deprecated-syntax
|
||||
const routes: Routes = [{
|
||||
path: 'lazy',
|
||||
// #enddocregion lazyload-deprecated-syntax
|
||||
// The new import() syntax
|
||||
loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)
|
||||
// #enddocregion lazyload-syntax
|
||||
/*
|
||||
// #docregion lazyload-deprecated-syntax
|
||||
// The following string syntax for loadChildren is deprecated
|
||||
loadChildren: './lazy/lazy.module#LazyModule',
|
||||
// #enddocregion lazyload-deprecated-syntax
|
||||
*/
|
||||
// #docregion lazyload-syntax, lazyload-deprecated-syntax
|
||||
}];
|
||||
// #enddocregion lazyload-syntax, lazyload-deprecated-syntax
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
SubmitButtonComponent
|
||||
],
|
||||
// #docregion reactive-form-no-warning
|
||||
imports: [
|
||||
// #enddocregion reactive-form-no-warning
|
||||
RouterModule.forChild(routes),
|
||||
FormsModule,
|
||||
BrowserModule,
|
||||
// #docregion reactive-form-no-warning
|
||||
ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'})
|
||||
],
|
||||
// #enddocregion reactive-form-no-warning
|
||||
providers: [],
|
||||
bootstrap: [AppComponent],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
||||
class SomeModule { }
|
||||
class SomeConfig { }
|
||||
|
||||
// #docplaster ...
|
||||
// #docregion ModuleWithProvidersGeneric, ModuleWithProvidersNonGeneric
|
||||
@NgModule({
|
||||
// #enddocregion ModuleWithProvidersGeneric, ModuleWithProvidersNonGeneric
|
||||
// #docregion ModuleWithProvidersGeneric, ModuleWithProvidersNonGeneric
|
||||
})
|
||||
// #docplaster
|
||||
export class MyModule {
|
||||
// #enddocregion ModuleWithProvidersGeneric, ModuleWithProvidersNonGeneric
|
||||
/*
|
||||
// #docregion ModuleWithProvidersNonGeneric
|
||||
static forRoot(config: SomeConfig): ModuleWithProviders {
|
||||
// #enddocregion ModuleWithProvidersNonGeneric
|
||||
*/
|
||||
// #docregion ModuleWithProvidersGeneric
|
||||
static forRoot(config: SomeConfig): ModuleWithProviders<SomeModule> {
|
||||
// #docregion ModuleWithProvidersNonGeneric
|
||||
return {
|
||||
ngModule: SomeModule,
|
||||
providers: [
|
||||
{provide: SomeConfig, useValue: config}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
// #enddocregion ModuleWithProvidersGeneric, ModuleWithProvidersNonGeneric
|
||||
@@ -0,0 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
const routes: Routes = [];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class LazyRoutingModule { }
|
||||
@@ -0,0 +1 @@
|
||||
<p>lazy works!</p>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LazyComponent } from './lazy.component';
|
||||
|
||||
describe('LazyComponent', () => {
|
||||
let component: LazyComponent;
|
||||
let fixture: ComponentFixture<LazyComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ LazyComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LazyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-lazy',
|
||||
templateUrl: './lazy.component.html',
|
||||
styleUrls: ['./lazy.component.css']
|
||||
})
|
||||
export class LazyComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { LazyRoutingModule } from './lazy-routing.module';
|
||||
import { LazyComponent } from './lazy.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
LazyComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
LazyRoutingModule
|
||||
]
|
||||
})
|
||||
export class LazyModule { }
|
||||
+1
@@ -0,0 +1 @@
|
||||
<p>submit-button works!</p>
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SubmitButtonComponent } from './submit-button.component';
|
||||
|
||||
describe('SubmitButtonComponent', () => {
|
||||
let component: SubmitButtonComponent;
|
||||
let fixture: ComponentFixture<SubmitButtonComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SubmitButtonComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SubmitButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// #docplaster ...
|
||||
import { Component, Input } from '@angular/core';
|
||||
// #docregion submitButton, submitButtonNarrow
|
||||
@Component({
|
||||
// #enddocregion submitButton, submitButtonNarrow
|
||||
// #docplaster
|
||||
selector: 'app-submit-button',
|
||||
templateUrl: './submit-button.component.html',
|
||||
styleUrls: ['./submit-button.component.css']
|
||||
// #docregion submitButton, submitButtonNarrow
|
||||
})
|
||||
export class SubmitButtonComponent {
|
||||
// #enddocregion submitButtonNarrow
|
||||
private disabledValue = false;
|
||||
|
||||
@Input()
|
||||
get disabled(): boolean {
|
||||
return this.disabledValue;
|
||||
}
|
||||
|
||||
set disabled(value: boolean|'') {
|
||||
this.disabledValue = (value === '') || value;
|
||||
}
|
||||
}
|
||||
// #enddocregion submitButton
|
||||
@Component({
|
||||
selector: 'app-submit-button-valid',
|
||||
templateUrl: './submit-button.component.html',
|
||||
styleUrls: ['./submit-button.component.css']
|
||||
})
|
||||
export class SubmitButtonValidComponent {
|
||||
|
||||
// tslint:disable: variable-name
|
||||
// #docregion submitButtonNarrow
|
||||
static ngAcceptInputType_disabled: boolean|'';
|
||||
|
||||
private disabledValue = false;
|
||||
|
||||
@Input()
|
||||
get disabled(): boolean {
|
||||
return this.disabledValue;
|
||||
}
|
||||
// #enddocregion submitButtonNarrow
|
||||
/*
|
||||
// #docregion submitButtonNarrow
|
||||
set disabled(value: boolean) {
|
||||
this.disabledValue = (value === '') || value;
|
||||
}
|
||||
// #enddocregion submitButtonNarrow
|
||||
*/
|
||||
// #docregion submitButtonNarrow
|
||||
}
|
||||
// #enddocregion submitButtonNarrow
|
||||
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Ponyracer</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"description": "Deprecation Guide",
|
||||
"files": [
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
"!**/*.[1,2].*"
|
||||
],
|
||||
"tags": [
|
||||
[
|
||||
"deprecation",
|
||||
"guide"
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// #docregion
|
||||
import { Component, Input, OnInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '@angular/core';
|
||||
import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
|
||||
import { AdDirective } from './ad.directive';
|
||||
import { AdItem } from './ad-item';
|
||||
@@ -18,15 +18,12 @@ import { AdComponent } from './ad.component';
|
||||
})
|
||||
// #docregion class
|
||||
export class AdBannerComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input() ads: AdItem[] = [];
|
||||
|
||||
currentAdIndex = -1;
|
||||
|
||||
@ViewChild(AdDirective, {static: true}) adHost!: AdDirective;
|
||||
interval: number | undefined;
|
||||
|
||||
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
|
||||
interval: number|undefined;
|
||||
|
||||
ngOnInit() {
|
||||
this.loadComponent();
|
||||
@@ -41,12 +38,10 @@ export class AdBannerComponent implements OnInit, OnDestroy {
|
||||
this.currentAdIndex = (this.currentAdIndex + 1) % this.ads.length;
|
||||
const adItem = this.ads[this.currentAdIndex];
|
||||
|
||||
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);
|
||||
|
||||
const viewContainerRef = this.adHost.viewContainerRef;
|
||||
viewContainerRef.clear();
|
||||
|
||||
const componentRef = viewContainerRef.createComponent<AdComponent>(componentFactory);
|
||||
const componentRef = viewContainerRef.createComponent<AdComponent>(adItem.component);
|
||||
componentRef.instance.data = adItem.data;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
import { QuestionBase } from './question-base';
|
||||
|
||||
export class DropdownQuestion extends QuestionBase<string> {
|
||||
controlType = 'dropdown';
|
||||
override controlType = 'dropdown';
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
import { QuestionBase } from './question-base';
|
||||
|
||||
export class TextboxQuestion extends QuestionBase<string> {
|
||||
controlType = 'textbox';
|
||||
override controlType = 'textbox';
|
||||
}
|
||||
|
||||
+7
-7
@@ -7,7 +7,7 @@
|
||||
|
||||
<div [hidden]="formDir.submitted">
|
||||
|
||||
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)">
|
||||
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.['identityRevealed'] && (heroForm.touched || heroForm.dirty)">
|
||||
<div class="form-group">
|
||||
|
||||
<label for="name">Name</label>
|
||||
@@ -18,13 +18,13 @@
|
||||
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
||||
class="alert alert-danger">
|
||||
|
||||
<div *ngIf="name.errors?.required">
|
||||
<div *ngIf="name.errors?.['required']">
|
||||
Name is required.
|
||||
</div>
|
||||
<div *ngIf="name.errors?.minlength">
|
||||
<div *ngIf="name.errors?.['minlength']">
|
||||
Name must be at least 4 characters long.
|
||||
</div>
|
||||
<div *ngIf="name.errors?.forbiddenName">
|
||||
<div *ngIf="name.errors?.['forbiddenName']">
|
||||
Name cannot be Bob.
|
||||
</div>
|
||||
</div>
|
||||
@@ -38,14 +38,14 @@
|
||||
|
||||
<div *ngIf="alterEgo.pending">Validating...</div>
|
||||
<div *ngIf="alterEgo.invalid" class="alert alert-danger alter-ego-errors">
|
||||
<div *ngIf="alterEgo.errors?.uniqueAlterEgo">
|
||||
<div *ngIf="alterEgo.errors?.['uniqueAlterEgo']">
|
||||
Alter ego is already taken.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- #docregion cross-validation-error-message -->
|
||||
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert alert-danger">
|
||||
<div *ngIf="heroForm.errors?.['identityRevealed'] && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert alert-danger">
|
||||
Name cannot match alter ego.
|
||||
</div>
|
||||
<!-- #enddocregion cross-validation-error-message -->
|
||||
@@ -59,7 +59,7 @@
|
||||
</select>
|
||||
|
||||
<div *ngIf="power.invalid && power.touched" class="alert alert-danger">
|
||||
<div *ngIf="power.errors?.required">Power is required.</div>
|
||||
<div *ngIf="power.errors?.['required']">Power is required.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
+7
-7
@@ -6,7 +6,7 @@
|
||||
<form #heroForm="ngForm" appIdentityRevealed>
|
||||
<!-- #enddocregion cross-validation-register-validator -->
|
||||
<div [hidden]="heroForm.submitted">
|
||||
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)">
|
||||
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.['identityRevealed'] && (heroForm.touched || heroForm.dirty)">
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
@@ -19,13 +19,13 @@
|
||||
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
||||
class="alert">
|
||||
|
||||
<div *ngIf="name.errors?.required">
|
||||
<div *ngIf="name.errors?.['required']">
|
||||
Name is required.
|
||||
</div>
|
||||
<div *ngIf="name.errors?.minlength">
|
||||
<div *ngIf="name.errors?.['minlength']">
|
||||
Name must be at least 4 characters long.
|
||||
</div>
|
||||
<div *ngIf="name.errors?.forbiddenName">
|
||||
<div *ngIf="name.errors?.['forbiddenName']">
|
||||
Name cannot be Bob.
|
||||
</div>
|
||||
|
||||
@@ -45,14 +45,14 @@
|
||||
|
||||
<div *ngIf="alterEgo.pending">Validating...</div>
|
||||
<div *ngIf="alterEgo.invalid" class="alert alter-ego-errors">
|
||||
<div *ngIf="alterEgo.errors?.uniqueAlterEgo">
|
||||
<div *ngIf="alterEgo.errors?.['uniqueAlterEgo']">
|
||||
Alter ego is already taken.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- #docregion cross-validation-error-message -->
|
||||
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert">
|
||||
<div *ngIf="heroForm.errors?.['identityRevealed'] && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert">
|
||||
Name cannot match alter ego.
|
||||
</div>
|
||||
<!-- #enddocregion cross-validation-error-message -->
|
||||
@@ -68,7 +68,7 @@
|
||||
</select>
|
||||
|
||||
<div *ngIf="power.errors && power.touched" class="alert">
|
||||
<div *ngIf="power.errors.required">Power is required.</div>
|
||||
<div *ngIf="power.errors['required']">Power is required.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,13 +14,15 @@ import { ProductListComponent } from './product-list/product-list.component';
|
||||
import { ProductAlertsComponent } from './product-alerts/product-alerts.component';
|
||||
// #enddocregion declare-product-alerts
|
||||
import { ProductDetailsComponent } from './product-details/product-details.component';
|
||||
// #docregion declare-cart
|
||||
import { CartComponent } from './cart/cart.component';
|
||||
// #enddocregion declare-cart
|
||||
import { ShippingComponent } from './shipping/shipping.component';
|
||||
|
||||
// #docregion product-details-route, http-client-module, shipping-route, cart-route, declare-product-alerts
|
||||
// #docregion product-details-route, http-client-module, shipping-route, cart-route, declare-product-alerts, declare-cart
|
||||
|
||||
@NgModule({
|
||||
// #enddocregion declare-product-alerts
|
||||
// #enddocregion declare-product-alerts, declare-cart
|
||||
imports: [
|
||||
BrowserModule,
|
||||
// #enddocregion product-details-route, cart-route
|
||||
@@ -39,7 +41,7 @@ import { ShippingComponent } from './shipping/shipping.component';
|
||||
])
|
||||
],
|
||||
// #enddocregion cart-route
|
||||
// #docregion declare-product-alerts
|
||||
// #docregion declare-product-alerts, declare-cart
|
||||
declarations: [
|
||||
AppComponent,
|
||||
TopBarComponent,
|
||||
@@ -49,11 +51,12 @@ import { ShippingComponent } from './shipping/shipping.component';
|
||||
ProductDetailsComponent,
|
||||
// #enddocregion product-details-route
|
||||
CartComponent,
|
||||
// #enddocregion declare-cart
|
||||
// #enddocregion http-client-module
|
||||
ShippingComponent
|
||||
// #docregion declare-product-alerts, http-client-module, product-details-route
|
||||
// #docregion declare-product-alerts, http-client-module, product-details-route, declare-cart
|
||||
],
|
||||
// #enddocregion declare-product-alerts, product-details-route
|
||||
// #enddocregion declare-product-alerts, product-details-route, declare-cart
|
||||
bootstrap: [
|
||||
AppComponent
|
||||
]
|
||||
|
||||
@@ -56,6 +56,7 @@ export class TiresService {
|
||||
})
|
||||
export class CarService {
|
||||
id = 'C1';
|
||||
|
||||
constructor(
|
||||
protected engineService: EngineService,
|
||||
protected tiresService: TiresService) { }
|
||||
@@ -75,13 +76,15 @@ export class CarService {
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CarService2 extends CarService {
|
||||
id = 'C2';
|
||||
override id = 'C2';
|
||||
|
||||
constructor(
|
||||
protected engineService: EngineService,
|
||||
protected tiresService: TiresService) {
|
||||
protected override engineService: EngineService,
|
||||
protected override tiresService: TiresService) {
|
||||
super(engineService, tiresService);
|
||||
}
|
||||
getCar() {
|
||||
|
||||
override getCar() {
|
||||
const car = super.getCar();
|
||||
car.name = 'BamBam Motors, BroVan 2000';
|
||||
return car;
|
||||
@@ -92,13 +95,15 @@ export class CarService2 extends CarService {
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CarService3 extends CarService2 {
|
||||
id = 'C3';
|
||||
override id = 'C3';
|
||||
|
||||
constructor(
|
||||
protected engineService: EngineService,
|
||||
protected tiresService: TiresService) {
|
||||
protected override engineService: EngineService,
|
||||
protected override tiresService: TiresService) {
|
||||
super(engineService, tiresService);
|
||||
}
|
||||
getCar() {
|
||||
|
||||
override getCar() {
|
||||
const car = super.getCar();
|
||||
car.name = 'Chizzamm Motors, Calico UltraMax Supreme';
|
||||
return car;
|
||||
|
||||
@@ -30,7 +30,7 @@ export class PackageSearchService {
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
httpErrorHandler: HttpErrorHandler) {
|
||||
this.handleError = httpErrorHandler.createHandleError('HeroesService');
|
||||
this.handleError = httpErrorHandler.createHandleError('PackageSearchService');
|
||||
}
|
||||
|
||||
search(packageName: string, refresh = false): Observable<NpmPackageInfo[]> {
|
||||
|
||||
@@ -142,19 +142,6 @@
|
||||
"scripts": []
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.spec.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
|
||||
@@ -28,7 +28,7 @@ export class MyCounterComponent implements OnChanges {
|
||||
}
|
||||
|
||||
// A change to `counter` is the only change we care about
|
||||
const chng = changes.counter;
|
||||
const chng = changes['counter'];
|
||||
const cur = chng.currentValue;
|
||||
const prev = JSON.stringify(chng.previousValue); // first time is {}; after is integer
|
||||
this.changeLog.push(`counter: currentValue = ${cur}, previousValue = ${prev}`);
|
||||
|
||||
@@ -44,7 +44,7 @@ export class PeekABooComponent extends PeekABooDirective implements
|
||||
const changesMsgs: string[] = [];
|
||||
for (const propName in changes) {
|
||||
if (propName === 'name') {
|
||||
const name = changes.name.currentValue;
|
||||
const name = changes['name'].currentValue;
|
||||
changesMsgs.push(`name ${this.verb} to "${name}"`);
|
||||
} else {
|
||||
changesMsgs.push(propName + ' ' + this.verb);
|
||||
|
||||
@@ -14,12 +14,12 @@ describe('observables', () => {
|
||||
});
|
||||
|
||||
it('should listen to input changes', () => {
|
||||
let triggerInputChange!: (e: {keyCode: number}) => void;
|
||||
let triggerInputChange!: (e: {code: string}) => void;
|
||||
const input = {
|
||||
value: 'Test',
|
||||
addEventListener: jasmine
|
||||
.createSpy('addEvent')
|
||||
.and.callFake((eventName: string, cb: (e: {keyCode: number}) => void) => {
|
||||
.and.callFake((eventName: string, cb: (e: {code: string}) => void) => {
|
||||
if (eventName === 'keydown') {
|
||||
triggerInputChange = cb;
|
||||
}
|
||||
@@ -29,10 +29,10 @@ describe('observables', () => {
|
||||
|
||||
const document = { getElementById: () => input } as unknown as Document;
|
||||
docRegionFromEvent(document);
|
||||
triggerInputChange({keyCode: 65});
|
||||
triggerInputChange({code: 'A'});
|
||||
expect(input.value).toBe('Test');
|
||||
|
||||
triggerInputChange({keyCode: 27});
|
||||
triggerInputChange({code: 'Escape'});
|
||||
expect(input.value).toBe('');
|
||||
});
|
||||
|
||||
|
||||
@@ -55,11 +55,11 @@ function fromEvent<T extends keyof HTMLElementEventMap>(target: HTMLElement, eve
|
||||
export function docRegionFromEvent(document: Document) {
|
||||
// #docregion fromevent_use
|
||||
|
||||
const ESC_KEY = 27;
|
||||
const ESC_CODE = 'Escape';
|
||||
const nameInput = document.getElementById('name') as HTMLInputElement;
|
||||
|
||||
const subscription = fromEvent(nameInput, 'keydown').subscribe((e: KeyboardEvent) => {
|
||||
if (e.keyCode === ESC_KEY) {
|
||||
if (e.code === ESC_CODE) {
|
||||
nameInput.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -51,5 +51,5 @@ export class FlyingHeroesComponent {
|
||||
styles: ['#flyers, #all {font-style: italic}', 'button {display: block}', 'input {margin: .25rem .25rem .5rem 0;}'],
|
||||
})
|
||||
export class FlyingHeroesImpureComponent extends FlyingHeroesComponent {
|
||||
title = 'Flying Heroes (impure pipe)';
|
||||
override title = 'Flying Heroes (impure pipe)';
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { Observable, of } from 'rxjs';
|
||||
/// <reference types="node" />
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { docRegionTypeahead } from './typeahead';
|
||||
|
||||
describe('typeahead', () => {
|
||||
let document: Document;
|
||||
let ajax: jasmine.Spy;
|
||||
let tick: MockClock['tick'];
|
||||
let triggertInputChange: (e: { target: { value: string } }) => void;
|
||||
|
||||
beforeEach(() => {
|
||||
jasmine.clock().install();
|
||||
const input = {
|
||||
addEventListener: jasmine
|
||||
.createSpy('addEvent')
|
||||
@@ -21,40 +23,37 @@ describe('typeahead', () => {
|
||||
|
||||
document = { getElementById: (id: string) => input } as unknown as Document;
|
||||
ajax = jasmine.createSpy('ajax').and.callFake((url: string) => of('foo bar'));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jasmine.clock().uninstall();
|
||||
tick = MockClock.install();
|
||||
});
|
||||
|
||||
it('should make an ajax call to the corrent endpoint', () => {
|
||||
docRegionTypeahead(document, ajax);
|
||||
triggertInputChange({ target: { value: 'foo' } });
|
||||
jasmine.clock().tick(11);
|
||||
tick(11);
|
||||
expect(ajax).toHaveBeenCalledWith('/api/endpoint?search=foo');
|
||||
});
|
||||
|
||||
it('should not make an ajax call, when the input length < 3', () => {
|
||||
docRegionTypeahead(document, ajax);
|
||||
triggertInputChange({ target: { value: '' } });
|
||||
jasmine.clock().tick(11);
|
||||
tick(11);
|
||||
expect(ajax).not.toHaveBeenCalled();
|
||||
triggertInputChange({ target: { value: 'fo' } });
|
||||
jasmine.clock().tick(11);
|
||||
tick(11);
|
||||
expect(ajax).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not make an ajax call for intermediate values when debouncing', () => {
|
||||
docRegionTypeahead(document, ajax);
|
||||
triggertInputChange({ target: { value: 'foo' } });
|
||||
jasmine.clock().tick(9);
|
||||
tick(9);
|
||||
triggertInputChange({ target: { value: 'bar' } });
|
||||
jasmine.clock().tick(9);
|
||||
tick(9);
|
||||
triggertInputChange({ target: { value: 'baz' } });
|
||||
jasmine.clock().tick(9);
|
||||
tick(9);
|
||||
triggertInputChange({ target: { value: 'qux' } });
|
||||
expect(ajax).not.toHaveBeenCalled();
|
||||
jasmine.clock().tick(10);
|
||||
tick(10);
|
||||
expect(ajax).toHaveBeenCalledTimes(1);
|
||||
expect(ajax).toHaveBeenCalledWith('/api/endpoint?search=qux');
|
||||
});
|
||||
@@ -62,11 +61,94 @@ describe('typeahead', () => {
|
||||
it('should not make an ajax call, when the input value has not changed', () => {
|
||||
docRegionTypeahead(document, ajax);
|
||||
triggertInputChange({ target: { value: 'foo' } });
|
||||
jasmine.clock().tick(11);
|
||||
tick(11);
|
||||
expect(ajax).toHaveBeenCalled();
|
||||
ajax.calls.reset();
|
||||
triggertInputChange({ target: { value: 'foo' } });
|
||||
jasmine.clock().tick(11);
|
||||
tick(11);
|
||||
expect(ajax).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Helpers
|
||||
interface MockTask {
|
||||
id: NodeJS.Timeout;
|
||||
fn: () => unknown;
|
||||
delay: number;
|
||||
recurring: boolean;
|
||||
nextTriggerTime: number;
|
||||
}
|
||||
|
||||
class MockClock {
|
||||
private tasks: MockTask[] = [];
|
||||
|
||||
private constructor(private now: number) {}
|
||||
|
||||
static install(mockTime = 0): MockClock['tick'] {
|
||||
const mocked = new this(mockTime);
|
||||
|
||||
spyOn(global, 'clearInterval').and.callFake(id => mocked.clearTask(id));
|
||||
spyOn(global, 'clearTimeout').and.callFake(id => mocked.clearTask(id));
|
||||
spyOn(global, 'setInterval').and.callFake(
|
||||
(fn, delay, ...args) => mocked.createTask(fn, delay, true, ...args));
|
||||
spyOn(global, 'setTimeout').and.callFake(
|
||||
(fn, delay, ...args) => mocked.createTask(fn, delay, false, ...args));
|
||||
|
||||
spyOn(Date, 'now').and.callFake(() => mocked.now);
|
||||
|
||||
return mocked.tick.bind(mocked);
|
||||
}
|
||||
|
||||
private clearTask(id: MockTask['id']): void {
|
||||
this.tasks = this.tasks.filter(task => task.id !== id);
|
||||
}
|
||||
|
||||
private createTask(
|
||||
fn: MockTask['fn'], delay: MockTask['delay'], recurring: MockTask['recurring'],
|
||||
...args: any[]): MockTask['id'] {
|
||||
// Avoid infinite loops.
|
||||
if (recurring && (delay <= 0)) {
|
||||
delay = 1;
|
||||
}
|
||||
|
||||
const task: MockTask = {
|
||||
id: {} as MockTask['id'],
|
||||
fn: fn.bind<null, unknown, unknown>(null, ...args),
|
||||
delay,
|
||||
recurring,
|
||||
nextTriggerTime: this.now + delay,
|
||||
};
|
||||
this.queueTask(task);
|
||||
|
||||
return task.id;
|
||||
}
|
||||
|
||||
private queueTask(task: MockTask): void {
|
||||
const firstLaterTaskIdx = this.tasks.findIndex(
|
||||
otherTask => otherTask.nextTriggerTime > task.nextTriggerTime);
|
||||
const newTaskIdx = (firstLaterTaskIdx === -1) ? this.tasks.length : firstLaterTaskIdx;
|
||||
|
||||
this.tasks.splice(newTaskIdx, 0, task);
|
||||
}
|
||||
|
||||
private tick(millis: number): void {
|
||||
const finalNow = this.now + millis;
|
||||
|
||||
while (this.tasks[0]?.nextTriggerTime <= finalNow) {
|
||||
const task = this.tasks.shift()!;
|
||||
this.now = task.nextTriggerTime;
|
||||
|
||||
if (task.recurring) {
|
||||
this.queueTask({...task, nextTriggerTime: this.now + task.delay});
|
||||
}
|
||||
|
||||
try {
|
||||
task.fn();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
this.now = finalNow;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -127,8 +127,8 @@ describe('Reactive forms', () => {
|
||||
const formValue = await formValueElement.getText();
|
||||
const formJson = JSON.parse(formValue.toString().replace('Form Value:', ''));
|
||||
|
||||
expect(profile.firstName).toBe(formJson.firstName);
|
||||
expect(profile.lastName).toBe(formJson.lastName);
|
||||
expect(profile['firstName']).toBe(formJson.firstName);
|
||||
expect(profile['lastName']).toBe(formJson.lastName);
|
||||
expect(formJson.aliases[0]).toBe(aliasText);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ import { slideInAnimation } from './animations';
|
||||
// #docregion function-binding
|
||||
export class AppComponent {
|
||||
getAnimationData(outlet: RouterOutlet) {
|
||||
return outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation;
|
||||
return outlet?.activatedRouteData?.['animation'];
|
||||
}
|
||||
}
|
||||
// #enddocregion function-binding
|
||||
|
||||
@@ -12,6 +12,6 @@ import { slideInAnimation } from './animations';
|
||||
})
|
||||
export class AppComponent {
|
||||
getAnimationData(outlet: RouterOutlet) {
|
||||
return outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation;
|
||||
return outlet?.activatedRouteData?.['animation'];
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ export class CrisisDetailComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
this.route.data
|
||||
.subscribe(data => {
|
||||
const crisis: Crisis = data.crisis;
|
||||
const crisis: Crisis = data['crisis'];
|
||||
this.editName = crisis.name;
|
||||
this.crisis = crisis;
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ export class SelectivePreloadingStrategyService implements PreloadingStrategy {
|
||||
preloadedModules: string[] = [];
|
||||
|
||||
preload(route: Route, load: () => Observable<any>): Observable<any> {
|
||||
if (route.data && route.data.preload && route.path != null) {
|
||||
if (route.data?.['preload'] && route.path != null) {
|
||||
// add the route path to the preloaded module array
|
||||
this.preloadedModules.push(route.path);
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
<h2>Reference variables, forms, and NgForm</h2>
|
||||
<!-- #docregion ngForm -->
|
||||
<form #itemForm="ngForm" (ngSubmit)="onSubmit(itemForm)">
|
||||
<label for="name">Name <input class="form-control" name="name" ngModel required />
|
||||
</label>
|
||||
<label for="name">Name</label>
|
||||
<input type="text" id="name" class="form-control" name="name" ngModel required />
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -58,10 +58,10 @@ export class HeroDetailComponent {
|
||||
})
|
||||
export class BigHeroDetailComponent extends HeroDetailComponent {
|
||||
|
||||
@Input() hero!: Hero;
|
||||
@Output() deleteRequest = new EventEmitter<Hero>();
|
||||
@Input() override hero!: Hero;
|
||||
@Output() override deleteRequest = new EventEmitter<Hero>();
|
||||
|
||||
delete() {
|
||||
override delete() {
|
||||
this.deleteRequest.emit(this.hero);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,15 @@
|
||||
<!-- #docregion -->
|
||||
<form (ngSubmit)="onSubmit(heroForm)" #heroForm="ngForm">
|
||||
<div class="form-group">
|
||||
<label for="name">Name
|
||||
<input class="form-control" name="name" required [(ngModel)]="hero.name">
|
||||
</label>
|
||||
<label for="name">Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
class="form-control"
|
||||
name="name"
|
||||
required
|
||||
[(ngModel)]="hero.name"
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" [disabled]="!heroForm.form.valid">Submit</button>
|
||||
</form>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
|
||||
///////// Fakes /////////
|
||||
export class FakeValueService extends ValueService {
|
||||
value = 'faked service value';
|
||||
override value = 'faked service value';
|
||||
}
|
||||
////////////////////////
|
||||
describe('demo (no TestBed):', () => {
|
||||
|
||||
@@ -372,14 +372,14 @@ describe('demo (with TestBed):', () => {
|
||||
|
||||
expect(el.context).toBe(childComp, 'context is the child component');
|
||||
|
||||
expect(el.attributes.account).toBe(childComp.id, 'account attribute');
|
||||
expect(el.attributes.bank).toBe(childComp.bank, 'bank attribute');
|
||||
expect(el.attributes['account']).toBe(childComp.id, 'account attribute');
|
||||
expect(el.attributes['bank']).toBe(childComp.bank, 'bank attribute');
|
||||
|
||||
expect(el.classes.closed).toBe(true, 'closed class');
|
||||
expect(el.classes.open).toBeFalsy('open class');
|
||||
expect(el.classes['closed']).toBe(true, 'closed class');
|
||||
expect(el.classes['open']).toBeFalsy('open class');
|
||||
|
||||
expect(el.styles.color).toBe(comp.color, 'color style');
|
||||
expect(el.styles.width).toBe(comp.width + 'px', 'width style');
|
||||
expect(el.styles['color']).toBe(comp.color, 'color style');
|
||||
expect(el.styles['width']).toBe(comp.width + 'px', 'width style');
|
||||
|
||||
// Removed on 12/02/2016 when ceased public discussion of the `Renderer`. Revive in future?
|
||||
// expect(el.properties['customProperty']).toBe(true, 'customProperty');
|
||||
@@ -510,11 +510,11 @@ describe('demo (with TestBed):', () => {
|
||||
expect(comp.children.toArray().length).toBe(4,
|
||||
'three different child components and an ElementRef with #content');
|
||||
|
||||
expect(el.references.nc).toBe(comp, '#nc reference to component');
|
||||
expect(el.references['nc']).toBe(comp, '#nc reference to component');
|
||||
|
||||
// #docregion custom-predicate
|
||||
// Filter for DebugElements with a #content reference
|
||||
const contentRefs = el.queryAll( de => de.references.content);
|
||||
const contentRefs = el.queryAll( de => de.references['content']);
|
||||
// #enddocregion custom-predicate
|
||||
expect(contentRefs.length).toBe(4, 'elements w/ a #content reference');
|
||||
});
|
||||
@@ -698,5 +698,5 @@ class FakeGrandchildComponent { }
|
||||
|
||||
@Injectable()
|
||||
class FakeValueService extends ValueService {
|
||||
value = 'faked value';
|
||||
override value = 'faked value';
|
||||
}
|
||||
|
||||
@@ -30,19 +30,19 @@ export class TestHeroService extends HeroService {
|
||||
heroes = getTestHeroes();
|
||||
lastResult!: Observable<any>; // result from last method call
|
||||
|
||||
addHero(hero: Hero): Observable<Hero> {
|
||||
override addHero(hero: Hero): Observable<Hero> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
deleteHero(hero: number | Hero): Observable<Hero> {
|
||||
override deleteHero(hero: number | Hero): Observable<Hero> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getHeroes(): Observable<Hero[]> {
|
||||
override getHeroes(): Observable<Hero[]> {
|
||||
return this.lastResult = asyncData(this.heroes);
|
||||
}
|
||||
|
||||
getHero(id: number | string): Observable<Hero> {
|
||||
override getHero(id: number | string): Observable<Hero> {
|
||||
if (typeof id === 'string') {
|
||||
id = parseInt(id, 10);
|
||||
}
|
||||
@@ -51,7 +51,7 @@ export class TestHeroService extends HeroService {
|
||||
return this.lastResult;
|
||||
}
|
||||
|
||||
updateHero(hero: Hero): Observable<Hero> {
|
||||
override updateHero(hero: Hero): Observable<Hero> {
|
||||
return this.lastResult = this.getHero(hero.id).pipe(
|
||||
map(h => {
|
||||
if (h) {
|
||||
|
||||
@@ -71,7 +71,7 @@ describe('HighlightDirective', () => {
|
||||
|
||||
|
||||
it('bare <h2> should not have a customProperty', () => {
|
||||
expect(bareH2.properties.customProperty).toBeUndefined();
|
||||
expect(bareH2.properties['customProperty']).toBeUndefined();
|
||||
});
|
||||
// #enddocregion selected-tests
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// this tsconfig is used to give intellisense to
|
||||
// all the examples in this folder
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["es2015", "dom"],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"../../tools/examples/shared/node_modules/@types"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"*/e2e-spec.ts"
|
||||
]
|
||||
}
|
||||
@@ -58,6 +58,7 @@
|
||||
],
|
||||
"no-non-null-assertion": false,
|
||||
"no-redundant-jsdoc": true,
|
||||
"no-string-literal": false,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-var-requires": false,
|
||||
"object-literal-key-quotes": [
|
||||
|
||||
@@ -64,7 +64,7 @@ When `true`, the compiler does not check the TypeScript version and does not rep
|
||||
### `enableI18nLegacyMessageIdFormat`
|
||||
|
||||
Instructs the Angular template compiler to generate legacy ids for messages that are tagged in templates by the `i18n` attribute.
|
||||
See [Mark text for translations][AioGuideI18nCommonPrepareMarkTextForTranslations] for more information about marking messages for localization.
|
||||
See [Mark text for translations][AioGuideI18nCommonPrepareMarkTextInComponentTemplate] for more information about marking messages for localization.
|
||||
|
||||
Set this option to `false` unless your project relies upon translations that were previously generated using legacy IDs. Default is `true`.
|
||||
|
||||
@@ -74,12 +74,6 @@ These message formats have a number of issues, such as whitespace handling and r
|
||||
The new message format is more resilient to whitespace changes, is the same across all translation file formats, and can be generated directly from calls to `$localize`.
|
||||
This allows `$localize` messages in application code to use the same ID as identical `i18n` messages in component templates.
|
||||
|
||||
### `enableIvy`
|
||||
|
||||
Enables the [Ivy](guide/ivy) compilation and rendering pipeline. Default is `true`, as of version 9. In version 9, you can [opt out of Ivy](guide/ivy#opting-out-of-angular-ivy) to continue using the previous compiler, View Engine.
|
||||
|
||||
For library projects generated with the CLI, the production configuration default is `false` in version 9.
|
||||
|
||||
### `enableResourceInlining`
|
||||
|
||||
When `true`, replaces the `templateUrl` and `styleUrls` property in all `@Component` decorators with inlined contents in `template` and `styles` properties.
|
||||
@@ -180,7 +174,7 @@ When you use the CLI command `ng new --strict`, it is set to `true` in the gener
|
||||
|
||||
### `strictTemplates`
|
||||
|
||||
When `true`, enables [strict template type checking](guide/template-typecheck#strict-mode). Strict mode is only available when using [Ivy](guide/ivy) (Angular version 9 and later).
|
||||
When `true`, enables [strict template type checking](guide/template-typecheck#strict-mode).
|
||||
|
||||
Additional strictness flags allow you to enable and disable specific types of strict template type checking. See [troubleshooting template errors](guide/template-typecheck#troubleshooting-template-errors).
|
||||
|
||||
@@ -203,8 +197,8 @@ In addition to the configuration file, you can also use [`tsc` command line opti
|
||||
|
||||
<!-- links -->
|
||||
|
||||
[AioGuideI18nCommonPrepareMarkTextForTranslations]: guide/i18n-common-prepare#mark-text-for-translations "Mark text for translations - Prepare templates for translations | Angular"
|
||||
[AioGuideI18nCommonPrepareMarkTextInComponentTemplate]: guide/i18n-common-prepare#mark-text-in-component-template "Mark text in component template - Prepare templates for translations | Angular"
|
||||
|
||||
<!-- end links -->
|
||||
|
||||
@reviewed 2021-09-15
|
||||
@reviewed 2021-10-13
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
# Angular Package Format
|
||||
|
||||
This document describes the Angular Package Format (APF).
|
||||
APF is an Angular specific specification for the structure and format of npm packages that is used by all first-party Angular packages (`@angular/core`, `@angular/material`, etc.) and most third-party Angular libraries.
|
||||
|
||||
APF enables a package to work seamlessly under most common scenarios that use Angular.
|
||||
Packages that use APF are compatible with the tooling offered by the Angular team as well as wider JavaScript ecosystem.
|
||||
It is recommended that third-party library developers follow the same npm package format.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
APF is versioned along with the rest of Angular, and every major version improves the package format.
|
||||
You can find the versions of the specification prior to v13 in this [google doc](https://docs.google.com/document/d/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs/preview).
|
||||
|
||||
</div>
|
||||
|
||||
## Why specify a package format?
|
||||
|
||||
In today's JavaScript landscape, developers will consume packages in many different ways, using many different toolchains (Webpack, rollup, esbuild, etc). These tools may understand and require different inputs - some tools may be able to process the latest ES language version, while others may benefit from directly consuming an older ES version.
|
||||
|
||||
The Angular distribution format supports all of the commonly used development tools and workflows, and adds emphasis on optimizations that result either in smaller application payload size or faster development iteration cycle (build time).
|
||||
|
||||
Developers can rely on Angular CLI and [ng-packagr](https://github.com/ng-packagr/ng-packagr) (a build tool Angular CLI uses) to produce packages in the Angular package format. See the [Creating Libraries](/guide/creating-libraries) guide for more details.
|
||||
|
||||
## File layout
|
||||
|
||||
The following example shows a simplified version of the `@angular/core` package's file layout, with an explanation for each file in the package. This table describes the file layout under `node_modules/@angular/core` annotated to describe the purpose of files and directories:
|
||||
|
||||
<style>
|
||||
table tbody>tr>td {
|
||||
padding: 8px;
|
||||
vertical-align: middle;
|
||||
line-height: normal;
|
||||
}
|
||||
</style>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><pre>README.md</pre>
|
||||
<td>Package README, used by npmjs web UI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>package.json</pre></td>
|
||||
<td>Primary package.json, describing the package itself as well as all available entrypoints and code formats. This file contains the "exports" mapping used by runtimes and tools to perform module resolution.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>core.d.ts</pre></td>
|
||||
<td>Bundled .d.ts for the primary entrypoint (@angular/core)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>esm2020/
|
||||
core.mjs
|
||||
index.mjs
|
||||
public_api.mjs</pre></td>
|
||||
<td>Tree of @angular/core's sources in unflattened ES2020 format.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>esm2020/testing/</pre></td>
|
||||
<td>Tree of the @angular/core/testing entrypoint in unflattened ES2020 format.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>fesm2015/
|
||||
core.mjs
|
||||
core.mjs.map
|
||||
testing.mjs
|
||||
testing.mjs.map</pre></td>
|
||||
<td>Code for all entrypoints in a flattened (FESM) ES2015 format, along with sourcemaps.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>fesm2020/
|
||||
core.mjs
|
||||
core.mjs.map
|
||||
testing.mjs
|
||||
testing.mjs.map</pre></td>
|
||||
<td>Code for all entrypoints in flattened (FESM) ES2020 format, along with sourcemaps.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>testing/</pre></td>
|
||||
<td>Directory representing the "testing" entrypoint.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>testing/package.json</pre></td>
|
||||
<td>Defines the @angular/core/testing entrypoint with its typings for TypeScript, which does not support the top level "exports" based module resolution.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><pre>testing/testing.d.ts</pre></td>
|
||||
<td>Actual .d.ts for the @angular/core/testing entrypoint</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## `package.json`
|
||||
|
||||
The primary `package.json` contains important package metadata, including the following:
|
||||
|
||||
* It [declares](#esm-declaration) the package to be in EcmaScript Module (ESM) format.
|
||||
* It contains an [`"exports"` field](#exports) which defines the available source code formats of all entrypoints.
|
||||
* It contains [keys](#legacy-resolution-keys) which define the available source code formats of the primary `@angular/core` entrypoint, for tools which do not understand `"exports"`. These keys are considered deprecated, and will be removed as the support for `"exports"` rolls out across the ecosystem.
|
||||
* It declares whether the package contains [side-effects](#side-effects).
|
||||
|
||||
### ESM declaration
|
||||
|
||||
The top-level `package.json` contains the key:
|
||||
|
||||
<code-example language="js">
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
</code-example>
|
||||
|
||||
This informs resolvers that code within the package is using EcmaScript Modules as opposed to CommonJS modules.
|
||||
|
||||
### `"exports"`
|
||||
|
||||
The `"exports"` field has the following structure:
|
||||
|
||||
<code-example language="js">
|
||||
"exports": {
|
||||
"./schematics/*": {
|
||||
"default": "./schematics/*.js"
|
||||
},
|
||||
"./package.json": {
|
||||
"default": "./package.json"
|
||||
},
|
||||
".": {
|
||||
"types": "./core.d.ts",
|
||||
"esm2020": "./esm2020/core.mjs",
|
||||
"es2020": "./fesm2020/core.mjs",
|
||||
"es2015": "./fesm2015/core.mjs",
|
||||
"node": "./fesm2015/core.mjs",
|
||||
"default": "./fesm2020/core.mjs"
|
||||
},
|
||||
"./testing": {
|
||||
"types": "./testing/testing.d.ts",
|
||||
"esm2020": "./esm2020/testing/testing.mjs",
|
||||
"es2020": "./fesm2020/testing.mjs",
|
||||
"es2015": "./fesm2015/testing.mjs",
|
||||
"node": "./fesm2015/testing.mjs",
|
||||
"default": "./fesm2020/testing.mjs"
|
||||
}
|
||||
}
|
||||
</code-example>
|
||||
|
||||
Of primary interest are the `"."` and the `"./testing"` keys, which define the available code formats for the `@angular/core` primary entrypoint and the `@angular/core/testing` secondary entrypoint, respectively. For each entrypoint, the available formats are:
|
||||
|
||||
* Typings (`.d.ts` files)
|
||||
`.d.ts` files are used by TypeScript when depending on a given package.
|
||||
* `es2020` - ES2020 code flattened into a single source file.
|
||||
* `es2015` - ES2015 code flattened into a single source file.
|
||||
* `esm2020` - ES2020 code in unflattened source files (this format is included for experimentation - see [this discussion of defaults](#note-about-the-defaults-in-packagejson) for details).
|
||||
|
||||
Tooling that is aware of these keys may preferentially select a desirable code format from `"exports"`. The remaining 2 keys control the default behavior of tooling:
|
||||
|
||||
* `"node"` selects flattened ES2015 code when the package is loaded in Node.
|
||||
|
||||
This format is used due to the requirements of `zone.js`, which does not support native `async`/`await` ES2017 syntax. Therefore, Node is instructed to use ES2015 code, where `async`/`await` structures have been downleveled into Promises.
|
||||
|
||||
* `"default"` selects flattened ES2020 code for all other consumers.
|
||||
|
||||
### Legacy resolution keys
|
||||
|
||||
In addition to `"exports"`, the top-level `package.json` also defines legacy module resolution keys for resolvers that don't support `"exports"`. For `@angular/core` these are:
|
||||
|
||||
<code-example language="js">
|
||||
{
|
||||
"fesm2020": "./fesm2020/core.mjs",
|
||||
"fesm2015": "./fesm2015/core.mjs",
|
||||
"esm2020": "./esm2020/core.mjs",
|
||||
"typings": "./core.d.ts",
|
||||
"module": "./fesm2015/core.mjs",
|
||||
"es2020": "./fesm2020/core.mjs",
|
||||
}
|
||||
</code-example>
|
||||
|
||||
As above, a module resolver can use these keys to load a specific code format. Note that instead of `"default"`, `"module"` selects the format both for Node as well as any tooling not configured to use a specific key. As with `"node"`, ES2015 code is selected due to the constraints of ZoneJS.
|
||||
|
||||
### Side effects
|
||||
|
||||
The last function of `package.json` is to declare whether the package has [side-effects](#sideeffects-flag).
|
||||
|
||||
<code-example language="js">
|
||||
{
|
||||
"sideEffects": false
|
||||
}
|
||||
</code-example>
|
||||
|
||||
Most Angular packages should not depend on top-level side effects, and thus should include this declaration.
|
||||
|
||||
### Secondary `package.json`s
|
||||
|
||||
In addition to the top-level `package.json`, secondary entrypoints have a corresponding directory with its own `package.json`. For example, in `@angular/core` the `@angular/core/testing` entrypoint is represented by `testing/package.json`.
|
||||
|
||||
This secondary `package.json` is required for legacy resolvers which do not support `"exports"`. We expect these files to be removed in a future version of the Angular Package Format, once support for `"exports"` is consistent across the ecosystem.
|
||||
|
||||
## Entrypoints and Code Splitting
|
||||
|
||||
Packages in the Angular Package Format contain one primary entrypoint and zero or more secondary entrypoints (e.g. `@angular/common/http`). Entrypoints serve several functions.
|
||||
|
||||
1. They define the module specifiers from which users import code (for example, `@angular/core` and `@angular/core/testing`).
|
||||
|
||||
Users typically perceive these entrypoints as distinct groups of symbols, with different purposes or functionality.
|
||||
|
||||
Specific entrypoints might only be used for special purposes, such as testing. Such APIs can be separated out from the primary entrypoint to reduce the chance of them being used accidentally or incorrectly.
|
||||
|
||||
2. They define the granularity at which code can be lazily loaded.
|
||||
|
||||
Many modern build tools are only capable of "code splitting" (aka lazy loading) at the ES Module level. Since the Angular Package Format uses primarily a single "flat" ES Module per entrypoint, this means that most build tooling will not be able to split code in a single entrypoint into multiple output chunks.
|
||||
|
||||
The general rule for APF packages is to use entrypoints for the smallest sets of logically connected code possible. For example, the Angular Material package publishes each logical component or set of components as a separate entrypoint - one for Button, one for Tabs, etc. This allows each Material component to be lazily loaded separately, if desired.
|
||||
|
||||
Not all libraries require such granularity. Most libraries with a single logical purpose should be published as a single entrypoint. `@angular/core` for example uses a single entrypoint for the runtime, because the Angular runtime is generally used as a single entity.
|
||||
|
||||
### Resolution of Secondary Entrypoints
|
||||
|
||||
Secondary entrypoints may be resolved by tooling in one of two ways:
|
||||
|
||||
* via the `"exports"` field of the primary `package.json` for the package
|
||||
* by Node Module Resolution rules, via a `package.json` in a subdirectory corresponding to the entrypoint's module ID. For example, the `testing/package.json` file for the `@angular/core/testing` secondary entrypoint.
|
||||
|
||||
## README.md
|
||||
|
||||
The readme file in the markdown format that is used to display description of a package on npm and github.
|
||||
|
||||
Example readme content of @angular/core package:
|
||||
|
||||
<code-example language="html">
|
||||
Angular
|
||||
=======
|
||||
|
||||
The sources for this package are in the main [Angular](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo.
|
||||
|
||||
License: MIT
|
||||
</code-example>
|
||||
|
||||
## Partial Compilation
|
||||
|
||||
Libraries in the Angular Package Format must be published in "partial compilation" mode. This is a compilation mode for `ngc` which produces compiled Angular code that is not tied to a specific Angular runtime version, in contrast to the full compilation used for applications, where the Angular compiler and runtime versions must match exactly.
|
||||
|
||||
To partially compile Angular code, use the `"compilationMode"` flag in `"angularCompilerOptions"` in your `tsconfig.json`:
|
||||
|
||||
<code-example language="js">
|
||||
{
|
||||
...
|
||||
"angularCompilerOptions": {
|
||||
"compilationMode": "partial",
|
||||
}
|
||||
}
|
||||
</code-example>
|
||||
|
||||
Partially compiled library code is then converted to fully compiled code during the application build process by the Angular CLI.
|
||||
|
||||
If your build pipeline does not use the Angular CLI then refer to the [Consuming partial ivy code outside the Angular CLI](/guide/creating-libraries#consuming-partial-ivy-code-outside-the-angular-cli) guide.
|
||||
|
||||
## Optimizations
|
||||
|
||||
### Flattening of ES Modules
|
||||
|
||||
The Angular Package Format specifies that code be published in "flattened" ES module format. This significantly reduces the build time of Angular applications as well as download and parse time of the final application bundle. Please check out the excellent post ["The cost of small modules"](https://nolanlawson.com/2016/08/15/the-cost-of-small-modules/) by Nolan Lawson.
|
||||
|
||||
The Angular compiler has support for generating index ES module files that can then be used to generate flattened modules using tools like Rollup, resulting in a file format we call Flattened ES Module or FESM.
|
||||
|
||||
FESM is a file format created by flattening all ES Modules accessible from an entrypoint into a single ES Module. It’s formed by following all imports from a package and copying that code into a single file while preserving all public ES exports and removing all private imports.
|
||||
|
||||
The shortened name “FESM” (pronounced “phesom”) can have a number after it such as “FESM5” or “FESM2015”. The number refers to the language level of the JavaScript inside the module. So a FESM5 file would be ESM+ES5 (import/export statements and ES5 source code).
|
||||
|
||||
To generate a flattened ES Module index file, use the following configuration options in your tsconfig.json file:
|
||||
|
||||
<code-example language="js">
|
||||
{
|
||||
"compilerOptions": {
|
||||
...
|
||||
"module": "esnext",
|
||||
"target": "es2020",
|
||||
...
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
...
|
||||
"flatModuleOutFile": "my-ui-lib.js",
|
||||
"flatModuleId": "my-ui-lib"
|
||||
}
|
||||
}
|
||||
</code-example>
|
||||
|
||||
Once the index file (e.g. `my-ui-lib.js`) is generated by ngc, bundlers and optimizers like Rollup can be used to produce the flattened ESM file.
|
||||
|
||||
#### Note about the defaults in package.json
|
||||
|
||||
As of webpack v4 the flattening of ES modules optimization should not be necessary for webpack users, and in fact theoretically we should be able to get better code-splitting without flattening of modules in webpack. In practice we still see size regressions when using unflattened modules as input for webpack v4. This is why `"module"` and `"es2020"` package.json entries still point to fesm files. We are investigating this issue and expect that we'll switch the `"module"` and `"es2020"` package.json entry points to unflattened files when the size regression issue is resolved. The APF currently includes unflattened ESM2020 code for the purpose of validating such a future change.
|
||||
|
||||
### "sideEffects" flag
|
||||
|
||||
By default, EcmaScript Modules are side-effectful: importing from a module ensures that any code at the top level of that module will execute. This is often undesirable, as most side-effectful code in typical modules is not truly side-effectful, but instead only affects specific symbols. If those symbols are not imported and used, it's often desirable to remove them in an optimization process known as tree-shaking, and the side-effectful code can prevent this.
|
||||
|
||||
Build tools such as Webpack support a flag which allows ackages to declare that they do not depend on side-effectful code at the top level of their modules, giving the tools more freedom to tree-shake code from the package. The end result of these optimizations should be smaller bundle size and better code distribution in bundle chunks after code-splitting. This optimization can break your code if it contains non-local side-effects - this is however not common in Angular applications and it's usually a sign of bad design. Our recommendation is for all packages to claim the side-effect free status by setting the `sideEffects` property to `false`, and that developers follow the [Angular Style Guide](https://angular.io/guide/styleguide) which naturally results in code without non-local side-effects.
|
||||
|
||||
More info: [webpack docs on side-effects](https://github.com/webpack/webpack/tree/master/examples/side-effects)
|
||||
|
||||
### ES2020 Language Level
|
||||
|
||||
ES2020 Language level is now the default language level that is consumed by Angular CLI and other tooling. The Angular CLI will downlevel the bundle to a language level that is supported by all targeted browsers at application build time.
|
||||
|
||||
### d.ts bundling / type definition flattening
|
||||
|
||||
As of APF v8 we now prefer to run [API Extractor](https://api-extractor.com/), to bundle TypeScript definitions so that the entire API appears in a single file.
|
||||
|
||||
In prior APF versions each entry point would have a `src` directory next to the .d.ts entry point and this directory contained individual d.ts files matching the structure of the original source code. While this distribution format is still allowed and supported, it is highly discouraged because it confuses tools like IDEs that then offer incorrect autocompletion, and allows users to depend on deep-import paths which are typically not considered to be public API of a library or a package.
|
||||
|
||||
### Tslib
|
||||
|
||||
As of APF v10, we recommend adding tslib as a direct dependency of your primary entry-point. This is because the tslib version is tied to the TypeScript version used to compile your library.
|
||||
|
||||
## Examples
|
||||
|
||||
* [@angular/core package](https://unpkg.com/browse/@angular/core@13.0.0-rc.0/)
|
||||
* [@angular/material package](https://unpkg.com/browse/@angular/material@13.0.0-rc.0/)
|
||||
|
||||
## Definition of Terms
|
||||
|
||||
The following terms are used throughout this document very intentionally. In this section we define all of them to provide additional clarity.
|
||||
|
||||
<style>
|
||||
dt {
|
||||
font-size: larger;
|
||||
font-weight: bold;
|
||||
padding: 16px 0px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<dl>
|
||||
<dt>Package</dt>
|
||||
<dd>The smallest set of files that are published to NPM and installed together, for example @angular/core. This package includes a manifest called package.json, compiled source code, typescript definition files, source maps, metadata, etc. The package is installed with npm install @angular/core.</dd>
|
||||
<dt>Symbol</dt>
|
||||
<dd>A class, function, constant or variable contained in a module and optionally made visible to the external world via a module export.</dd>
|
||||
<dt>Module</dt>
|
||||
<dd>Short for ECMAScript Modules. A file containing statements that import and export symbols. This is identical to the definition of modules in the ECMAScript spec.</dd>
|
||||
<dt>ESM</dt>
|
||||
<dd>Short for ECMAScript Modules (see above)</dd>
|
||||
<dt>FESM</dt>
|
||||
<dd>Short for Flattened ES Modules and consists of a file format created by flattening all ES Modules accessible from an entry point into a single ES Module.
|
||||
</dd>
|
||||
<dt>Module ID</dt>
|
||||
<dd>The identifier of a module used in the import statements, e.g. `@angular/core`. The ID often maps directly to a path on the filesystem, but this is not always the case due to various module resolution strategies.</dd>
|
||||
<dt>Module Specifier</dt>
|
||||
<dd>A module identifier (see above)</dd>
|
||||
<dt>Module Resolution Strategy</dt>
|
||||
<dd>Algorithm used to convert Module IDs to paths on the filesystem. Node.js has one that is well specified and widely used, TypeScript supports several module resolution strategies, Closure has yet another strategy.</dd>
|
||||
<dt>Module Format</dt>
|
||||
<dd>Specification of the module syntax that covers at minimum the syntax for the importing and exporting from a file. Common module formats are CommonJS (CJS, typically used for Node.js applications) or ECMAScript Modules (ESM). The module format indicates only the packaging of the individual modules, but not the JavaScript language features used to make up the module content. Because of this, the Angular team often uses the language level specifier as a suffix to the module format, e.g. ESM+ES5 specifies that the module is in ESM format and contains code down-leveled to ES5. Other commonly used combos: ESM+ES2015, CJS+ES5, and CJS+ES2015.</dd>
|
||||
<dt>Bundle</dt>
|
||||
<dd>An artifact in the form of a single JS file, produced by a build tool, e.g. WebPack or Rollup, that contains symbols originating in one or more modules. Bundles are a browser-specific workaround that reduce network strain that would be caused if browsers were to start downloading hundreds if not tens of thousands of files. Node.js typically doesn't use bundles. Common bundle formats are UMD and System.register.</dd>
|
||||
<dt>Language Level</dt>
|
||||
<dd>The language of the code (ES5 or ES2015). Independent of the module format.</dd>
|
||||
<dt>Entry Point</dt>
|
||||
<dd>A module intended to be imported by the user. It is referenced by a unique module ID and exports the public API referenced by that module ID. An example is @angular/core or @angular/core/testing. Both entry points exist in the @angular/core package, but they export different symbols. A package can have many entry points.</dd>
|
||||
<dt>Deep Import</dt>
|
||||
<dd>A process of retrieving symbols from modules that are not Entry Points. These module IDs are usually considered to be private APIs that can change over the lifetime of the project or while the bundle for the given package is being created.</dd>
|
||||
<dt>Top-Level Import</dt>
|
||||
<dd>An import coming from an entry point. The available top-level imports are what define the public API and are exposed in “@angular/name” modules, such as @angular/core or @angular/common.</dd>
|
||||
<dt>Tree-shaking</dt>
|
||||
<dd>The process of identifying and removing code not used by an application - also known as dead code elimination. This is a global optimization performed at the application level using tools like Rollup, Closure Compiler, or Uglify.</dd>
|
||||
<dt>AOT Compiler</dt>
|
||||
<dd>The Ahead of Time Compiler for Angular.</dd>
|
||||
<dt>Flattened Type Definitions</dt>
|
||||
<dd>The bundled TypeScript definitions generated from api-extractor.</dd>
|
||||
</dl>
|
||||
@@ -263,7 +263,7 @@ What it does
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>[state](api/animations/state)()</code></td>
|
||||
<td><code><a href="api/animations/state" class="code-anchor">state</a>()</code></td>
|
||||
<td>Creates a named set of CSS styles that should be applied on successful transition to a given state. The state can then be referenced by name within other animation functions.</td>
|
||||
</tr>
|
||||
|
||||
@@ -283,7 +283,7 @@ What it does
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>[group](api/animations/group)()</code></td>
|
||||
<td><code><a href="api/animations/group" class="code-anchor">group</a>()</code></td>
|
||||
<td>Specifies a group of animation steps (<em>inner animations</em>) to be run in parallel. Animation continues only after all inner animation steps have completed. Used within <code>sequence()</code> or <code>transition()</code>.</td>
|
||||
</tr>
|
||||
|
||||
|
||||
@@ -551,18 +551,6 @@ In the template type-checking phase, the Angular template compiler uses the Type
|
||||
Enable this phase explicitly by adding the compiler option `"fullTemplateTypeCheck"` in the `"angularCompilerOptions"` of the project's TypeScript configuration file
|
||||
(see [Angular Compiler Options](guide/angular-compiler-options)).
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
In [Angular Ivy](guide/ivy), the template type checker has been completely rewritten to be more capable as well as stricter, meaning it can catch a variety of new errors that the previous type checker would not detect.
|
||||
|
||||
As a result, templates that previously compiled under View Engine can fail type checking under Ivy. This can happen because Ivy's stricter checking catches genuine errors, or because application code is not typed correctly, or because the application uses libraries in which typings are inaccurate or not specific enough.
|
||||
|
||||
This stricter type checking is not enabled by default in version 9, but can be enabled by setting the `strictTemplates` configuration option.
|
||||
|
||||
For more information about type-checking options, and about improvements to template type checking in version 9 and above, see [Template type checking](guide/template-typecheck).
|
||||
|
||||
</div>
|
||||
|
||||
Template validation produces error messages when a type error is detected in a template binding
|
||||
expression, similar to how type errors are reported by the TypeScript compiler against code in a `.ts`
|
||||
file.
|
||||
|
||||
@@ -319,35 +319,6 @@ Autoprefixer looks for the `browserslist` configuration when it prefixes your CS
|
||||
|
||||
See the [browserslist repo](https://github.com/browserslist/browserslist) for more examples of how to target specific browsers and versions.
|
||||
|
||||
### Backward compatibility with Lighthouse
|
||||
|
||||
If you want to produce a progressive web application and are using [Lighthouse](https://developers.google.com/web/tools/lighthouse/) to grade the project, add the following `browserslist` entry to your `package.json` file, in order to eliminate the [old flexbox](https://developers.google.com/web/tools/lighthouse/audits/old-flexbox) prefixes:
|
||||
|
||||
```
|
||||
"browserslist": [
|
||||
"last 2 versions",
|
||||
"not ie <= 10",
|
||||
"not ie_mob <= 10"
|
||||
]
|
||||
```
|
||||
|
||||
### Backward compatibility with CSS grid
|
||||
|
||||
CSS grid layout support in Autoprefixer, which was previously on by default, is off by default in Angular 8 and higher.
|
||||
|
||||
To use CSS grid with Internet Explorer 10/11, you must explicitly enable it using the `autoplace` option.
|
||||
To do this, add the following to the top of the global styles file (or within a specific css selector scope):
|
||||
|
||||
```
|
||||
/* autoprefixer grid: autoplace */
|
||||
```
|
||||
or
|
||||
```
|
||||
/* autoprefixer grid: no-autoplace */
|
||||
```
|
||||
|
||||
For more information, see [Autoprefixer documentation](https://autoprefixer.github.io/).
|
||||
|
||||
{@a proxy}
|
||||
|
||||
## Proxying to a backend server
|
||||
|
||||
@@ -443,3 +443,5 @@ and verify that the history meets expectations:
|
||||
|
||||
|
||||
[Back to top](guide/component-interaction#top)
|
||||
|
||||
@reviewed 2021-09-17
|
||||
|
||||
@@ -10,7 +10,7 @@ A simple example might be a button that sends users to your company website, tha
|
||||
Use the Angular CLI to generate a new library skeleton in a new workspace with the following commands.
|
||||
|
||||
<code-example language="bash">
|
||||
ng new my-workspace --create-application=false
|
||||
ng new my-workspace --no-create-application
|
||||
cd my-workspace
|
||||
ng generate library my-lib
|
||||
</code-example>
|
||||
@@ -71,9 +71,9 @@ Use an NgModule to expose services and components.
|
||||
|
||||
Your library should supply documentation (typically a README file) for installation and maintenance.
|
||||
|
||||
## Refactoring parts of an app into a library
|
||||
## Refactoring parts of an application into a library
|
||||
|
||||
To make your solution reusable, you need to adjust it so that it does not depend on app-specific code.
|
||||
To make your solution reusable, you need to adjust it so that it does not depend on application-specific code.
|
||||
Here are some things to consider in migrating application functionality to a library.
|
||||
|
||||
* Declarations such as components and pipes should be designed as stateless, meaning they don’t rely on or alter external variables. If you do rely on state, you need to evaluate every case and decide whether it is application state or state that the library would manage.
|
||||
@@ -122,7 +122,7 @@ If that form needs additional customization by the developer who is using your l
|
||||
However, if the form will always be the same and not need much customization by developers, then you could create a dynamic component that takes the configuration and generates the form.
|
||||
In general, the more complex the customization, the more useful the schematic approach.
|
||||
|
||||
To learn more, see [Schematics Overview](guide/schematics) and [Schematics for Libraries](guide/schematics-for-libraries).
|
||||
For more information, see [Schematics Overview](guide/schematics) and [Schematics for Libraries](guide/schematics-for-libraries).
|
||||
|
||||
## Publishing your library
|
||||
|
||||
@@ -149,56 +149,19 @@ npm publish
|
||||
|
||||
## Managing assets in a library
|
||||
|
||||
Starting with version 9.x of the [ng-packagr](https://github.com/ng-packagr/ng-packagr/blob/master/README.md) tool, you can configure the tool to automatically copy assets into your library package as part of the build process.
|
||||
Use this feature when your library needs to publish optional theming files, Sass mixins, or documentation (like a changelog).
|
||||
|
||||
* Learn how to [copy assets into your library as part of the build](https://github.com/ng-packagr/ng-packagr/blob/master/docs/copy-assets.md).
|
||||
|
||||
* Learn more about how to use the tool to [embed assets in CSS](https://github.com/ng-packagr/ng-packagr/blob/master/docs/embed-assets-css.md).
|
||||
|
||||
## Linked libraries
|
||||
|
||||
While working on a published library, use [npm link](https://docs.npmjs.com/cli/link) to avoid reinstalling the library on every build.
|
||||
|
||||
The library must be rebuilt on every change.
|
||||
When linking a library, make sure that the build step runs in watch mode, and that the library's `package.json` configuration points at the correct entry points.
|
||||
For example, `main` should point at a JavaScript file, not a TypeScript file.
|
||||
|
||||
### Use TypeScript path mapping for peer dependencies
|
||||
In your Angular library, the distributable can include additional assets like theming files, Sass mixins, or documentation (like a changelog).
|
||||
For more information [copy assets into your library as part of the build](https://github.com/ng-packagr/ng-packagr/blob/master/docs/copy-assets.md) and [embed assets in component styles](https://github.com/ng-packagr/ng-packagr/blob/master/docs/embed-assets-css.md).
|
||||
|
||||
## Peer dependencies
|
||||
Angular libraries should list any `@angular/*` dependencies the library depends on as peer dependencies.
|
||||
This ensures that when modules ask for Angular, they all get the exact same module.
|
||||
If a library lists `@angular/core` in `dependencies` instead of `peerDependencies`, it might get a different Angular module instead, which would cause your application to break.
|
||||
|
||||
While developing a library, you must install all peer dependencies through `devDependencies` to ensure that the library compiles properly.
|
||||
A linked library then has its own set of Angular libraries that it uses for building, located in its `node_modules` folder.
|
||||
However, this can cause problems while building or running your application.
|
||||
## Using your own library in applications
|
||||
|
||||
To get around this problem use TypeScript path mapping to tell TypeScript that it should load some modules from a specific location.
|
||||
List all the peer dependencies that your library uses in the workspace TypeScript configuration file `./tsconfig.json`, and point them at the local copy in the application's `node_modules` folder.
|
||||
You don't have to publish your library to the npm package manager to use it the same workspace, but you do have to build it first.
|
||||
|
||||
```
|
||||
{
|
||||
"compilerOptions": {
|
||||
// ...
|
||||
// paths are relative to `baseUrl` path.
|
||||
"paths": {
|
||||
"@angular/*": [
|
||||
"./node_modules/@angular/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This mapping ensures that your library always loads the local copies of the modules it needs.
|
||||
|
||||
|
||||
## Using your own library in apps
|
||||
|
||||
You don't have to publish your library to the npm package manager to use it in your own applications, but you do have to build it first.
|
||||
|
||||
To use your own library in an app:
|
||||
To use your own library in an application:
|
||||
|
||||
* Build the library. You cannot use a library before it is built.
|
||||
<code-example language="bash">
|
||||
@@ -217,15 +180,17 @@ 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 import something from a library in an Angular application, 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.
|
||||
|
||||
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.
|
||||
|
||||
For more information, see [Path mapping overview](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping).
|
||||
|
||||
</div>
|
||||
|
||||
If you find that changes to your library are not reflected in your application, your app is probably using an old build of the library.
|
||||
If you find that changes to your library are not reflected in your application, your application is probably using an old build of the library.
|
||||
|
||||
You can rebuild your library whenever you make changes to it, but this extra step takes time.
|
||||
*Incremental builds* functionality improves the library-development experience.
|
||||
@@ -254,83 +219,32 @@ TypeScript path mappings should *not* point to the library source `.ts` files.
|
||||
|
||||
{@a ivy-libraries}
|
||||
|
||||
## Building libraries with Ivy
|
||||
## Publishing libraries
|
||||
|
||||
There are three distribution formats to use when publishing a library:
|
||||
There are two distribution formats to use when publishing a library:
|
||||
|
||||
* View Engine _(deprecated)_—legacy format, slated for removal in Angular version 13.
|
||||
Only use this format if you must support View Engine applications.
|
||||
* partial-Ivy **(recommended)**—contains portable code that can be consumed by Ivy applications built with any version of Angular from v12 onwards.
|
||||
* full-Ivy—contains private Angular Ivy instructions, which are not guaranteed to work across different versions of Angular. This format requires that the library and application are built with the _exact_ same version of Angular. This format is useful for environments where all library and application code is built directly from source.
|
||||
|
||||
New libraries created with Angular CLI default to partial-Ivy format.
|
||||
If you are creating a new library with `ng generate library`, Angular uses Ivy by default with no further action on your part.
|
||||
|
||||
### Transitioning libraries to partial-Ivy format
|
||||
|
||||
Existing libraries, which are configured to generate the View Engine format, do not change when upgrading to later versions of Angular that use Ivy.
|
||||
|
||||
If you intend to publish your library to npm, compile with partial-Ivy code by setting `"compilationMode": "partial"` in `tsconfig.prod.json`.
|
||||
|
||||
A library that uses View Engine, rather than Ivy, has a `tsconfig.prod.json` file that contains the following:
|
||||
|
||||
<code-example>
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"enableIvy": false
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
To convert such libraries to use the partial-Ivy format, change the `tsconfig.prod.json` file by removing the `enableIvy` option and adding the `compilationMode` option.
|
||||
|
||||
Enable partial-Ivy compilation by replacing `"enableIvy": false` with `"compilationMode": "partial"` as follows:
|
||||
|
||||
<code-example>
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"compilationMode": "partial"
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
For publishing to npm use the partial-Ivy format as it is stable between patch versions of Angular.
|
||||
|
||||
Avoid compiling libraries with full-Ivy code if you are publishing to npm because the generated Ivy instructions are not part of Angular's public API, and so might change between patch versions.
|
||||
|
||||
Partial-Ivy code is not backward compatible with View Engine.
|
||||
If you use the library in a View Engine application, you must compile the library into the View Engine format by setting `"enableIvy": false` in the `tsconfig.json` file.
|
||||
|
||||
Ivy applications can still consume the View Engine format because the Angular compatibility compiler, or `ngcc`, can convert it to Ivy.
|
||||
|
||||
## Ensuring library version compatibility
|
||||
|
||||
The Angular version used to build an application should always be the same or greater than the Angular versions used to build any of its dependent libraries.
|
||||
For example, if you had a library using Angular version 12, the application that depends on that library should use Angular version 12 or later.
|
||||
For example, if you had a library using Angular version 13, the application that depends on that library should use Angular version 13 or later.
|
||||
Angular does not support using an earlier version for the application.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The Angular CLI uses Ivy to build applications and no longer uses View Engine.
|
||||
A library or an application built with View Engine cannot consume a partial-Ivy library.
|
||||
|
||||
</div>
|
||||
|
||||
Because this process happens during the application build, it uses the same version of the Angular compiler, ensuring that the application and all of its libraries use a single version of Angular.
|
||||
|
||||
If you intend to publish your library to npm, compile with partial-Ivy code by setting `"compilationMode": "partial"` in `tsconfig.prod.json`.
|
||||
This partial format is stable between different versions of Angular, so is safe to publish to npm.
|
||||
This partial format is stable between different versions of Angular, so is safe to publish to npm. Code with this format is processed during the application build using the same version of the Angular compiler, ensuring that the application and all of its libraries use a single version of Angular.
|
||||
|
||||
|
||||
Avoid compiling libraries with full-Ivy code if you are publishing to npm because the generated Ivy instructions are not part of Angular's public API, and so might change between patch versions.
|
||||
|
||||
Partial-Ivy code is not backward compatible with View Engine.
|
||||
If you use the library in a View Engine application, you must compile the library into the View Engine format by setting `"enableIvy": false` in the `tsconfig.json` file.
|
||||
|
||||
Ivy applications can still consume the View Engine format because the Angular compatibility compiler, or `ngcc`, can convert it to Ivy in the Angular CLI.
|
||||
|
||||
If you've never published a package in npm before, you must create a user account. Read more in [Publishing npm Packages](https://docs.npmjs.com/getting-started/publishing-npm-packages).
|
||||
|
||||
|
||||
## Consuming partial-Ivy code outside the Angular CLI
|
||||
|
||||
An application installs many Angular libraries from npm into its `node_modules` directory.
|
||||
@@ -351,3 +265,5 @@ The Angular linker Babel plugin supports build caching, meaning that libraries o
|
||||
The Angular CLI integrates the linker plugin automatically, so if consumers of your library are using the CLI, they can install Ivy-native libraries from npm without any additional configuration.
|
||||
|
||||
</div>
|
||||
|
||||
@reviewed 2021-10-29
|
||||
|
||||
+138
-310
@@ -33,45 +33,44 @@ v12 - v15
|
||||
v13 -> v16
|
||||
-->
|
||||
|
||||
| Area | API or Feature | May be removed in |
|
||||
|:--- |:--- |:--- |
|
||||
| `@angular/common` | [`ReflectiveInjector`](#reflectiveinjector) | <!--v8--> v11 |
|
||||
| `@angular/common` | [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation) | <!--v9--> v11 |
|
||||
| `@angular/common/http` | [`XhrFactory`](api/common/http/XhrFactory) | <!--v12--> v15 |
|
||||
| `@angular/core` | [`DefaultIterableDiffer`](#core) | <!--v7--> v11 |
|
||||
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v11 |
|
||||
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v11 |
|
||||
| `@angular/core` | [Factory-based signature of `ApplicationRef.bootstrap`](#core) | <!--v13--> v15 |
|
||||
| `@angular/core` | [`PlatformRef.bootstrapModuleFactory`](#core) | <!--v13--> v15 |
|
||||
| `@angular/core` | [`getModuleFactory`](#core) | <!--v13--> v16 |
|
||||
| `@angular/core` | [`ModuleWithComponentFactories`](#core) | <!--v13--> v16 |
|
||||
| `@angular/core` | [`Compiler`](#core) | <!--v13--> v16 |
|
||||
| `@angular/core` | [`CompilerFactory`](#core) | <!--v13--> v16 |
|
||||
| `@angular/core` | [`NgModuleFactory`](#core) | <!--v13--> v16 |
|
||||
| `@angular/platform-browser-dynamic` | [`JitCompilerFactory`](#platform-browser-dynamic) | <!--v13--> v16 |
|
||||
| `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | <!--v6--> v11 |
|
||||
| `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | <!--v8--> v11 |
|
||||
| `@angular/upgrade` | [`getAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
||||
| `@angular/upgrade` | [`setAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
||||
| template syntax | [`<template>`](#template-tag) | <!--v7--> v11 |
|
||||
| polyfills | [reflect-metadata](#reflect-metadata) | <!--v8--> v11 |
|
||||
| npm package format | [`esm5` and `fesm5` entry-points in @angular/* npm packages](guide/deprecations#esm5-fesm5) | <!-- v9 --> v11 |
|
||||
| `@angular/compiler-cli` | [Input setter coercion](#input-setter-coercion) | <!--v13--> v15 |
|
||||
| `@angular/compiler-cli` | [`fullTemplateTypeCheck`](#full-template-type-check) | <!--v13--> v15 |
|
||||
| `@angular/core` | [`defineInjectable`](#core) | <!--v8--> v11 |
|
||||
| `@angular/core` | [`entryComponents`](api/core/NgModule#entryComponents) | <!--v9--> v11 |
|
||||
| `@angular/core` | [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | <!--v9--> v11 |
|
||||
| `@angular/core` | [Factory-based signature of `ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent) | <!--v13--> v15 |
|
||||
| `@angular/core/testing` | [`TestBed.get`](#testing) | <!--v9--> v12 |
|
||||
| `@angular/core/testing` | [`async`](#testing) | <!--v9--> v12 |
|
||||
| `@angular/core/testing` | [`aotSummaries` argument in `TestBed.initTestEnvironment`](#testing) | <!--v13--> v14 |
|
||||
| `@angular/core/testing` | [`aotSummaries` field of the `TestModuleMetadata` type](#testing) | <!--v13--> v14 |
|
||||
| `@angular/forms` | [`FormBuilder.group` legacy options parameter](api/forms/FormBuilder#group) | <!--v11--> v14 |
|
||||
| `@angular/router` | [`ActivatedRoute` params and `queryParams` properties](#activatedroute-props) | unspecified |
|
||||
| `@angular/service-worker` | [`SwUpdate#activated`](api/service-worker/SwUpdate#activated) | <!--v13--> v16 |
|
||||
| `@angular/service-worker` | [`SwUpdate#available`](api/service-worker/SwUpdate#available) | <!--v13--> v16 |
|
||||
| template syntax | [`/deep/`, `>>>`, and `::ng-deep`](#deep-component-style-selector) | <!--v7--> unspecified |
|
||||
| template syntax | [`bind-`, `on-`, `bindon-`, and `ref-`](#bind-syntax) | <!--v13--> v15 |
|
||||
| Area | API or Feature | May be removed in |
|
||||
| :---------------------------------- | :--------------------------------------------------------------------------------------------------------- | :-------------------- |
|
||||
| `@angular/common` | [`ReflectiveInjector`](#reflectiveinjector) | <!--v8--> v11 |
|
||||
| `@angular/common` | [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation) | <!--v9--> v11 |
|
||||
| `@angular/common/http` | [`XhrFactory`](api/common/http/XhrFactory) | <!--v12--> v15 |
|
||||
| `@angular/core` | [`DefaultIterableDiffer`](#core) | <!--v7--> v11 |
|
||||
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v11 |
|
||||
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v11 |
|
||||
| `@angular/core` | [Factory-based signature of `ApplicationRef.bootstrap`](#core) | <!--v13--> v15 |
|
||||
| `@angular/core` | [`PlatformRef.bootstrapModuleFactory`](#core) | <!--v13--> v15 |
|
||||
| `@angular/core` | [`getModuleFactory`](#core) | <!--v13--> v16 |
|
||||
| `@angular/core` | [`ModuleWithComponentFactories`](#core) | <!--v13--> v16 |
|
||||
| `@angular/core` | [`Compiler`](#core) | <!--v13--> v16 |
|
||||
| `@angular/core` | [`CompilerFactory`](#core) | <!--v13--> v16 |
|
||||
| `@angular/core` | [`NgModuleFactory`](#core) | <!--v13--> v16 |
|
||||
| `@angular/platform-browser-dynamic` | [`JitCompilerFactory`](#platform-browser-dynamic) | <!--v13--> v16 |
|
||||
| `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | <!--v6--> v11 |
|
||||
| `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | <!--v8--> v11 |
|
||||
| `@angular/upgrade` | [`getAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
||||
| `@angular/upgrade` | [`setAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
||||
| template syntax | [`<template>`](#template-tag) | <!--v7--> v11 |
|
||||
| polyfills | [reflect-metadata](#reflect-metadata) | <!--v8--> v11 |
|
||||
| `@angular/compiler-cli` | [Input setter coercion](#input-setter-coercion) | <!--v13--> v15 |
|
||||
| `@angular/compiler-cli` | [`fullTemplateTypeCheck`](#full-template-type-check) | <!--v13--> v15 |
|
||||
| `@angular/core` | [`defineInjectable`](#core) | <!--v8--> v11 |
|
||||
| `@angular/core` | [`entryComponents`](api/core/NgModule#entryComponents) | <!--v9--> v11 |
|
||||
| `@angular/core` | [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | <!--v9--> v11 |
|
||||
| `@angular/core` | [Factory-based signature of `ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent) | <!--v13--> v15 |
|
||||
| `@angular/core/testing` | [`TestBed.get`](#testing) | <!--v9--> v12 |
|
||||
| `@angular/core/testing` | [`async`](#testing) | <!--v9--> v12 |
|
||||
| `@angular/core/testing` | [`aotSummaries` argument in `TestBed.initTestEnvironment`](#testing) | <!--v13--> v14 |
|
||||
| `@angular/core/testing` | [`aotSummaries` field of the `TestModuleMetadata` type](#testing) | <!--v13--> v14 |
|
||||
| `@angular/forms` | [`FormBuilder.group` legacy options parameter](api/forms/FormBuilder#group) | <!--v11--> v14 |
|
||||
| `@angular/platform-server` | [`renderModuleFactory`](#platform-server) | <!--v13--> v15 |
|
||||
| `@angular/service-worker` | [`SwUpdate#activated`](api/service-worker/SwUpdate#activated) | <!--v13--> v16 |
|
||||
| `@angular/service-worker` | [`SwUpdate#available`](api/service-worker/SwUpdate#available) | <!--v13--> v16 |
|
||||
| template syntax | [`/deep/`, `>>>`, and `::ng-deep`](#deep-component-style-selector) | <!--v7--> unspecified |
|
||||
| template syntax | [`bind-`, `on-`, `bindon-`, and `ref-`](#bind-syntax) | <!--v13--> v15 |
|
||||
|
||||
For information about Angular CDK and Angular Material deprecations, see the [changelog](https://github.com/angular/components/blob/master/CHANGELOG.md).
|
||||
|
||||
@@ -90,7 +89,7 @@ This section contains a complete list all of the currently-deprecated APIs, with
|
||||
### @angular/common
|
||||
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
| :--- |:--- |:--- |:--- |
|
||||
| :-------------------------------------------------------------------------------------------- | :-------------------------------------------------- | :-------------------- | :-------------------------------------------------------------------------------------------------------- |
|
||||
| [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation) | `{provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'}` | v9 | From v11 the default code will be extracted from the locale data given by `LOCALE_ID`, rather than `USD`. |
|
||||
|
||||
{@a common-http}
|
||||
@@ -98,57 +97,65 @@ This section contains a complete list all of the currently-deprecated APIs, with
|
||||
### @angular/common/http
|
||||
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
|:--- |:--- |:--- |:--- |
|
||||
| :----------------------------------------- | :-------------------------------- | :-------------------- | :--------------------------------------------------------------------------- |
|
||||
| [`XhrFactory`](api/common/http/XhrFactory) | `XhrFactory` in `@angular/common` | v12 | The `XhrFactory` has moved from `@angular/common/http` to `@angular/common`. |
|
||||
|
||||
{@a core}
|
||||
|
||||
### @angular/core
|
||||
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
|:--- |:--- |:--- |:--- |
|
||||
| [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. |
|
||||
| [`ReflectiveInjector`](api/core/ReflectiveInjector) | `{@link Injector#create Injector.create()}` | v5 | See [`ReflectiveInjector`](#reflectiveinjector) |
|
||||
| [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none |
|
||||
| [`defineInjectable`](api/core/defineInjectable) | `ɵɵdefineInjectable` | v8 | Used only in generated code. No source code should depend on this API. |
|
||||
| [`entryComponents`](api/core/NgModule#entryComponents) | none | v9 | See [`entryComponents`](#entryComponents) |
|
||||
| [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | v9 | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) |
|
||||
| [`async`](api/core/testing/async) | [`waitForAsync`](api/core/testing/waitForAsync) | v11 | The [`async`](api/core/testing/async) function from `@angular/core/testing` has been renamed to `waitForAsync` in order to avoid confusion with the native JavaScript <code class="no-auto-link">async</code> syntax. The existing function is deprecated and will be removed in a future version. |
|
||||
| [`getModuleFactory`](api/core/getModuleFactory) | [`getNgModuleById`](api/core/getNgModuleById) | v13 | Ivy allows working with NgModule classes directly, without retrieving corresponding factories. |
|
||||
| `ViewChildren.emitDistinctChangesOnly` / `ContentChildren.emitDistinctChangesOnly` | none (was part of [issue #40091](https://github.com/angular/angular/issues/40091)) | | This is a temporary flag introduced as part of bugfix of [issue #40091](https://github.com/angular/angular/issues/40091) and will be removed. |
|
||||
| Factory-based signature of [`ApplicationRef.bootstrap`](api/core/ApplicationRef#bootstrap) | Type-based signature of [`ApplicationRef.bootstrap`](api/core/ApplicationRef#bootstrap) | v13 | With Ivy, there is no need to resolve Component factory and Component Type can be provided directly. |
|
||||
| [`PlatformRef.bootstrapModuleFactory`](api/core/PlatformRef#bootstrapModuleFactory) | [`PlatformRef.bootstrapModule`](api/core/PlatformRef#bootstrapModule) | v13 | With Ivy, there is no need to resolve NgModule factory and NgModule Type can be provided directly. |
|
||||
| [`ModuleWithComponentFactories`](api/core/ModuleWithComponentFactories) | none | v13 | Ivy JIT mode doesn't require accessing this symbol. See [JIT API changes due to ViewEngine deprecation](#jit-api-changes) for additional context. |
|
||||
| [`Compiler`](api/core/Compiler) | none | v13 | Ivy JIT mode doesn't require accessing this symbol. See [JIT API changes due to ViewEngine deprecation](#jit-api-changes) for additional context. |
|
||||
| [`CompilerFactory`](api/core/CompilerFactory) | none | v13 | Ivy JIT mode doesn't require accessing this symbol. See [JIT API changes due to ViewEngine deprecation](#jit-api-changes) for additional context. |
|
||||
| [`NgModuleFactory`](api/core/NgModuleFactory) | Use non-factory based framework APIs like [PlatformRef.bootstrapModule](api/core/PlatformRef#bootstrapModule) and [createNgModuleRef](api/core/createNgModuleRef) | v13 | Ivy JIT mode doesn't require accessing this symbol. See [JIT API changes due to ViewEngine deprecation](#jit-api-changes) for additional context. |
|
||||
| [Factory-based signature of `ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent) | [Type-based signature of `ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent) | v13 | Angular no longer requires component factories to dynamically create components. Use different signature of the `createComponent` method, which allows passing Component class directly. |
|
||||
|
||||
{@a platform-browser-dynamic}
|
||||
|
||||
### @angular/platform-browser-dynamic
|
||||
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
|:--- |:--- |:--- |:--- |
|
||||
| [`JitCompilerFactory`](api/platform-browser-dynamic/JitCompilerFactory) | none | v13 | Ivy JIT mode doesn't require accessing this symbol. See [JIT API changes due to ViewEngine deprecation](#jit-api-changes) for additional context. |
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
| :--------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. |
|
||||
| [`ReflectiveInjector`](api/core/ReflectiveInjector) | `{@link Injector#create Injector.create()}` | v5 | See [`ReflectiveInjector`](#reflectiveinjector) |
|
||||
| [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none |
|
||||
| [`defineInjectable`](api/core/defineInjectable) | `ɵɵdefineInjectable` | v8 | Used only in generated code. No source code should depend on this API. |
|
||||
| [`entryComponents`](api/core/NgModule#entryComponents) | none | v9 | See [`entryComponents`](#entryComponents) |
|
||||
| [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | v9 | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) |
|
||||
| [`async`](api/core/testing/async) | [`waitForAsync`](api/core/testing/waitForAsync) | v11 | The [`async`](api/core/testing/async) function from `@angular/core/testing` has been renamed to `waitForAsync` in order to avoid confusion with the native JavaScript <code class="no-auto-link">async</code> syntax. The existing function is deprecated and will be removed in a future version. |
|
||||
| [`getModuleFactory`](api/core/getModuleFactory) | [`getNgModuleById`](api/core/getNgModuleById) | v13 | Ivy allows working with NgModule classes directly, without retrieving corresponding factories. |
|
||||
| `ViewChildren.emitDistinctChangesOnly` / `ContentChildren.emitDistinctChangesOnly` | none (was part of [issue #40091](https://github.com/angular/angular/issues/40091)) | | This is a temporary flag introduced as part of bugfix of [issue #40091](https://github.com/angular/angular/issues/40091) and will be removed. |
|
||||
| Factory-based signature of [`ApplicationRef.bootstrap`](api/core/ApplicationRef#bootstrap) | Type-based signature of [`ApplicationRef.bootstrap`](api/core/ApplicationRef#bootstrap) | v13 | With Ivy, there is no need to resolve Component factory and Component Type can be provided directly. |
|
||||
| [`PlatformRef.bootstrapModuleFactory`](api/core/PlatformRef#bootstrapModuleFactory) | [`PlatformRef.bootstrapModule`](api/core/PlatformRef#bootstrapModule) | v13 | With Ivy, there is no need to resolve NgModule factory and NgModule Type can be provided directly. |
|
||||
| [`ModuleWithComponentFactories`](api/core/ModuleWithComponentFactories) | none | v13 | Ivy JIT mode doesn't require accessing this symbol. See [JIT API changes due to ViewEngine deprecation](#jit-api-changes) for additional context. |
|
||||
| [`Compiler`](api/core/Compiler) | none | v13 | Ivy JIT mode doesn't require accessing this symbol. See [JIT API changes due to ViewEngine deprecation](#jit-api-changes) for additional context. |
|
||||
| [`CompilerFactory`](api/core/CompilerFactory) | none | v13 | Ivy JIT mode doesn't require accessing this symbol. See [JIT API changes due to ViewEngine deprecation](#jit-api-changes) for additional context. |
|
||||
| [`NgModuleFactory`](api/core/NgModuleFactory) | Use non-factory based framework APIs like [PlatformRef.bootstrapModule](api/core/PlatformRef#bootstrapModule) and [createNgModuleRef](api/core/createNgModuleRef) | v13 | Ivy JIT mode doesn't require accessing this symbol. See [JIT API changes due to ViewEngine deprecation](#jit-api-changes) for additional context. |
|
||||
| [Factory-based signature of `ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent) | [Type-based signature of `ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent) | v13 | Angular no longer requires component factories to dynamically create components. Use different signature of the `createComponent` method, which allows passing Component class directly. |
|
||||
|
||||
{@a testing}
|
||||
|
||||
### @angular/core/testing
|
||||
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
|:--- |:--- |:--- |:--- |
|
||||
| [`TestBed.get`](api/core/testing/TestBed#get) | [`TestBed.inject`](api/core/testing/TestBed#inject) | v9 | Same behavior, but type safe. |
|
||||
| [`async`](api/core/testing/async) | [`waitForAsync`](api/core/testing/waitForAsync) | v10 | Same behavior, but rename to avoid confusion. |
|
||||
| [`aotSummaries` argument in `TestBed.initTestEnvironment`](api/core/testing/TestBed#inittestenvironment) | No replacement needed | v13 | Summary files are unused in Ivy. |
|
||||
| [`aotSummaries` field of the `TestModuleMetadata` type](api/core/testing/TestModuleMetadata) | No replacement needed | v13 | Summary files are unused in Ivy. |
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
| :------------------------------------------------------------------------------------------------------- | :-------------------------------------------------- | :-------------------- | :-------------------------------------------- |
|
||||
| [`TestBed.get`](api/core/testing/TestBed#get) | [`TestBed.inject`](api/core/testing/TestBed#inject) | v9 | Same behavior, but type safe. |
|
||||
| [`async`](api/core/testing/async) | [`waitForAsync`](api/core/testing/waitForAsync) | v10 | Same behavior, but rename to avoid confusion. |
|
||||
| [`aotSummaries` argument in `TestBed.initTestEnvironment`](api/core/testing/TestBed#inittestenvironment) | No replacement needed | v13 | Summary files are unused in Ivy. |
|
||||
| [`aotSummaries` field of the `TestModuleMetadata` type](api/core/testing/TestModuleMetadata) | No replacement needed | v13 | Summary files are unused in Ivy. |
|
||||
|
||||
{@a platform-browser-dynamic}
|
||||
|
||||
### @angular/platform-browser-dynamic
|
||||
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
| :---------------------------------------------------------------------- | :---------- | :-------------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`JitCompilerFactory`](api/platform-browser-dynamic/JitCompilerFactory) | none | v13 | This symbol is no longer necessary. See [JIT API changes due to ViewEngine deprecation](#jit-api-changes) for additional context. |
|
||||
|
||||
{@a platform-server}
|
||||
|
||||
### @angular/platform-server
|
||||
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
| :--------------------------------------------------------------- | :------------------------------------------------- | :-------------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`renderModuleFactory`](api/platform-server/renderModuleFactory) | [`renderModule`](api/platform-server/renderModule) | v13 | This symbol is no longer necessary. See [JIT API changes due to ViewEngine deprecation](#jit-api-changes) for additional context. |
|
||||
|
||||
{@a forms}
|
||||
|
||||
### @angular/forms
|
||||
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
|:--- |:--- |:--- |:--- |
|
||||
| :-------------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :-------------------- | :---- |
|
||||
| [`ngModel` with reactive forms](#ngmodel-reactive) | [`FormControlDirective`](api/forms/FormControlDirective) | v6 | none |
|
||||
| [`FormBuilder.group` legacy options parameter](api/forms/FormBuilder#group) | [`AbstractControlOptions` parameter value](api/forms/AbstractControlOptions) | v11 | none |
|
||||
|
||||
@@ -156,17 +163,17 @@ This section contains a complete list all of the currently-deprecated APIs, with
|
||||
|
||||
### @angular/service-worker
|
||||
|
||||
| API | Replacement | Deprecation announced | Notes
|
||||
|:--- |:--- |:--- |:---
|
||||
| [`SwUpdate#activated`](api/service-worker/SwUpdate#activated) | [`SwUpdate#activateUpdate()` return value](api/service-worker/SwUpdate#activateUpdate) | v13 | The return value of `SwUpdate#activateUpdate()` indicates whether an update was successfully activated. |
|
||||
| [`SwUpdate#available`](api/service-worker/SwUpdate#available) | [`SwUpdate#versionUpdates`](api/service-worker/SwUpdate#versionUpdates) | v13 | The behavior of `SwUpdate#available` can be rebuilt by filtering for `VersionReadyEvent` events on [`SwUpdate#versionUpdates`](api/service-worker/SwUpdate#versionUpdates) |
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
| :------------------------------------------------------------ | :------------------------------------------------------------------------------------- | :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`SwUpdate#activated`](api/service-worker/SwUpdate#activated) | [`SwUpdate#activateUpdate()` return value](api/service-worker/SwUpdate#activateUpdate) | v13 | The return value of `SwUpdate#activateUpdate()` indicates whether an update was successfully activated. |
|
||||
| [`SwUpdate#available`](api/service-worker/SwUpdate#available) | [`SwUpdate#versionUpdates`](api/service-worker/SwUpdate#versionUpdates) | v13 | The behavior of `SwUpdate#available` can be rebuilt by filtering for `VersionReadyEvent` events on [`SwUpdate#versionUpdates`](api/service-worker/SwUpdate#versionUpdates) |
|
||||
|
||||
{@a upgrade}
|
||||
|
||||
### @angular/upgrade
|
||||
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
|:--- |:--- |:--- |:--- |
|
||||
| :------------------------------ | :---------------------------------------------- | :-------------------- | :--------------------------------------------- |
|
||||
| [All entry points](api/upgrade) | [`@angular/upgrade/static`](api/upgrade/static) | v5 | See [Upgrading from AngularJS](guide/upgrade). |
|
||||
|
||||
{@a upgrade-static}
|
||||
@@ -174,7 +181,7 @@ This section contains a complete list all of the currently-deprecated APIs, with
|
||||
### @angular/upgrade/static
|
||||
|
||||
| API | Replacement | Deprecation announced | Notes |
|
||||
|:--- |:--- |:--- |:--- |
|
||||
| :-------------------------------------------------- | :------------------------------------------------------------ | :-------------------- | :--------------------------------------------- |
|
||||
| [`getAngularLib`](api/upgrade/static/getAngularLib) | [`getAngularJSGlobal`](api/upgrade/static/getAngularJSGlobal) | v5 | See [Upgrading from AngularJS](guide/upgrade). |
|
||||
| [`setAngularLib`](api/upgrade/static/setAngularLib) | [`setAngularJSGlobal`](api/upgrade/static/setAngularJSGlobal) | v5 | See [Upgrading from AngularJS](guide/upgrade). |
|
||||
|
||||
@@ -232,17 +239,9 @@ Support for using the `ngModel` input property and `ngModelChange` event with re
|
||||
|
||||
Now deprecated:
|
||||
|
||||
<code-example language="html">
|
||||
<code-example path="deprecation-guide/src/app/app.component.html" region="deprecated-example" language="html"></code-example>
|
||||
|
||||
<input [formControl]="control" [(ngModel)]="value">
|
||||
|
||||
</code-example>
|
||||
|
||||
<code-example language="typescript">
|
||||
|
||||
this.value = 'some value';
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.component.ts" region="deprecated-example"></code-example>
|
||||
|
||||
This support was deprecated for several reasons. First, developers found this pattern confusing. It seems like the actual `ngModel` directive is being used, but in fact it's an input/output property named `ngModel` on the reactive form directive that approximates some, but not all, of the directive's behavior.
|
||||
It allows getting and setting a value and intercepting value events, but some of `ngModel`'s other features, such as delaying updates with`ngModelOptions` or exporting the directive, don't work.
|
||||
@@ -254,41 +253,19 @@ To update your code before support is removed, you'll want to decide whether to
|
||||
|
||||
**After** (choice 1 - use reactive forms):
|
||||
|
||||
<code-example language="html">
|
||||
<code-example path="deprecation-guide/src/app/app.component.html" region="reactive-form-example" language="html"></code-example>
|
||||
|
||||
<input [formControl]="control">
|
||||
|
||||
</code-example>
|
||||
|
||||
<code-example language="typescript">
|
||||
|
||||
this.control.setValue('some value');
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.component.ts" region="reactive-form-example"></code-example>
|
||||
|
||||
**After** (choice 2 - use template-driven forms):
|
||||
|
||||
<code-example language="html">
|
||||
<code-example path="deprecation-guide/src/app/app.component.html" region="template-driven-form-example" language="html"></code-example>
|
||||
|
||||
<input [(ngModel)]="value">
|
||||
|
||||
</code-example>
|
||||
|
||||
<code-example language="typescript">
|
||||
|
||||
this.value = 'some value';
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.component.ts" region="template-driven-form-example"></code-example>
|
||||
|
||||
By default, when you use this pattern, you will see a deprecation warning once in dev mode. You can choose to silence this warning by configuring `ReactiveFormsModule` at import time:
|
||||
|
||||
<code-example language="typescript">
|
||||
|
||||
imports: [
|
||||
ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'});
|
||||
]
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.module.ts" region="reactive-form-no-warning"></code-example>
|
||||
|
||||
Alternatively, you can choose to surface a separate warning for each instance of this pattern with a configuration value of `"always"`. This may help to track down where in the code the pattern is being used as the code is being updated.
|
||||
|
||||
@@ -300,19 +277,11 @@ In v5, Angular replaced the `ReflectiveInjector` with the `StaticInjector`. The
|
||||
|
||||
**Before**:
|
||||
|
||||
<code-example language="typescript">
|
||||
|
||||
ReflectiveInjector.resolveAndCreate(providers);
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.component.ts" language="typescript" region="reflective-injector-deprecated-example"></code-example>
|
||||
|
||||
**After**:
|
||||
|
||||
<code-example language="typescript">
|
||||
|
||||
Injector.create({providers});
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.component.ts" language="typescript" region="static-injector-example"></code-example>
|
||||
|
||||
{@a loadChildren}
|
||||
|
||||
@@ -324,27 +293,11 @@ In version 8, the string syntax for the [`loadChildren`](api/router/LoadChildren
|
||||
|
||||
**Before**:
|
||||
|
||||
<code-example language="typescript">
|
||||
|
||||
const routes: Routes = [{
|
||||
path: 'lazy',
|
||||
// The following string syntax for loadChildren is deprecated
|
||||
loadChildren: './lazy/lazy.module#LazyModule'
|
||||
}];
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.module.ts" language="typescript" region="lazyload-deprecated-syntax"></code-example>
|
||||
|
||||
**After**:
|
||||
|
||||
<code-example language="typescript">
|
||||
|
||||
const routes: Routes = [{
|
||||
path: 'lazy',
|
||||
// The new import() syntax
|
||||
loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)
|
||||
}];
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.module.ts" language="typescript" region="lazyload-syntax"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -358,18 +311,6 @@ const routes: Routes = [{
|
||||
|
||||
</div>
|
||||
|
||||
{@a activatedroute-props}
|
||||
|
||||
### ActivatedRoute params and queryParams properties
|
||||
|
||||
[ActivatedRoute](api/router/ActivatedRoute) contains two [properties](api/router/ActivatedRoute#properties) that are less capable than their replacements and may be deprecated in a future Angular version.
|
||||
|
||||
| Property | Replacement |
|
||||
| -------- | ----------- |
|
||||
| `params` | `paramMap` |
|
||||
| `queryParams` | `queryParamMap` |
|
||||
|
||||
For more information see the [Getting route information](guide/router-reference#activated-route) section of the [Router guide](guide/router).
|
||||
|
||||
{@a reflect-metadata}
|
||||
|
||||
@@ -393,21 +334,12 @@ See the [dedicated migration guide for static queries](guide/static-query-migrat
|
||||
|
||||
The following pattern is deprecated:
|
||||
|
||||
<code-example language="typescript">
|
||||
|
||||
@Input() @ContentChild(TemplateRef) tpl !: TemplateRef<any>;
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.component.ts" language="typescript" region="template-with-input-deprecated"></code-example>
|
||||
|
||||
Rather than using this pattern, separate the two decorators into their own
|
||||
properties and add fallback logic as in the following example:
|
||||
|
||||
<code-example language="typescript">
|
||||
|
||||
@Input() tpl !: TemplateRef<any>;
|
||||
@ContentChild(TemplateRef) inlineTemplate !: TemplateRef<any>;
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.component.ts" language="typescript" region="template-with-input"></code-example>
|
||||
|
||||
{@a cant-assign-template-vars}
|
||||
|
||||
@@ -416,19 +348,11 @@ properties and add fallback logic as in the following example:
|
||||
In the following example, the two-way binding means that `optionName`
|
||||
should be written when the `valueChange` event fires.
|
||||
|
||||
<code-example language="html">
|
||||
|
||||
<option *ngFor="let optionName of options" [(value)]="optionName"></option>
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.component.1.html" region="two-way-template-deprecated"></code-example>
|
||||
|
||||
However, in practice, Angular ignores two-way bindings to template variables. Starting in version 8, attempting to write to template variables is deprecated. In a future version, we will throw to indicate that the write is not supported.
|
||||
|
||||
<code-example language="html">
|
||||
|
||||
<option *ngFor="let optionName of options" [value]="optionName"></option>
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.component.html" region="valid-template-bind"></code-example>
|
||||
|
||||
{@a binding-to-innertext}
|
||||
|
||||
@@ -469,39 +393,11 @@ For example:
|
||||
|
||||
**Before**
|
||||
|
||||
<code-example language="typescript">
|
||||
|
||||
@NgModule({...})
|
||||
export class MyModule {
|
||||
static forRoot(config: SomeConfig): ModuleWithProviders {
|
||||
return {
|
||||
ngModule: SomeModule,
|
||||
providers: [
|
||||
{provide: SomeConfig, useValue: config}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.module.ts" language="typescript" region="ModuleWithProvidersNonGeneric"></code-example>
|
||||
|
||||
**After**:
|
||||
|
||||
<code-example language="typescript">
|
||||
|
||||
@NgModule({...})
|
||||
export class MyModule {
|
||||
static forRoot(config: SomeConfig): ModuleWithProviders<SomeModule> {
|
||||
return {
|
||||
ngModule: SomeModule,
|
||||
providers: [
|
||||
{provide: SomeConfig, useValue: config }
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
</code-example>
|
||||
<code-example path="deprecation-guide/src/app/app.module.ts" language="typescript" region="ModuleWithProvidersGeneric"></code-example>
|
||||
|
||||
<!--
|
||||
### Internet Explorer 11
|
||||
@@ -527,41 +423,11 @@ This means that input coercion fields are no longer needed, as their effects can
|
||||
|
||||
For example, the following directive:
|
||||
|
||||
```typescript
|
||||
@Component({...})
|
||||
class SubmitButton {
|
||||
private _disabled: boolean;
|
||||
|
||||
@Input()
|
||||
get disabled(): boolean {
|
||||
return this._disabled;
|
||||
}
|
||||
|
||||
set disabled(value: boolean) {
|
||||
this._disabled = (value === '') || value;
|
||||
}
|
||||
|
||||
static ngAcceptInputType_disabled: boolean|'';
|
||||
}
|
||||
```
|
||||
<code-example path="deprecation-guide/src/app/submit-button/submit-button.component.ts" language="typescript" region="submitButtonNarrow"></code-example>
|
||||
|
||||
can be refactored as follows:
|
||||
|
||||
```typescript
|
||||
@Component({...})
|
||||
class SubmitButton {
|
||||
private _disabled: boolean;
|
||||
|
||||
@Input()
|
||||
get disabled(): boolean {
|
||||
return this._disabled;
|
||||
}
|
||||
|
||||
set disabled(value: boolean|'') {
|
||||
this._disabled = (value === '') || value;
|
||||
}
|
||||
}
|
||||
```
|
||||
<code-example path="deprecation-guide/src/app/submit-button/submit-button.component.ts" language="typescript" region="submitButton"></code-example>
|
||||
|
||||
{@a full-template-type-check}
|
||||
### `fullTemplateTypeCheck`
|
||||
@@ -608,89 +474,39 @@ Important note: this deprecation doesn't affect JIT mode in Ivy (JIT remains ava
|
||||
|
||||
This section contains a complete list all of the currently deprecated CLI flags.
|
||||
|
||||
### @angular/cli
|
||||
|
||||
| API/Option | May be removed in | Notes |
|
||||
| ----------------- | ----------------- | ----------------------------------------- |
|
||||
| `--prod` | <!--v12--> v14 | Use `--configuration production` instead. |
|
||||
| `ng update --all` | <!--v11--> v14 | No longer has an effect. |
|
||||
|
||||
### @angular-devkit/build-angular
|
||||
|
||||
| API/Option | May be removed in | Notes |
|
||||
| ------------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| `extractCss` | <!--v11--> v13 | No longer required to disable CSS extraction during development. |
|
||||
| `i18nFormat` | <!--v9--> v12 | Format is now automatically detected. |
|
||||
| `i18nLocale` | <!--v9--> v12 | New [localization option][AioGuideI18nCommonMergeDefineLocalesInTheBuildConfiguration] in version 9 and later. |
|
||||
| `hmrWarning` | <!--v11--> v13 | No longer has an effect. |
|
||||
| `servePathDefaultWarning` | <!--v11--> v13 | No longer has an effect. |
|
||||
| API/Option | May be removed in | Notes |
|
||||
| -------------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `deployUrl` | <!--v13--> v15 | Use `baseHref` option, `APP_BASE_HREF` DI token or a combination of both instead. For more information, see [the deploy url](guide/deployment#the-deploy-url). |
|
||||
| `showCircularDependencies` | <!--v12--> v14 | The recommended method to detect circular dependencies in project code is to use either a lint rule or other external tooling. |
|
||||
| Protractor builder | <!--v12--> v14 | Deprecate as part of the Protractor deprecation. |
|
||||
|
||||
### @schematics/angular
|
||||
### @angular-devkit/build-optimizer
|
||||
|
||||
| API/Option | May be removed in | Notes |
|
||||
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
|
||||
| `lintFix` | <!--v11--> v12 | Deprecated as part of TSLint deprecation. |
|
||||
The entire NPM package is deprecated. It has always been experimental (never hit `1.0.0`) and has
|
||||
been an internal package for the Angular CLI. All the relevant functionality has been moved to
|
||||
`@angular-devkit/build-angular`.
|
||||
|
||||
{@a removed}
|
||||
|
||||
## Removed APIs
|
||||
|
||||
The following APIs have been removed starting with version 11.0.0*:
|
||||
The following APIs have been removed starting with version 11.0.0\*:
|
||||
|
||||
| Package | API | Replacement | Notes |
|
||||
| ---------------- | -------------- | ----------- | ----- |
|
||||
| `@angular/router` | `preserveQueryParams` | [`queryParamsHandling`](api/router/UrlCreationOptions#queryParamsHandling) | |
|
||||
| Package | API | Replacement | Notes |
|
||||
| ----------------- | --------------------- | -------------------------------------------------------------------------- | ----- |
|
||||
| `@angular/router` | `preserveQueryParams` | [`queryParamsHandling`](api/router/UrlCreationOptions#queryParamsHandling) | |
|
||||
|
||||
\* To see APIs removed in version 10, check out this guide on the [version 10 docs site](https://v10.angular.io/guide/deprecations#removed).
|
||||
|
||||
{@a esm5-fesm5}
|
||||
|
||||
### `esm5` and `fesm5` code formats in @angular/* npm packages
|
||||
|
||||
As of Angular v8, the CLI primarily consumes the `fesm2015` variant of the code distributed as part of `@angular/*` npm packages.
|
||||
This renders the `esm5` and `fesm5` distributions obsolete and unnecessary, adding bloat to the package size and slowing down npm installations.
|
||||
|
||||
This removal has no impact on CLI users, unless they modified their build configuration to explicitly consume these code distributions.
|
||||
|
||||
Any application still relying on the `esm5` and `fesm5` as the input to its build system will need to ensure that the build pipeline is capable of accepting JavaScript code conforming to ECMAScript 2015 (ES2015) language specification.
|
||||
|
||||
Note that this change doesn't make existing libraries distributed in this format incompatible with the Angular CLI.
|
||||
The CLI will fall back and consume libraries in less desirable formats if others are not available.
|
||||
However, we do recommend that libraries ship their code in ES2015 format in order to make builds faster and build output smaller.
|
||||
|
||||
In practical terms, the `package.json` of all `@angular` packages has changed in the following way:
|
||||
|
||||
**Before**:
|
||||
|
||||
<code-example language="json" header="package.app.json">
|
||||
|
||||
{
|
||||
"name": "@angular/core",
|
||||
"version": "9.0.0",
|
||||
"main": "./bundles/core.umd.js",
|
||||
"module": "./fesm5/core.js",
|
||||
"es2015": "./fesm2015/core.js",
|
||||
"esm5": "./esm5/core.js",
|
||||
"esm2015": "./esm2015/core.js",
|
||||
"fesm5": "./fesm5/core.js",
|
||||
"fesm2015": "./fesm2015/core.js",
|
||||
...
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
**After**:
|
||||
|
||||
<code-example language="json" header="package.app.json">
|
||||
|
||||
{
|
||||
"name": "@angular/core",
|
||||
"version": "10.0.0",
|
||||
"main": "./bundles/core.umd.js",
|
||||
"module": "./fesm2015/core.js",
|
||||
"es2015": "./fesm2015/core.js",
|
||||
"esm2015": "./esm2015/core.js",
|
||||
"fesm2015": "./fesm2015/core.js",
|
||||
...
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
For more information about the npm package format, see the [Angular Package Format spec](https://goo.gl/jB3GVv).
|
||||
|
||||
{@a style-sanitization}
|
||||
|
||||
### Style Sanitization for `[style]` and `[style.prop]` bindings
|
||||
@@ -706,6 +522,18 @@ Find more information about the replacement in the [`LoadChildrenCallback` docum
|
||||
|
||||
The supporting classes `NgModuleFactoryLoader`, `SystemJsNgModuleLoader` and `SystemJsNgModuleLoaderConfig` classes were removed from `@angular/core`, as well as `SpyNgModuleFactoryLoader` from `@angular/router`.
|
||||
|
||||
### `WrappedValue`
|
||||
|
||||
The purpose of `WrappedValue` was to allow the same object instance to be treated as different for the purposes of change detection.
|
||||
It was commonly used with the `async` pipe in the case where the `Observable` produces the same instance of the value.
|
||||
|
||||
Given that this use case is relatively rare and special handling impacted application performance, the `WrappedValue` API has been removed in Angular 13.
|
||||
|
||||
If you rely on the behavior that the same object instance should cause change detection, you have two options:
|
||||
|
||||
- Clone the resulting value so that it has a new identity.
|
||||
- Explicitly call [`ChangeDetectorRef.detectChanges()`](api/core/ChangeDetectorRef#detectchanges) to force the update.
|
||||
|
||||
<!-- links -->
|
||||
|
||||
[AioGuideI18nCommonMergeDefineLocalesInTheBuildConfiguration]: guide/i18n-common-merge#define-locales-in-the-build-configuration "Define locales in the build configuration - Common Internationalization task #6: Merge translations into the application | Angular"
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# Dynamic component loader
|
||||
|
||||
Component templates are not always fixed. An application might need to load new components at runtime.
|
||||
|
||||
This cookbook shows you how to use `ComponentFactoryResolver` to add components dynamically.
|
||||
Component templates are not always fixed. An application might need to load new components at runtime. This cookbook shows you how to add components dynamically.
|
||||
|
||||
See the <live-example name="dynamic-component-loader"></live-example>
|
||||
of the code in this cookbook.
|
||||
@@ -113,10 +111,6 @@ value to select an `adItem` from the array.
|
||||
|
||||
|
||||
|
||||
After `loadComponent()` selects an ad, it uses `ComponentFactoryResolver`
|
||||
to resolve a `ComponentFactory` for each specific component.
|
||||
The `ComponentFactory` then creates an instance of each component.
|
||||
|
||||
Next, you're targeting the `viewContainerRef` that
|
||||
exists on this specific instance of the component. How do you know it's
|
||||
this specific instance? Because it's referring to `adHost` and `adHost` is the
|
||||
|
||||
@@ -123,21 +123,14 @@ The recently-developed [custom elements](https://developer.mozilla.org/en-US/doc
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
In browsers that support Custom Elements natively, the specification requires developers use ES2015 classes to define Custom Elements - developers can opt-in to this by setting the `target: "es2015"` property in their project's [TypeScript configuration file](/guide/typescript-configuration). As Custom Element and ES2015 support might not be available in all browsers, developers can instead choose to use a polyfill to support older browsers and ES5 code.
|
||||
|
||||
Use the [Angular CLI](cli) to automatically set up your project with the correct polyfill:
|
||||
Use the [Angular CLI](cli) to add the `@angular/elements` package to your workspace.
|
||||
|
||||
<code-example language="sh">
|
||||
|
||||
ng add @angular/elements --project=*your_project_name*
|
||||
npm install @angular/elements --save
|
||||
|
||||
</code-example>
|
||||
|
||||
- For more information about polyfills, see [polyfill documentation](https://www.webcomponents.org/polyfills).
|
||||
|
||||
- For more information about Angular browser support, see [Browser Support](guide/browser-support).
|
||||
|
||||
|
||||
## Example: A Popup Service
|
||||
|
||||
Previously, when you wanted to add a component to an application at runtime, you had to define a _dynamic component_, and then you would have to load it, attach it to an element in the DOM, and wire up all of the dependencies, change detection, and event handling, as described in [Dynamic Component Loader](guide/dynamic-component-loader).
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Entry components have been deprecated with the [Ivy rendering engine](guide/ivy).
|
||||
For more information, see [entryComponents deprecation](guide/deprecations#entrycomponents-and-analyze_for_entry_components-no-longer-required) in the [Deprecated APIs and features](guide/deprecations).
|
||||
Entry components are deprecated, for more information, see
|
||||
[entryComponents deprecation](guide/deprecations#entrycomponents-and-analyze_for_entry_components-no-longer-required)
|
||||
in the [Deprecated APIs and features](guide/deprecations).
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ of some of the things they contain:
|
||||
<tr>
|
||||
<td><code>HttpClientModule</code></td>
|
||||
<td><code>@angular/common/http</code></td>
|
||||
<td>When you want to talk to a server</td>
|
||||
<td>When you want to communicate with a server using the HTTP protocol</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
@@ -34,6 +34,14 @@ An Angular [component](#component) packaged as a [custom element](#custom-elemen
|
||||
|
||||
Learn more in [Angular Elements Overview](guide/elements).
|
||||
|
||||
{@a apf}
|
||||
|
||||
## Angular package format (APF)
|
||||
|
||||
An Angular specific specification for layout of npm packages that is used by all first-party Angular packages, and most third-party Angular libraries.
|
||||
|
||||
Learn more in the [Angular Package Format specification](guide/angular-package-format).
|
||||
|
||||
{@a annotation}
|
||||
|
||||
## annotation
|
||||
@@ -495,10 +503,9 @@ Read more in the [Interpolation](guide/interpolation) guide.
|
||||
|
||||
## Ivy
|
||||
|
||||
Ivy is the code name for Angular's [next-generation compilation and rendering pipeline](https://blog.angular.io/a-plan-for-version-8-0-and-ivy-b3318dfc19f7).
|
||||
With the version 9 release of Angular, the new compiler and runtime instructions are used by default instead of the older compiler and runtime, known as [View Engine](#ve).
|
||||
|
||||
See [Angular Ivy](guide/ivy).
|
||||
Ivy is the historical code name for Angular's current
|
||||
[compilation and rendering pipeline](https://blog.angular.io/a-plan-for-version-8-0-and-ivy-b3318dfc19f7).
|
||||
It is now the only supported engine, so everything uses Ivy.
|
||||
|
||||
|
||||
{@a J}
|
||||
@@ -1061,7 +1068,9 @@ View hierarchies can be loaded and unloaded dynamically as the user navigates th
|
||||
|
||||
## View Engine
|
||||
|
||||
The compilation and rendering pipeline used by Angular before version 9. Compare [Ivy](#ivy).
|
||||
A previous compilation and rendering pipeline used by Angular. It has since been replaced by
|
||||
[Ivy](#ivy) and is no longer in use. View Engine was deprecated in version 9 and removed in version
|
||||
13.
|
||||
|
||||
|
||||
{@a view-tree}
|
||||
|
||||
@@ -1072,7 +1072,7 @@ the service becomes available only in the `VillainsListComponent` and its sub-co
|
||||
`VillainService` is a singleton with respect to `VillainsListComponent`
|
||||
because that is where it is declared. As long as `VillainsListComponent`
|
||||
does not get destroyed it will be the same instance of `VillainService`
|
||||
but if there are multilple instances of `VillainsListComponent`, then each
|
||||
but if there are multiple instances of `VillainsListComponent`, then each
|
||||
instance of `VillainsListComponent` will have its own instance of `VillainService`.
|
||||
|
||||
|
||||
|
||||
@@ -1,28 +1,35 @@
|
||||
# Add the localize package
|
||||
|
||||
{@a setting-up-cli}
|
||||
{@a add-localize}
|
||||
To take advantage of the localization features of Angular, use the [Angular CLI][AioCliMain] to add the `@angular/localize` package to your project.
|
||||
|
||||
To take advantage of the localization features of Angular, use the Angular CLI to add the `@angular/localize` package to your project.
|
||||
To add the `@angular/localize` package, use the following command to update the `package.json` and `polyfills.ts` files in your project.
|
||||
|
||||
<code-example path="i18n/doc-files/commands.sh" region="add-localize" language="sh"></code-example>
|
||||
|
||||
This command updates the `package.json` and `polyfills.ts` files of your project to import the `@angular/localize` package.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For more information about `package.json` and polyfill packages, see [Workspace npm dependencies][AioGuideNpmPackages].
|
||||
For more information about `package.json` and `polyfill.ts` files, see [Workspace npm dependencies][AioGuideNpmPackages].
|
||||
|
||||
</div>
|
||||
|
||||
If `@angular/localize` is not installed, the Angular CLI may generate an error when you try to build a localized version of your application.
|
||||
If `@angular/localize` is not installed and you try to build a localized version of your project, the [Angular CLI][AioCliMain] generates an error.
|
||||
|
||||
<!--todo: add example error -->
|
||||
|
||||
## What's next
|
||||
|
||||
* [Refer to locales by ID][AioGuideI18nCommonLocaleId]
|
||||
|
||||
<!-- links -->
|
||||
|
||||
[AioCliMain]: cli "CLI Overview and Command Reference | Angular"
|
||||
|
||||
[AioGuideI18nCommonLocaleId]: guide/i18n-common-locale-id "Refer to locales by ID | Angular"
|
||||
|
||||
[AioGuideNpmPackages]: guide/npm-packages "Workspace npm dependencies | Angular"
|
||||
|
||||
<!-- external links -->
|
||||
|
||||
<!-- end links -->
|
||||
|
||||
@reviewed 2021-09-15
|
||||
@reviewed 2021-10-07
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
# Deploy multiple locales
|
||||
|
||||
{@a deploy-locales}
|
||||
|
||||
If `myapp` is the directory containing the distributable files of your application, you would typically make available different versions for different locales in locale directories such as `myapp/fr` for the French version and `myapp/es` for the Spanish version.
|
||||
If `myapp` is the directory that contains the distributable files of your project, you typically make different versions available for different locales in locale directories. For example, your French version is located in the `myapp/fr` directory and the Spanish version is located in the `myapp/es` directory.
|
||||
|
||||
The HTML `base` tag with the `href` attribute specifies the base URI, or URL, for relative links.
|
||||
If you set the `"localize"` option in `angular.json` to `true` or to an array of locale IDs, the CLI adjusts the base `href` for each version of the application.
|
||||
If you set the `"localize"` option in [`angular.json`][AioGuideWorkspaceConfig] workspace build configuration file to `true` or to an array of locale IDs, the CLI adjusts the base `href` for each version of the application.
|
||||
To adjust the base `href` for each version of the application, the CLI adds the locale to the configured `"baseHref"`.
|
||||
Specify the `"baseHref"` for each locale in your workspace configuration file (`angular.json`).
|
||||
Specify the `"baseHref"` for each locale in your [`angular.json`][AioGuideWorkspaceConfig] workspace build configuration file.
|
||||
The following example displays `"baseHref"` set to an empty string.
|
||||
|
||||
<code-example language="json" header="angular.json" path="i18n/angular.json" region="i18n-baseHref" ></code-example>
|
||||
|
||||
Also, to declare the base `href` at compile time, use the CLI `--baseHref` option with [`ng build`][AioCliBuild].
|
||||
|
||||
### Configuring servers
|
||||
## Configure a server
|
||||
|
||||
Typical deployment of multiple languages serve each language from a different subdirectory.
|
||||
Users are redirected to the preferred language defined in the browser using the `Accept-Language` HTTP header.
|
||||
If the user has not defined a preferred language, or if the preferred language is not available, then the server falls back to the default language.
|
||||
To change the language, see another subdirectory.
|
||||
To change the language, change your current location to another subdirectory.
|
||||
The change of subdirectory often occurs using a menu implemented in the application.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For more information on how to deploy apps to a remote server, see [Deployment][AioGuideDeployment].
|
||||
|
||||
#### Nginx
|
||||
</div>
|
||||
|
||||
### Nginx example
|
||||
|
||||
The following example displays an Nginx configuration.
|
||||
|
||||
<code-example path="i18n/doc-files/nginx.conf" language="nginx"></code-example>
|
||||
|
||||
#### Apache
|
||||
### Apache example
|
||||
|
||||
The following example displays an Apache configuration.
|
||||
|
||||
@@ -42,8 +44,10 @@ The following example displays an Apache configuration.
|
||||
|
||||
[AioGuideDeployment]: guide/deployment "Deployment | Angular"
|
||||
|
||||
[AioGuideWorkspaceConfig]: guide/workspace-config "Angular workspace configuration | Angular"
|
||||
|
||||
<!-- external links -->
|
||||
|
||||
<!-- end links -->
|
||||
|
||||
@reviewed 2021-09-15
|
||||
@reviewed 2021-10-07
|
||||
|
||||
@@ -1,18 +1,49 @@
|
||||
# Format data based on locale
|
||||
|
||||
{@a i18n-pipes}
|
||||
|
||||
Angular provides the following built-in data transformation [pipes][AioGuideGlossaryPipe]. The data transformation pipes use the [`LOCALE_ID`][AioApiCoreLocaleId] token to format data based on rules of each locale.
|
||||
|
||||
* [`DatePipe`][AioApiCommonDatepipe]: Formats a date value.
|
||||
* [`CurrencyPipe`][AioApiCommonCurrencypipe]: Transforms a number to a currency string.
|
||||
* [`DecimalPipe`][AioApiCommonDecimalpipe]: Transforms a number into a decimal number string.
|
||||
* [`PercentPipe`][AioApiCommonPercentpipe]: Transforms a number to a percentage string.
|
||||
| Data transformation pipe | Details |
|
||||
|:--- |:--- |
|
||||
| [`DatePipe`][AioApiCommonDatepipe] | Formats a date value. |
|
||||
| [`CurrencyPipe`][AioApiCommonCurrencypipe] | Transforms a number into a currency string. |
|
||||
| [`DecimalPipe`][AioApiCommonDecimalpipe] | Transforms a number into a decimal number string. |
|
||||
| [`PercentPipe`][AioApiCommonPercentpipe] | Transforms a number into a percentage string. |
|
||||
|
||||
For example, `{{today | date}}` uses `DatePipe` to display the current date in the format for the locale in `LOCALE_ID`.
|
||||
## Use DatePipe to display the current date
|
||||
|
||||
To override the value of `LOCALE_ID`, add the `locale` parameter.
|
||||
For example, to force the currency to use `en-US` no matter which language-locale you set for `LOCALE_ID`, use this form: `{{amount | currency : 'en-US'}}`.
|
||||
To display the current date in the format for the current locale, use the following format for the `DatePipe`.
|
||||
|
||||
<!--todo: replace with code-example -->
|
||||
|
||||
<code-example format="typescript" language="typescript">
|
||||
|
||||
{{ today | date }}
|
||||
|
||||
</code-example>
|
||||
|
||||
## Override current locale for CurrencyPipe
|
||||
|
||||
Add the `locale` parameter to the pipe to override the current value of `LOCALE_ID` token.
|
||||
|
||||
To force the currency to use American English (`en-US`), use the following format for the `CurrencyPipe`
|
||||
|
||||
<!--todo: replace with code-example -->
|
||||
|
||||
<code-example format="typescript" language="typescript">
|
||||
|
||||
{{ amount | currency : 'en-US' }}
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
**NOTE**: The locale specified for the `CurrencyPipe` overrides the global `LOCALE_ID` token of your application.
|
||||
|
||||
</div>
|
||||
|
||||
## What's next
|
||||
|
||||
* [Prepare templates for translations][AioGuideI18nCommonPrepare]
|
||||
|
||||
<!-- links -->
|
||||
|
||||
@@ -24,8 +55,10 @@ For example, to force the currency to use `en-US` no matter which language-local
|
||||
|
||||
[AioGuideGlossaryPipe]: guide/glossary#pipe "pipe - Glossary | Angular"
|
||||
|
||||
[AioGuideI18nCommonPrepare]: guide/i18n-common-prepare "Prepare templates for translations | Angular"
|
||||
|
||||
<!-- external links -->
|
||||
|
||||
<!-- end links -->
|
||||
|
||||
@reviewed 2021-09-15
|
||||
@reviewed 2021-10-28
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user