Compare commits
141 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 46e8982c6a | |||
| 09834f9478 | |||
| 16855bd483 | |||
| 9a8352f27d | |||
| eaaa294e49 | |||
| 474eec7684 | |||
| 791fa9aa53 | |||
| baaf1d14b5 | |||
| ae9f1657cc | |||
| 46c67e9b1c | |||
| 44d8c366ec | |||
| 43bb40982a | |||
| 107d094083 | |||
| 9b0fd078aa | |||
| 1096ad26b3 | |||
| 5a5813b055 | |||
| fcf369f562 | |||
| 7306fc74e7 | |||
| f839e01d49 | |||
| dacac7eb62 | |||
| 0583227708 | |||
| 3d9455ee8b | |||
| 970e68ae38 | |||
| 5ffc882864 | |||
| a5ca049fd9 | |||
| bb465e1789 | |||
| 5547ce0aaa | |||
| 09d57530db | |||
| 063035f86d | |||
| 0a99130f85 | |||
| 6a94157d6f | |||
| bb40a08164 | |||
| c365cfab03 | |||
| b31f5dd2f0 | |||
| 2548712aba | |||
| f4fa4afd04 | |||
| 439d77e852 | |||
| 036f740f77 | |||
| e10aa8858f | |||
| 38d23af419 | |||
| 923a7446a2 | |||
| 874d78b502 | |||
| 8d18969714 | |||
| 2106b74853 | |||
| 5ff715c549 | |||
| 30c9e80a30 | |||
| 0392feb135 | |||
| 6d215d3124 | |||
| e46972ac4b | |||
| 2e42bb9c62 | |||
| d14235cfb1 | |||
| befd650a78 | |||
| fd2abebf91 | |||
| 387f4a79f8 | |||
| 8f32c09872 | |||
| 04b3497db1 | |||
| d0e3ad5300 | |||
| c7fed38cf0 | |||
| 1d39ab6e6b | |||
| fce4ea1d8d | |||
| 2a833e6eac | |||
| d0f4f1d601 | |||
| 3ee883e417 | |||
| b868f1c94e | |||
| efb8e3e431 | |||
| 8085fe34f1 | |||
| 9edfe4ac97 | |||
| 79825d3f10 | |||
| 4e9492cf6d | |||
| 3f162f5e3f | |||
| b6445c4cd6 | |||
| d05d05e48e | |||
| 7a7452dc28 | |||
| 05f3f7445a | |||
| 6307f3586e | |||
| ce6ad5a66a | |||
| cbfd91b0f2 | |||
| 8cd95d4901 | |||
| ec00075a2f | |||
| a0a635322c | |||
| 8dcd3cd263 | |||
| 677fe6a1cc | |||
| 5d8394cc8c | |||
| acdb28dc1d | |||
| c5a5165294 | |||
| 71831c63ba | |||
| 3511373e22 | |||
| ff7c71653a | |||
| 6b779b11d1 | |||
| 1eb77a2f4f | |||
| 929d69e62f | |||
| f5d764d75c | |||
| 55f485b3bb | |||
| 3606917732 | |||
| 0cbbd6aeec | |||
| a8e9247e6a | |||
| 793326deef | |||
| 10d46b4247 | |||
| b744bf6a08 | |||
| 2e5bd88ebf | |||
| 9b8ea9b5ca | |||
| eb91035102 | |||
| 17e2a2a9d1 | |||
| b431b5c6d6 | |||
| bc371dac44 | |||
| 36361b0506 | |||
| 38f53a55c3 | |||
| 8de3d099d2 | |||
| 945a3ad359 | |||
| 88003e27db | |||
| a4cba79440 | |||
| 6ca7a79bfd | |||
| e95ee96562 | |||
| 536ded2280 | |||
| 6b8e60c06a | |||
| 5004b6ebf1 | |||
| 6c08635662 | |||
| f0add81caf | |||
| 21e7584c8c | |||
| 92c9661165 | |||
| a462f91d3e | |||
| aa8d3ed69b | |||
| 2ca404dbfd | |||
| f9edb776ca | |||
| c195c3f73c | |||
| 114011d2a0 | |||
| b5153814af | |||
| e8e8e5f171 | |||
| 08fb7bc4ca | |||
| 6d709273eb | |||
| 571d7debb5 | |||
| b1ba6429d4 | |||
| 6c104a28e0 | |||
| 5e09cd1f67 | |||
| 6a7b818d94 | |||
| 9ecaa05c7d | |||
| d36a9e6bf3 | |||
| 4b452ffd3d | |||
| 3f2d16c823 | |||
| 4dca1842b3 | |||
| 442b16c551 |
@@ -96,11 +96,11 @@ build:remote --cpu=k8
|
||||
build:remote --host_cpu=k8
|
||||
|
||||
# Toolchain and platform related flags
|
||||
build:remote --crosstool_top=@npm//@angular/dev-infra-private/bazel/remote-execution/cpp:cc_toolchain_suite
|
||||
build:remote --extra_toolchains=@npm//@angular/dev-infra-private/bazel/remote-execution/cpp:cc_toolchain
|
||||
build:remote --extra_execution_platforms=@npm//@angular/dev-infra-private/bazel/remote-execution:platform
|
||||
build:remote --host_platform=@npm//@angular/dev-infra-private/bazel/remote-execution:platform
|
||||
build:remote --platforms=@npm//@angular/dev-infra-private/bazel/remote-execution:platform
|
||||
build:remote --crosstool_top=@npm//@angular/build-tooling/bazel/remote-execution/cpp:cc_toolchain_suite
|
||||
build:remote --extra_toolchains=@npm//@angular/build-tooling/bazel/remote-execution/cpp:cc_toolchain
|
||||
build:remote --extra_execution_platforms=@npm//@angular/build-tooling/bazel/remote-execution:platform
|
||||
build:remote --host_platform=@npm//@angular/build-tooling/bazel/remote-execution:platform
|
||||
build:remote --platforms=@npm//@angular/build-tooling/bazel/remote-execution:platform
|
||||
|
||||
# Remote instance and caching
|
||||
build:remote --remote_instance_name=projects/internal-200822/instances/primary_instance
|
||||
|
||||
@@ -24,20 +24,20 @@ 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 v6-angular-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "aio/yarn.lock" }}
|
||||
var_3: &cache_key v8-angular-node-16-{{ 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 v6-angular-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}
|
||||
var_4: &cache_key_fallback v8-angular-node-16-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}
|
||||
|
||||
# Windows needs its own cache key because binaries in node_modules are different.
|
||||
var_3_win: &cache_key_win v8-angular-win-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}
|
||||
var_4_win: &cache_key_win_fallback v8-angular-win-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}
|
||||
var_3_win: &cache_key_win v10-angular-win-node-16-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}
|
||||
var_4_win: &cache_key_win_fallback v10-angular-win-node-16-{{ 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" }}-7a24e95bafbdeb697f74a48e275c2442bcbefc74
|
||||
var_6: &components_repo_unit_tests_cache_key_fallback v1-angular-components-{{ checksum "month.txt" }}
|
||||
var_5: &components_repo_unit_tests_cache_key v2-angular-components-{{ checksum "month.txt" }}-7a24e95bafbdeb697f74a48e275c2442bcbefc74
|
||||
var_6: &components_repo_unit_tests_cache_key_fallback v2-angular-components-{{ checksum "month.txt" }}
|
||||
|
||||
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages`.
|
||||
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
|
||||
|
||||
@@ -12,7 +12,7 @@ Info on remote development and developing inside a container with VSCode:
|
||||
|
||||
_Prerequisite: [Install Docker](https://docs.docker.com/install) on your local environment._
|
||||
|
||||
To get started, read and follow the instuctions in [Developing inside a Container](https://code.visualstudio.com/docs/remote/containers). The [.devcontainer/](.) directory contains pre-configured `devcontainer.json` and `Dockerfile` files, which you can use to set up remote development with a docker container.
|
||||
To get started, read and follow the instructions in [Developing inside a Container](https://code.visualstudio.com/docs/remote/containers). The [.devcontainer/](.) directory contains pre-configured `devcontainer.json` and `Dockerfile` files, which you can use to set up remote development with a docker container.
|
||||
|
||||
In a nutshell, you need to:
|
||||
- Install the [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension.
|
||||
|
||||
@@ -13,13 +13,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3.0.2
|
||||
- uses: angular/dev-infra/github-actions/commit-message-based-labels@8420d8593135df75b283e90beba0aa9b5b9838ce
|
||||
- uses: angular/dev-infra/github-actions/commit-message-based-labels@22d3067021130271afcfd02d063828c5bdd9c2d7
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
post_approval_changes:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3.0.2
|
||||
- uses: angular/dev-infra/github-actions/post-approval-changes@8420d8593135df75b283e90beba0aa9b5b9838ce
|
||||
- uses: angular/dev-infra/github-actions/post-approval-changes@22d3067021130271afcfd02d063828c5bdd9c2d7
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -14,6 +14,6 @@ jobs:
|
||||
if: github.repository == 'angular/angular'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/feature-request@8420d8593135df75b283e90beba0aa9b5b9838ce
|
||||
- uses: angular/dev-infra/github-actions/feature-request@22d3067021130271afcfd02d063828c5bdd9c2d7
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -14,6 +14,6 @@ jobs:
|
||||
if: github.repository == 'angular/angular'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/lock-closed@8420d8593135df75b283e90beba0aa9b5b9838ce
|
||||
- uses: angular/dev-infra/github-actions/lock-closed@22d3067021130271afcfd02d063828c5bdd9c2d7
|
||||
with:
|
||||
lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -45,6 +45,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: 'Upload to code-scanning'
|
||||
uses: github/codeql-action/upload-sarif@3f62b754e23e0dd60f91b744033e1dc1654c0ec6 # tag=v2.1.15
|
||||
uses: github/codeql-action/upload-sarif@2ca79b6fa8d3ec278944088b4aa5f46912db5d63 # tag=v2.1.18
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {CaretakerConfig} from '@angular/dev-infra-private/ng-dev';
|
||||
import {CaretakerConfig} from '@angular/ng-dev';
|
||||
|
||||
/** The configuration for `ng-dev caretaker` commands. */
|
||||
export const caretaker: CaretakerConfig = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {CommitMessageConfig} from '@angular/dev-infra-private/ng-dev';
|
||||
import {CommitMessageConfig} from '@angular/ng-dev';
|
||||
|
||||
/**
|
||||
* The configuration for `ng-dev commit-message` commands.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {FormatConfig} from '@angular/dev-infra-private/ng-dev';
|
||||
import {FormatConfig} from '@angular/ng-dev';
|
||||
|
||||
/**
|
||||
* Configuration for the `ng-dev format` command.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {GithubConfig} from '@angular/dev-infra-private/ng-dev';
|
||||
import {GithubConfig} from '@angular/ng-dev';
|
||||
|
||||
/**
|
||||
* Github configuration for the `ng-dev` command. This repository is used as
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {PullRequestConfig} from '@angular/dev-infra-private/ng-dev';
|
||||
import {PullRequestConfig} from '@angular/ng-dev';
|
||||
|
||||
/**
|
||||
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ReleaseConfig} from '@angular/dev-infra-private/ng-dev';
|
||||
import {ReleaseConfig} from '@angular/ng-dev';
|
||||
|
||||
/** Configuration for the `ng-dev release` command. */
|
||||
export const release: ReleaseConfig = {
|
||||
|
||||
@@ -58,13 +58,13 @@
|
||||
# josmar-crwdstffng
|
||||
# ileil
|
||||
# TeriGlover
|
||||
# TMDavisGoogle
|
||||
# bob-watson
|
||||
|
||||
version: 3
|
||||
|
||||
#availability:
|
||||
# users_unavailable:
|
||||
# - username
|
||||
availability:
|
||||
users_unavailable:
|
||||
- atscott # August 15 - September 20 (at least)
|
||||
|
||||
# Meta field that goes unused by PullApprove to allow for defining aliases to be
|
||||
# used throughout the config.
|
||||
@@ -240,6 +240,8 @@ groups:
|
||||
'aio/content/examples/component-styles/**/{*,.*}',
|
||||
'aio/content/examples/content-projection/**/{*,.*}',
|
||||
'aio/content/guide/content-projection.md',
|
||||
'aio/content/guide/creating-injectable-service.md',
|
||||
'aio/content/guide/dependency-injection-overview.md',
|
||||
'aio/content/guide/dependency-injection.md',
|
||||
'aio/content/examples/dependency-injection/**/{*,.*}',
|
||||
'aio/content/images/guide/dependency-injection/**/{*,.*}',
|
||||
@@ -1054,6 +1056,8 @@ groups:
|
||||
'aio/content/guide/docs-style-guide.md',
|
||||
'aio/content/examples/docs-style-guide/**/{*,.*}',
|
||||
'aio/content/images/guide/docs-style-guide/**/{*,.*}',
|
||||
'aio/content/guide/docs-lint-errors.md',
|
||||
'aio/content/images/guide/docs-lint-errors/**/{*,.*}',
|
||||
'aio/content/guide/localized-documentation.md',
|
||||
'aio/content/guide/localizing-angular.md',
|
||||
'aio/content/guide/reviewing-content.md',
|
||||
@@ -1062,8 +1066,9 @@ groups:
|
||||
])
|
||||
reviewers:
|
||||
users:
|
||||
- alxhub
|
||||
- AndrewKushnir
|
||||
- gkalpak
|
||||
- jessicajaniuk
|
||||
- josephperrott
|
||||
|
||||
# =========================================================
|
||||
@@ -1091,7 +1096,7 @@ groups:
|
||||
<<: *defaults
|
||||
conditions:
|
||||
- >
|
||||
contains_any_globs(files, [
|
||||
contains_any_globs(files.exclude('.pullapprove.yml'), [
|
||||
'{*,.*}',
|
||||
'.circleci/**/{*,.*}',
|
||||
'.devcontainer/**/{*,.*}',
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
// The following extensions are useful when working on angular.io (i.e. inside the `aio/` directory).
|
||||
//"angular.ng-template",
|
||||
//"dbaeumer.vscode-eslint",
|
||||
//"errata-ai.vale-server",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,3 +1,113 @@
|
||||
<a name="14.1.3"></a>
|
||||
# 14.1.3 (2022-08-17)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [0583227708](https://github.com/angular/angular/commit/05832277089f9df9cde424d62033661620826592) | fix | infinite loop in parser assignment expression with invalid left-hand expression ([#47151](https://github.com/angular/angular/pull/47151)) |
|
||||
## Special Thanks
|
||||
AlirezaEbrahimkhani, Alma Eyre, Andrew Scott, Bob Watson, George Kalpakas, Kalbarczyk, Kristiyan Kostadinov, Leosvel Pérez Espinosa, Roman Matusevich and Sonu Kapoor
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.2"></a>
|
||||
# 14.1.2 (2022-08-10)
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5ff715c549](https://github.com/angular/angular/commit/5ff715c5497fcbcbb33eb93770470fff5a816cd5) | fix | check if transplanted views are attached to change detector ([#46974](https://github.com/angular/angular/pull/46974)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [439d77e852](https://github.com/angular/angular/commit/439d77e852c2e08d384c420a2c3a962a804c60f1) | fix | Fix route recognition behavior with some versions of rxjs ([#47098](https://github.com/angular/angular/pull/47098)) ([#47112](https://github.com/angular/angular/pull/47112)) |
|
||||
## Special Thanks
|
||||
4javier, Andrew Kushnir, Andrew Scott, AntonioCardenas, Bob Watson, Bruno Barbosa, Eduardo Speroni, Edward, George Kalpakas, Jan Melcher, Kristiyan Kostadinov, Mladen Jakovljević, Paul Gschwendtner, Pawel Kozlowski, Roman Matusevich, Vovch, ashide2729, ileil and onrails
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.1"></a>
|
||||
# 14.1.1 (2022-08-03)
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [3606917732](https://github.com/angular/angular/commit/3606917732e5ad887426c1cbccf6e375eff905d6) | fix | improve the missing control flow directive message ([#46903](https://github.com/angular/angular/pull/46903)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [79825d3f10](https://github.com/angular/angular/commit/79825d3f1078c9b91eeb5e4699718ccde6c458b5) | fix | Do not call preload method when not necessary ([#47007](https://github.com/angular/angular/pull/47007)) |
|
||||
| [05f3f7445a](https://github.com/angular/angular/commit/05f3f7445a0bbe3c7ffed3aec308ca8888e77d99) | fix | Use correct return type for provideRoutes function ([#46941](https://github.com/angular/angular/pull/46941)) |
|
||||
## Special Thanks
|
||||
Alan Agius, Andrew Kushnir, Andrew Quinn, Andrew Scott, Aristeidis Bampakos, Asaf M, Bob Watson, Cédric Exbrayat, Durairaj Subramaniam, George Kalpakas, Ivaylo Kirov, J Rob Gant, Kristiyan Kostadinov, Marek Hám, Paul Gschwendtner, Roman Matusevich and Simona Cotin
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.0"></a>
|
||||
# 14.1.0 (2022-07-20)
|
||||
## Deprecations
|
||||
### core
|
||||
- The `createNgModuleRef` is deprecated in favor of newly added `createNgModule` one.
|
||||
- The bit field signature of `inject()` has been deprecated, in favor of the
|
||||
new options object. Correspondingly, `InjectFlags` is deprecated as well.
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [55308f2df5](https://github.com/angular/angular/commit/55308f2df5b0f6e8e40e3c4085c463a5b83c5ed1) | feat | add `provideAnimations()` and `provideNoopAnimations()` functions ([#46793](https://github.com/angular/angular/pull/46793)) |
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4a2e7335b1](https://github.com/angular/angular/commit/4a2e7335b180190800cf2c5db8ed1c8841dbf473) | feat | make the `CommonModule` pipes standalone ([#46401](https://github.com/angular/angular/pull/46401)) |
|
||||
| [a7597dd080](https://github.com/angular/angular/commit/a7597dd08026a4071758323d54ccbfb382e0c780) | feat | make the CommonModule directives standalone ([#46469](https://github.com/angular/angular/pull/46469)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [33ce3883a5](https://github.com/angular/angular/commit/33ce3883a5d962adf5ee7feffd3426feefe0c0b5) | feat | Add extended diagnostic to warn when missing let on ngForOf ([#46683](https://github.com/angular/angular/pull/46683)) |
|
||||
| [6f11a58040](https://github.com/angular/angular/commit/6f11a580406877e440c43df31fae3d5f120cafed) | feat | Add extended diagnostic to warn when text attributes are intended to be bindings ([#46161](https://github.com/angular/angular/pull/46161)) |
|
||||
| [9e836c232f](https://github.com/angular/angular/commit/9e836c232ff66043bc101b905dae4c8bde18bc58) | feat | warn when style suffixes are used with attribute bindings ([#46651](https://github.com/angular/angular/pull/46651)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [93c65e7b14](https://github.com/angular/angular/commit/93c65e7b1468bb0c696dec1bc3362422a2ca5170) | feat | add extended diagnostic for non-nullable optional chains ([#46686](https://github.com/angular/angular/pull/46686)) |
|
||||
| [131d029da1](https://github.com/angular/angular/commit/131d029da16b7d486cdc200f00160b9a11871fb2) | feat | detect missing control flow directive imports in standalone components ([#46146](https://github.com/angular/angular/pull/46146)) |
|
||||
| [6b8e60c06a](https://github.com/angular/angular/commit/6b8e60c06a4f9a5fdbce9e7971d403ccb2bc6e8e) | fix | improve the missingControlFlowDirective message ([#46846](https://github.com/angular/angular/pull/46846)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e8e8e5f171](https://github.com/angular/angular/commit/e8e8e5f1718f605c62a671b0ff15fa4dff7d8229) | feat | add `createComponent` function |
|
||||
| [b5153814af](https://github.com/angular/angular/commit/b5153814af3e6b1ec8550ddf39584efd7cca36be) | feat | add `reflectComponentType` function |
|
||||
| [96c6139c9a](https://github.com/angular/angular/commit/96c6139c9ab35aa6ab2330a5a79a5906d5c2e8be) | feat | add ability to set inputs on ComponentRef ([#46641](https://github.com/angular/angular/pull/46641)) |
|
||||
| [a6d5fe202c](https://github.com/angular/angular/commit/a6d5fe202cafb419f3beb8d09711132124b6aa9a) | feat | alias `createNgModuleRef` as `createNgModule` ([#46789](https://github.com/angular/angular/pull/46789)) |
|
||||
| [71e606d3c3](https://github.com/angular/angular/commit/71e606d3c3cfdcf93a6e40f84fd69aa75c5cca42) | feat | expose EnvironmentInjector on ApplicationRef ([#46665](https://github.com/angular/angular/pull/46665)) |
|
||||
| [19e6d9ccd3](https://github.com/angular/angular/commit/19e6d9ccd36c7776fcee7de541bba413fd6dcabf) | feat | import AsyncStackTaggingZone if available ([#46693](https://github.com/angular/angular/pull/46693)) |
|
||||
| [a7a14df5f8](https://github.com/angular/angular/commit/a7a14df5f8ff2d1e6a4dba3ac53f4479a6646810) | feat | introduce `EnvironmentInjector.runInContext` API ([#46653](https://github.com/angular/angular/pull/46653)) |
|
||||
| [fa52b6e906](https://github.com/angular/angular/commit/fa52b6e906e549e1c6b4a4f4428596d093fb0549) | feat | options object to supersede bit flags for `inject()` ([#46649](https://github.com/angular/angular/pull/46649)) |
|
||||
| [af20112222](https://github.com/angular/angular/commit/af20112222fcfa961ff9a2dee4050dd59aa4156e) | feat | support the descendants option for ContentChild queries ([#46638](https://github.com/angular/angular/pull/46638)) |
|
||||
| [945a3ad359](https://github.com/angular/angular/commit/945a3ad359cd2fe3456f635b0fe36effbfe8d5c2) | fix | Fix `runInContext` for `NgModuleRef` injector ([#46877](https://github.com/angular/angular/pull/46877)) |
|
||||
| [bb7c80477b](https://github.com/angular/angular/commit/bb7c80477b1258f48971b67b2eade6a4b41337f2) | fix | make parent injector argument required in `createEnvironmentInjector` ([#46397](https://github.com/angular/angular/pull/46397)) |
|
||||
### http
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [82acbf919b](https://github.com/angular/angular/commit/82acbf919bae2ddb6a46f41239aa2a101901f089) | feat | improve error message for nullish header ([#46059](https://github.com/angular/angular/pull/46059)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [53ca936366](https://github.com/angular/angular/commit/53ca936366fb908278571bae5fcc7fa08b19a5a0) | feat | Add ability to create `UrlTree` from any `ActivatedRouteSnapshot` ([#45877](https://github.com/angular/angular/pull/45877)) |
|
||||
| [de058bba99](https://github.com/angular/angular/commit/de058bba9979f68de2320118b537e6be4dbb888d) | feat | Add CanMatch guard to control whether a Route should match ([#46021](https://github.com/angular/angular/pull/46021)) |
|
||||
| [6c1357dd7d](https://github.com/angular/angular/commit/6c1357dd7d5f466575398ebc32c581068d9bf2df) | feat | Add stable cancelation code to `NavigationCancel` event ([#46675](https://github.com/angular/angular/pull/46675)) |
|
||||
| [a4ce273e50](https://github.com/angular/angular/commit/a4ce273e50551e6c6bc8f3a86591b04760de7ba4) | feat | Add the target `RouterStateSnapshot` to `NavigationError` ([#46731](https://github.com/angular/angular/pull/46731)) |
|
||||
| [abe3759e24](https://github.com/angular/angular/commit/abe3759e2417c0396d75e226edcef2fae99e363b) | fix | allow to return `UrlTree` from `CanMatchFn` ([#46455](https://github.com/angular/angular/pull/46455)) |
|
||||
| [e8c7dd10e9](https://github.com/angular/angular/commit/e8c7dd10e93d338456131e11d600bf758ee48634) | fix | Ensure `APP_INITIALIZER` of `enabledBlocking` option completes ([#46026](https://github.com/angular/angular/pull/46026)) |
|
||||
| [ce20ed067f](https://github.com/angular/angular/commit/ce20ed067f12714ee6488ffe07f1a1c18bb60a15) | fix | Ensure Route injector is created before running CanMatch guards ([#46394](https://github.com/angular/angular/pull/46394)) |
|
||||
| [6a7b818d94](https://github.com/angular/angular/commit/6a7b818d94fdd13d81a01285e3e4be953a29cc06) | fix | Ensure target `RouterStateSnapshot` is defined in `NavigationError` ([#46842](https://github.com/angular/angular/pull/46842)) |
|
||||
| [f94c6f433d](https://github.com/angular/angular/commit/f94c6f433dba3924b79f137cfcc49d2dfd4d679c) | fix | Expose CanMatchFn as public API ([#46394](https://github.com/angular/angular/pull/46394)) |
|
||||
| [e8ae0fe3e9](https://github.com/angular/angular/commit/e8ae0fe3e91e2e805146fad0d2f0976b1f886112) | fix | Fix cancellation code for canLoad rejections ([#46752](https://github.com/angular/angular/pull/46752)) |
|
||||
### upgrade
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e9cb0454dc](https://github.com/angular/angular/commit/e9cb0454dce2113cfd5ba11deeca6cdcf47d47f0) | feat | more closely align `UpgradeModule#bootstrap()` with `angular.bootstrap()` ([#46214](https://github.com/angular/angular/pull/46214)) |
|
||||
## Special Thanks
|
||||
AleksanderBodurri, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Cédric Exbrayat, Dmitrij Kuba, Dylan Hunn, George Kalpakas, Jessica Janiuk, JiaLiPassion, Joey Perrott, John Vandenberg, JoostK, Keith Li, Or'el Ben-Ya'ir, Paul Gschwendtner, Pawel Kozlowski, SyedAhm3r, arturovt, mariu, markostanimirovic and mgechev
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.0.6"></a>
|
||||
# 14.0.6 (2022-07-13)
|
||||
### compiler-cli
|
||||
@@ -18,33 +128,6 @@ Alan Agius, Andrew Scott, Bob Watson, Dylan Hunn, George Kalpakas, Ivaylo Kirov,
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.0-next.4"></a>
|
||||
# 14.1.0-next.4 (2022-07-06)
|
||||
## Deprecations
|
||||
### core
|
||||
- The bit field signature of `inject()` has been deprecated, in favor of the
|
||||
new options object. Correspondingly, `InjectFlags` is deprecated as well.
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [6f11a58040](https://github.com/angular/angular/commit/6f11a580406877e440c43df31fae3d5f120cafed) | feat | Add extended diagnostic to warn when text attributes are intended to be bindings ([#46161](https://github.com/angular/angular/pull/46161)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [96c6139c9a](https://github.com/angular/angular/commit/96c6139c9ab35aa6ab2330a5a79a5906d5c2e8be) | feat | add ability to set inputs on ComponentRef ([#46641](https://github.com/angular/angular/pull/46641)) |
|
||||
| [a7a14df5f8](https://github.com/angular/angular/commit/a7a14df5f8ff2d1e6a4dba3ac53f4479a6646810) | feat | introduce `EnvironmentInjector.runInContext` API ([#46653](https://github.com/angular/angular/pull/46653)) |
|
||||
| [fa52b6e906](https://github.com/angular/angular/commit/fa52b6e906e549e1c6b4a4f4428596d093fb0549) | feat | options object to supersede bit flags for `inject()` ([#46649](https://github.com/angular/angular/pull/46649)) |
|
||||
| [af20112222](https://github.com/angular/angular/commit/af20112222fcfa961ff9a2dee4050dd59aa4156e) | feat | support the descendants option for ContentChild queries ([#46638](https://github.com/angular/angular/pull/46638)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [6c1357dd7d](https://github.com/angular/angular/commit/6c1357dd7d5f466575398ebc32c581068d9bf2df) | feat | Add stable cancelation code to `NavigationCancel` event ([#46675](https://github.com/angular/angular/pull/46675)) |
|
||||
| [e8c7dd10e9](https://github.com/angular/angular/commit/e8c7dd10e93d338456131e11d600bf758ee48634) | fix | Ensure `APP_INITIALIZER` of `enabledBlocking` option completes ([#46026](https://github.com/angular/angular/pull/46026)) |
|
||||
## Special Thanks
|
||||
Alan Agius, Alex Rickabaugh, Andrew Scott, Aristeidis Bampakos, Bob Watson, George Kalpakas, Paul Gschwendtner and Pawel Kozlowski
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.0.5"></a>
|
||||
# 14.0.5 (2022-07-06)
|
||||
### router
|
||||
@@ -56,39 +139,6 @@ Alan Agius, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Aristeidis Bampakos,
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.0-next.3"></a>
|
||||
# 14.1.0-next.3 (2022-06-29)
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4eb1ca11f0](https://github.com/angular/angular/commit/4eb1ca11f0fbc7683115fc382420b3e75f248b4c) | fix | cleanup DOM elements when the root view is removed ([#45143](https://github.com/angular/angular/pull/45143)) |
|
||||
| [b417370383](https://github.com/angular/angular/commit/b41737038377af480763226a904b67e7d8e283ae) | fix | enable shadowElements to leave when their parent does ([#46459](https://github.com/angular/angular/pull/46459)) |
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4a2e7335b1](https://github.com/angular/angular/commit/4a2e7335b180190800cf2c5db8ed1c8841dbf473) | feat | make the `CommonModule` pipes standalone ([#46401](https://github.com/angular/angular/pull/46401)) |
|
||||
| [a7597dd080](https://github.com/angular/angular/commit/a7597dd08026a4071758323d54ccbfb382e0c780) | feat | make the CommonModule directives standalone ([#46469](https://github.com/angular/angular/pull/46469)) |
|
||||
| [1c39aace0e](https://github.com/angular/angular/commit/1c39aace0e74b02338729e5fb61e322ab06a9e14) | fix | handle CSS custom properties in NgStyle ([#46451](https://github.com/angular/angular/pull/46451)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8b6f4215a3](https://github.com/angular/angular/commit/8b6f4215a3c13a44d989d6068f6fb4625f3bab67) | fix | trigger `ApplicationRef.destroy` when Platform is destroyed ([#46497](https://github.com/angular/angular/pull/46497)) |
|
||||
| [606d94299a](https://github.com/angular/angular/commit/606d94299ac929243e8fa10fe7e00dee6d40064b) | fix | Update ngfor error code to be negative ([#46555](https://github.com/angular/angular/pull/46555)) |
|
||||
| [f86e0948f8](https://github.com/angular/angular/commit/f86e0948f80d398a8c4da9032372e083acc5d7a2) | fix | Updates error to use RuntimeError code ([#46526](https://github.com/angular/angular/pull/46526)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [0a5c8c0bc4](https://github.com/angular/angular/commit/0a5c8c0bc45ec58bcd297c7b001b88aec5c5e14d) | fix | Convert existing reactive errors to use RuntimeErrorCode. ([#46560](https://github.com/angular/angular/pull/46560)) |
|
||||
| [f6a096e3d4](https://github.com/angular/angular/commit/f6a096e3d4db9dfc31ab9228f37e119b87e5f8cf) | fix | Update a Forms validator error to use RuntimeError ([#46537](https://github.com/angular/angular/pull/46537)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [ee39cf62bc](https://github.com/angular/angular/commit/ee39cf62bc3f3d60d1dfe7064fe34565e5429b84) | fix | Ensure that new `RouterOutlet` instances work after old ones are destroyed ([#46554](https://github.com/angular/angular/pull/46554)) |
|
||||
## Special Thanks
|
||||
Alan Agius, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Bezael, Chad Ramos, Chellappan, Cédric Exbrayat, Dylan Hunn, George Kalpakas, Jeremy Meiss, Jessica Janiuk, Joey Perrott, KMathy, Kristiyan Kostadinov, Paul Gschwendtner, Pawel Kozlowski, Ramesh Thiruchelvam, Vaibhav Kumar, arturovt, dario-piotrowicz and renovate[bot]
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.0.4"></a>
|
||||
# 14.0.4 (2022-06-29)
|
||||
### animations
|
||||
@@ -120,37 +170,6 @@ Alan Agius, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Bezael, Chad Ramos, C
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.0-next.2"></a>
|
||||
# 14.1.0-next.2 (2022-06-22)
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [342effeb7d](https://github.com/angular/angular/commit/342effeb7d4d078ea51a7c345497794a7834b3c0) | fix | reset the start and done fns on player reset ([#46364](https://github.com/angular/angular/pull/46364)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [ddd6d66203](https://github.com/angular/angular/commit/ddd6d662038336f9a8e7d68a8b5cda2c3ca1c1d7) | fix | deduplicate imports of standalone components in JIT compiler ([#46439](https://github.com/angular/angular/pull/46439)) |
|
||||
| [3d8787c961](https://github.com/angular/angular/commit/3d8787c961176e15a7583d415f778054378ea06e) | fix | handle NgModules with standalone pipes in TestBed correctly ([#46407](https://github.com/angular/angular/pull/46407)) |
|
||||
| [bb7c80477b](https://github.com/angular/angular/commit/bb7c80477b1258f48971b67b2eade6a4b41337f2) | fix | make parent injector argument required in `createEnvironmentInjector` ([#46397](https://github.com/angular/angular/pull/46397)) |
|
||||
### http
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [82acbf919b](https://github.com/angular/angular/commit/82acbf919bae2ddb6a46f41239aa2a101901f089) | feat | improve error message for nullish header ([#46059](https://github.com/angular/angular/pull/46059)) |
|
||||
### platform-server
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [08d3db232c](https://github.com/angular/angular/commit/08d3db232cb758701c41277996f1b41773f98e0d) | fix | invalid style attribute being generated for null values ([#46433](https://github.com/angular/angular/pull/46433)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [abe3759e24](https://github.com/angular/angular/commit/abe3759e2417c0396d75e226edcef2fae99e363b) | fix | allow to return `UrlTree` from `CanMatchFn` ([#46455](https://github.com/angular/angular/pull/46455)) |
|
||||
| [ce20ed067f](https://github.com/angular/angular/commit/ce20ed067f12714ee6488ffe07f1a1c18bb60a15) | fix | Ensure Route injector is created before running CanMatch guards ([#46394](https://github.com/angular/angular/pull/46394)) |
|
||||
| [f94c6f433d](https://github.com/angular/angular/commit/f94c6f433dba3924b79f137cfcc49d2dfd4d679c) | fix | Expose CanMatchFn as public API ([#46394](https://github.com/angular/angular/pull/46394)) |
|
||||
## Special Thanks
|
||||
4javier, Aakash, Alan Agius, Andrew Kushnir, Andrew Scott, Aristeidis Bampakos, Dany Paredes, Derek Cormier, Dmitrij Kuba, JoostK, Kristiyan Kostadinov, Paul Gschwendtner, Ramesh Thiruchelvam, arturovt, behrooz bozorg chami, dario-piotrowicz, markostanimirovic, renovate[bot] and web-dave
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.0.3"></a>
|
||||
# 14.0.3 (2022-06-22)
|
||||
### animations
|
||||
@@ -171,31 +190,6 @@ Alan Agius, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Bezael, Chad Ramos, C
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.0-next.1"></a>
|
||||
# 14.1.0-next.1 (2022-06-15)
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8b9c32d4dc](https://github.com/angular/angular/commit/8b9c32d4dccca5417adce1ca6067adf0805d6d08) | fix | allow null in ngComponentOutlet ([#46280](https://github.com/angular/angular/pull/46280)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [131d029da1](https://github.com/angular/angular/commit/131d029da16b7d486cdc200f00160b9a11871fb2) | feat | detect missing control flow directive imports in standalone components ([#46146](https://github.com/angular/angular/pull/46146)) |
|
||||
| [04acc6b14d](https://github.com/angular/angular/commit/04acc6b14d502597a98537019822f1a1217c9a61) | fix | don't emit empty providers array ([#46301](https://github.com/angular/angular/pull/46301)) |
|
||||
| [f35f4751b5](https://github.com/angular/angular/commit/f35f4751b54dcb9e65f2d72aeea87651314159e8) | fix | use inline type-check blocks for components outside `rootDir` ([#46096](https://github.com/angular/angular/pull/46096)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [b1a3dec58d](https://github.com/angular/angular/commit/b1a3dec58d0dc21d76f757d4085c7581d89dc7dd) | fix | Resolve forwardRef declarations for jit ([#46334](https://github.com/angular/angular/pull/46334)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [de058bba99](https://github.com/angular/angular/commit/de058bba9979f68de2320118b537e6be4dbb888d) | feat | Add CanMatch guard to control whether a Route should match ([#46021](https://github.com/angular/angular/pull/46021)) |
|
||||
## Special Thanks
|
||||
Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Badawi7, Daniel Schmitz, Derek Cormier, Jessica Janiuk, JiaLiPassion, JoostK, Kevin Davila, Kristiyan Kostadinov, Paul Draper, Paul Gschwendtner, Tom Eustace, Totati, Younes Jaaidi, alefra, dario-piotrowicz, markostanimirovic, mgechev, piyush132000, sten-bone and vivekkoya
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.0.2"></a>
|
||||
# 14.0.2 (2022-06-15)
|
||||
### common
|
||||
@@ -216,21 +210,6 @@ Alex Rickabaugh, Andrew Scott, Badawi7, Daniel Schmitz, Derek Cormier, JoostK, K
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.1.0-next.0"></a>
|
||||
# 14.1.0-next.0 (2022-06-08)
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [53ca936366](https://github.com/angular/angular/commit/53ca936366fb908278571bae5fcc7fa08b19a5a0) | feat | Add ability to create `UrlTree` from any `ActivatedRouteSnapshot` ([#45877](https://github.com/angular/angular/pull/45877)) |
|
||||
### upgrade
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e9cb0454dc](https://github.com/angular/angular/commit/e9cb0454dce2113cfd5ba11deeca6cdcf47d47f0) | feat | more closely align `UpgradeModule#bootstrap()` with `angular.bootstrap()` ([#46214](https://github.com/angular/angular/pull/46214)) |
|
||||
## Special Thanks
|
||||
AleksanderBodurri, Alex Rickabaugh, Andrew Kushnir, Andrew Scott, George Kalpakas, Jessica Janiuk, Joey Perrott, Keith Li, SyedAhm3r, mariu and mgechev
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="14.0.1"></a>
|
||||
# 14.0.1 (2022-06-08)
|
||||
### bazel
|
||||
|
||||
@@ -123,7 +123,7 @@ load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories"
|
||||
|
||||
web_test_repositories()
|
||||
|
||||
load("@npm//@angular/dev-infra-private/bazel/browsers:browser_repositories.bzl", "browser_repositories")
|
||||
load("@npm//@angular/build-tooling/bazel/browsers:browser_repositories.bzl", "browser_repositories")
|
||||
|
||||
browser_repositories()
|
||||
|
||||
@@ -158,10 +158,10 @@ cldr_xml_data_repository(
|
||||
# sass rules
|
||||
http_archive(
|
||||
name = "io_bazel_rules_sass",
|
||||
sha256 = "2f86f221636cf721bd118ac954d0c982ef16cb3f795794ce17a5e0b7e62e103b",
|
||||
strip_prefix = "rules_sass-eda73f679f50d1d30599cebb8cc1bbc5ae43d5c6",
|
||||
sha256 = "926cdd6133926a69e557172cb3232d5dac64ec6b8f167b7dceecd96cc4c6f19b",
|
||||
strip_prefix = "rules_sass-6802532fce57dee2a17be221d82cf441a923aeb7",
|
||||
urls = [
|
||||
"https://github.com/bazelbuild/rules_sass/archive/eda73f679f50d1d30599cebb8cc1bbc5ae43d5c6.zip",
|
||||
"https://github.com/bazelbuild/rules_sass/archive/6802532fce57dee2a17be221d82cf441a923aeb7.zip",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
@name `inject()` must be called from an injection context
|
||||
@category runtime
|
||||
@shortDescription `inject()` must be called from an injection context (a constructor, a factory function or a field initializer)
|
||||
@shortDescription `inject()` must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with `EnvironmentInjector#runInContext`.
|
||||
|
||||
@description
|
||||
You see this error when you try to use the `inject()` function outside of the allowed injection context. The injection context is available during the class creation and initialization.
|
||||
You see this error when you try to use the `inject()` function outside of the allowed injection context. The injection context is available during the class creation and initialization. It is also available to functions
|
||||
used with `EnvironmentInjector#runInContext`.
|
||||
|
||||
In practice the `inject()` calls are allowed in a constructor, a constructor parameter and a field initializer:
|
||||
|
||||
@@ -34,7 +35,7 @@ providers: [
|
||||
]
|
||||
```
|
||||
|
||||
Calls to the `inject()` function outside of the class creation context will result in error. Most notably, calls to `inject()` are disallowed after a class instance was created, in methods (including lifecycle hooks):
|
||||
Calls to the `inject()` function outside of the class creation or `runInContext` will result in error. Most notably, calls to `inject()` are disallowed after a class instance was created, in methods (including lifecycle hooks):
|
||||
|
||||
```typescript
|
||||
@Component({ ... })
|
||||
|
||||
@@ -13,26 +13,18 @@ export abstract class Parent { abstract name: string; }
|
||||
|
||||
const DifferentParent = Parent;
|
||||
|
||||
// #docregion provide-parent, provide-the-parent
|
||||
// Helper method to provide the current component instance in the name of a `parentType`.
|
||||
// #enddocregion provide-the-parent
|
||||
// The `parentType` defaults to `Parent` when omitting the second parameter.
|
||||
// #docregion provide-the-parent
|
||||
export function provideParent
|
||||
// #enddocregion provide-the-parent
|
||||
(component: any, parentType?: any) {
|
||||
return { provide: parentType || Parent, useExisting: forwardRef(() => component) };
|
||||
}
|
||||
// #enddocregion provide-parent
|
||||
|
||||
// Simpler syntax version that always provides the component in the name of `Parent`.
|
||||
export function provideTheParent
|
||||
// #docregion provide-the-parent
|
||||
(component: any) {
|
||||
return { provide: Parent, useExisting: forwardRef(() => component) };
|
||||
}
|
||||
// #enddocregion provide-the-parent
|
||||
|
||||
|
||||
///////// C - Child //////////
|
||||
const templateC = `
|
||||
@@ -119,9 +111,7 @@ export class BobComponent implements Parent {
|
||||
@Component({
|
||||
selector: 'beth',
|
||||
template: templateB,
|
||||
// #docregion beth-providers
|
||||
providers: [ provideParent(BethComponent, DifferentParent) ]
|
||||
// #enddocregion beth-providers
|
||||
})
|
||||
export class BethComponent implements Parent {
|
||||
name = 'Beth';
|
||||
@@ -169,9 +159,7 @@ export class AlexComponent extends Base
|
||||
<bob></bob>
|
||||
<carol></carol>
|
||||
</div> `,
|
||||
// #docregion alice-providers
|
||||
providers: [ provideParent(AliceComponent) ]
|
||||
// #enddocregion alice-providers
|
||||
})
|
||||
// #docregion alice-class-signature
|
||||
export class AliceComponent implements Parent
|
||||
|
||||
@@ -7,7 +7,6 @@ import { InjectionToken } from '@angular/core';
|
||||
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
|
||||
// #enddocregion token
|
||||
|
||||
// #docregion config
|
||||
export const HERO_DI_CONFIG: AppConfig = {
|
||||
apiEndpoint: 'api.heroes.com',
|
||||
title: 'Dependency Injection'
|
||||
|
||||
@@ -29,15 +29,11 @@ import { ProvidersModule } from './providers.module';
|
||||
InjectorComponent,
|
||||
TestComponent
|
||||
],
|
||||
// #docregion providers
|
||||
providers: [
|
||||
// #enddocregion providers
|
||||
Logger,
|
||||
// #docregion providers
|
||||
UserService,
|
||||
{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }
|
||||
],
|
||||
// #enddocregion providers
|
||||
exports: [ CarComponent, HeroesComponent ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
|
||||
@@ -21,9 +21,7 @@ import { Logger } from './logger.service';
|
||||
export class InjectorComponent {
|
||||
car: Car;
|
||||
|
||||
// #docregion get-hero-service
|
||||
heroService: HeroService;
|
||||
// #enddocregion get-hero-service
|
||||
hero: Hero;
|
||||
|
||||
constructor(private injector: Injector) {
|
||||
|
||||
@@ -154,7 +154,6 @@ export class Provider6bComponent {
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
||||
// #docregion silent-logger
|
||||
// An object in the shape of the logger service
|
||||
function silentLoggerFn() {}
|
||||
|
||||
@@ -162,15 +161,12 @@ export const SilentLogger = {
|
||||
logs: ['Silent logger says "Shhhhh!". Provided via "useValue"'],
|
||||
log: silentLoggerFn
|
||||
};
|
||||
// #enddocregion silent-logger
|
||||
|
||||
@Component({
|
||||
selector: 'provider-7',
|
||||
template,
|
||||
providers:
|
||||
// #docregion providers-7
|
||||
[{ provide: Logger, useValue: SilentLogger }]
|
||||
// #enddocregion providers-7
|
||||
})
|
||||
export class Provider7Component {
|
||||
log: string;
|
||||
|
||||
@@ -7,6 +7,5 @@ import { Component } from '@angular/core';
|
||||
})
|
||||
export class CartComponent {
|
||||
|
||||
constructor() { }
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
// #docplaster
|
||||
/*
|
||||
// #docregion as-generated
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
// #enddocregion as-generated
|
||||
*/
|
||||
// #docregion imports
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Product } from '../products';
|
||||
// #enddocregion imports
|
||||
// #docregion as-generated
|
||||
@@ -16,14 +11,10 @@ import { Product } from '../products';
|
||||
styleUrls: ['./product-alerts.component.css']
|
||||
})
|
||||
// #docregion input-decorator
|
||||
export class ProductAlertsComponent implements OnInit {
|
||||
export class ProductAlertsComponent {
|
||||
|
||||
// #enddocregion as-generated
|
||||
@Input() product!: Product;
|
||||
// #docregion as-generated
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,16 +17,7 @@
|
||||
.activebutton {
|
||||
box-shadow: inset 0 1px 0 0 #dcecfb;
|
||||
background: #bddbfa linear-gradient(to bottom, #bddbfa 5%, #80b5ea 100%);
|
||||
border-radius: 6px;
|
||||
border: 1px solid #84bbf3;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
color: #ffffff;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
padding: 6px 24px;
|
||||
text-decoration: none;
|
||||
text-shadow: 0 1px 0 #528ecc;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ import { ProfileComponent } from './profile/profile.component';
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
RouterModule.forRoot([
|
||||
{
|
||||
// #enddocregion imports-array
|
||||
// #docregion matcher
|
||||
{
|
||||
matcher: (url) => {
|
||||
if (url.length === 1 && url[0].path.match(/^@[\w]+$/gm)) {
|
||||
return {
|
||||
|
||||
@@ -16,6 +16,6 @@ nav a:hover {
|
||||
color: white;
|
||||
background-color: #42545C;
|
||||
}
|
||||
nav a.active {
|
||||
nav a:active {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ In the following example, the `HeroListComponent` class includes:
|
||||
* A `selectedHero` property that holds the last hero selected by the user.
|
||||
* A `selectHero()` method sets a `selectedHero` property when the user clicks to choose a hero from that list.
|
||||
|
||||
The component initializes the `heroes` property by using the `HeroService` service, which is a TypeScript [parameter property](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor. Angular's dependency injection system provides the `HeroService` service to the component.
|
||||
The component initializes the `heroes` property by using the `HeroService` service, which is a TypeScript [parameter property](https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties) on the constructor. Angular's dependency injection system provides the `HeroService` service to the component.
|
||||
|
||||
<code-example header="src/app/hero-list.component.ts (class)" path="architecture/src/app/hero-list.component.ts" region="class"></code-example>
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ import * as Plotly from 'plotly.js-dist-min';
|
||||
class AppComponent implements OnInit {
|
||||
constructor(private ngZone: NgZone) {}
|
||||
ngOnInit() {
|
||||
this.zone.runOutsideAngular(() => {
|
||||
this.ngZone.runOutsideAngular(() => {
|
||||
Plotly.newPlot('chart', data);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ The `CountdownLocalVarParentComponent` that hosts the timer component is as foll
|
||||
|
||||
The parent component cannot data bind to the child's `start` and `stop` methods nor to its `seconds` property.
|
||||
|
||||
Place a local variable, `#timer`, on the tag `<countdown-timer>` representing the child component.
|
||||
Place a local variable, `#timer`, on the tag `<app-countdown-timer>` representing the child component.
|
||||
That gives you a reference to the child component and the ability to access *any of its properties or methods* from within the parent template.
|
||||
|
||||
This example wires parent buttons to the child's `start` and `stop` and uses interpolation to display the child's `seconds` property.
|
||||
|
||||
@@ -1,14 +1,85 @@
|
||||
# Content Contributor's Guide
|
||||
# Documentation contributor guide
|
||||
|
||||
Angular, as an open source project, depends on its community. This dependence is particularly important to the documentation. The more the community contributes to the documentation, the better the documentation becomes, which helps both new and experienced Angular developers.
|
||||
The topics in this section describe how you can contribute to this documentation.
|
||||
For information about contributing only code to the Angular framework, see [Contributing to Angular][GithubAngularAngularBlobMainContributingMd].
|
||||
|
||||
The topics in this section cover ways in which you can contribute to the Angular documentation set.
|
||||
Angular, an open source project, depends on its community, especially when it comes to the documentation.
|
||||
The more the community contributes to the documentation, the better the documentation becomes.
|
||||
Input from you helps both new and experienced Angular developers.
|
||||
|
||||
## Before you begin
|
||||
<div class="alert is-important">
|
||||
|
||||
Before you get started with your contributions, we recommend that you review [Contributing to Angular](https://github.com/angular/angular/blob/main/CONTRIBUTING.md). That topic explains many of the tasks and guidelines you need to know before you make your first pull request.
|
||||
You must have a GitHub account to propose a minor change or review and edit the documentation.
|
||||
|
||||
## Contributing to Angular
|
||||
</div>
|
||||
|
||||
## Propose a minor change
|
||||
|
||||
You can propose minor changes to a documentation topic without downloading any software. Choose the pencil icon <img alt="drawing of a pencil used as the topic edit icon" src="generated/images/guide/contributors-guide/edit-icon.png" > at the top of the page, to the right of the topic title to enter your suggestion.
|
||||
|
||||
## Review and edit documentation topics
|
||||
|
||||
This section describes how to make larger contributions to the documentation.
|
||||
|
||||
### Before you start
|
||||
|
||||
The following steps set up your local computer to contribute documentation content.
|
||||
|
||||
1. Sign into your account on GitHub.
|
||||
If you don't have an account on GitHub, [create a new account][GithubJoin] before you continue.
|
||||
2. In your browser, navigate to [https://github.com/angular/angular][GithubAngular].
|
||||
3. In your browser, fork the `angular/angular` repo into your GitHub account.
|
||||
1. From [https://github.com/angular/angular][GithubAngular] in the browser, choose the **Fork** button near the top-right corner of the page.
|
||||
2. If asked, choose to create a fork to contribute to the original project.
|
||||
4. On the computer that you want to edit, clone your fork of the `angular` repo.
|
||||
5. In the `angular/aio` directory of your cloned repo, locally build and view the documentation.
|
||||
1. Run the following commands.
|
||||
To learn more, see [Angular documentation project][GithubAngularAngularBlobMainAioReadmeMd].
|
||||
|
||||
<code-example format="shell" language="shell">
|
||||
|
||||
yarn
|
||||
|
||||
</code-example>
|
||||
|
||||
<code-example format="shell" language="shell">
|
||||
|
||||
yarn setup
|
||||
|
||||
</code-example>
|
||||
|
||||
<code-example format="shell" language="shell">
|
||||
|
||||
yarn build
|
||||
|
||||
</code-example>
|
||||
|
||||
<code-example format="shell" language="shell">
|
||||
|
||||
yarn start
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="callout is-helpful">
|
||||
|
||||
**NOTE**: <br/>
|
||||
If you have any errors or issues with your yarn install, just re-run the `yarn` command.
|
||||
|
||||
</div>
|
||||
|
||||
2. In your browser, navigate to [http://localhost:4200][AngularLocalhost]
|
||||
3. Review your local build of the Angular framework documentation.
|
||||
|
||||
After you can see your local build of the Angular framework documentation on your own system, you are ready to review and edit it. The following sections help you with the next steps.
|
||||
|
||||
### Before you edit the documentation
|
||||
|
||||
The following steps prepare you to edit the Angular documentation successfully:
|
||||
|
||||
1. Review [Contributing to Angular][GithubAngularAngularBlobMainContributingMd].
|
||||
2. Install [the documentation linter][GithubAngularAngularTreeMainAioToolsDocLinterInstallValeOnYourDevelopmentSystemReadmeMd].
|
||||
|
||||
### Ways to contribute to Angular documentation
|
||||
|
||||
<div class="card-container">
|
||||
<a href="guide/reviewing-content" class="docs-card" title="Reviewing content">
|
||||
@@ -26,6 +97,11 @@ Before you get started with your contributions, we recommend that you review [Co
|
||||
<p>Learn how to make documentation changes through the GitHub UI.</p>
|
||||
<p class="card-footer">Contribute to Angular in your browser</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### More resources for contributing to documentation
|
||||
|
||||
<div class="card-container">
|
||||
<a href="guide/docs-style-guide" class="docs-card" title="Documentation Style Guide">
|
||||
<section>Documentation style guide</section>
|
||||
<p>Review the syntax and styles used within the Angular documentation set.</p>
|
||||
@@ -37,3 +113,23 @@ Before you get started with your contributions, we recommend that you review [Co
|
||||
<p class="card-footer">Localize documentation</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- links -->
|
||||
|
||||
<!-- external links -->
|
||||
|
||||
[GithubJoin]: https://github.com/join "Join GitHub | GitHub"
|
||||
|
||||
[GithubAngular]: https://github.com/angular/angular "angular/angular | GitHub"
|
||||
|
||||
[GithubAngularAngularBlobMainAioReadmeMd]: https://github.com/angular/angular/blob/main/aio/README.md "Angular documentation project (https://angular.io) | angular/angular | Github"
|
||||
|
||||
[GithubAngularAngularBlobMainContributingMd]: https://github.com/angular/angular/blob/main/CONTRIBUTING.md "Contributing to Angular | angular/angular | Github"
|
||||
|
||||
[GithubAngularAngularTreeMainAioToolsDocLinterInstallValeOnYourDevelopmentSystemReadmeMd]: https://github.com/angular/angular/tree/main/aio/tools/doc-linter/README.md#install-vale-on-your-development-system "Install Vale on your development system - Angular documentation lint tool | angular/angular | Github"
|
||||
|
||||
[AngularLocalhost]: http://localhost:4200 "Angular.io on localhost"
|
||||
|
||||
<!--end links -->
|
||||
|
||||
@reviewed 2022-08-08
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
# Creating an injectable service
|
||||
|
||||
Service is a broad category encompassing any value, function, or feature that an application needs. A service is typically a class with a narrow, well-defined purpose. A component is one type of class that can use DI.
|
||||
|
||||
Angular distinguishes components from services to increase modularity and reusability. By separating a component's view-related features from other kinds of processing, you can make your component classes lean and efficient.
|
||||
|
||||
Ideally, a component's job is to enable the user experience and nothing more. A component should present properties and methods for data binding, to mediate between the view (rendered by the template) and the application logic (which often includes some notion of a model).
|
||||
|
||||
A component can delegate certain tasks to services, such as fetching data from the server, validating user input, or logging directly to the console. By defining such processing tasks in an injectable service class, you make those tasks available to any component. You can also make your application more adaptable by injecting different providers of the same kind of service, as appropriate in different circumstances.
|
||||
|
||||
Angular does not enforce these principles. Angular helps you follow these principles by making it easy to factor your application logic into services and make those services available to components through DI.
|
||||
|
||||
## Service examples
|
||||
|
||||
Here's an example of a service class that logs to the browser console.
|
||||
|
||||
<code-example header="src/app/logger.service.ts (class)" path="architecture/src/app/logger.service.ts" region="class"></code-example>
|
||||
|
||||
Services can depend on other services.
|
||||
For example, here's a `HeroService` that depends on the `Logger` service, and also uses `BackendService` to get heroes.
|
||||
That service in turn might depend on the `HttpClient` service to fetch heroes asynchronously from a server.
|
||||
|
||||
<code-example header="src/app/hero.service.ts (class)" path="architecture/src/app/hero.service.ts" region="class"></code-example>
|
||||
|
||||
## Creating an injectable service
|
||||
|
||||
Angular CLI provides a command to create a new service. In the following example, you add a new service to your application, which was created earlier with the `ng new` command.
|
||||
|
||||
To generate a new `HeroService` class in the `src/app/heroes` folder, follow these steps:
|
||||
|
||||
1. Run this [Angular CLI](cli) command:
|
||||
|
||||
<code-example language="sh">
|
||||
ng generate service heroes/hero
|
||||
</code-example>
|
||||
|
||||
This command creates the following default `HeroService`.
|
||||
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.0.ts" header="src/app/heroes/hero.service.ts (CLI-generated)">
|
||||
</code-example>
|
||||
|
||||
The `@Injectable()` decorator specifies that Angular can use this class in the DI system.
|
||||
The metadata, `providedIn: 'root'`, means that the `HeroService` is visible throughout the application.
|
||||
|
||||
2. Add a `getHeroes()` method that returns the heroes from `mock.heroes.ts` to get the hero mock data:
|
||||
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.3.ts" header="src/app/heroes/hero.service.ts">
|
||||
</code-example>
|
||||
|
||||
For clarity and maintainability, it is recommended that you define components and services in separate files.
|
||||
|
||||
## Injecting services
|
||||
|
||||
To inject a service as a dependency into a component, you can use component's `constructor()` and supply a constructor argument with the dependency type. The following example specifies the `HeroService` in the `HeroListComponent` constructor. The type of the `heroService` is `HeroService`. Angular recognizes the `HeroService` as a dependency, since that class was previously annotated with the `@Injectable` decorator.
|
||||
|
||||
<code-example header="src/app/heroes/hero-list.component (constructor signature)" path="dependency-injection/src/app/heroes/hero-list.component.ts"
|
||||
region="ctor-signature">
|
||||
</code-example>
|
||||
|
||||
## Injecting services in other services
|
||||
|
||||
When a service depends on another service, follow the same pattern as injecting into a component.
|
||||
In the following example `HeroService` depends on a `Logger` service to report its activities.
|
||||
|
||||
First, import the `Logger` service. Next, inject the `Logger` service in the `HeroService` `constructor()` by specifying `private logger: Logger`.
|
||||
|
||||
Here, the `constructor()` specifies a type of `Logger` and stores the instance of `Logger` in a private field called `logger`.
|
||||
|
||||
The following code tabs feature the `Logger` service and two versions of `HeroService`. The first version of `HeroService` does not depend on the `Logger` service. The revised second version does depend on `Logger` service.
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane header="src/app/heroes/hero.service (v2)" path="dependency-injection/src/app/heroes/hero.service.2.ts">
|
||||
</code-pane>
|
||||
|
||||
<code-pane header="src/app/heroes/hero.service (v1)" path="dependency-injection/src/app/heroes/hero.service.1.ts">
|
||||
</code-pane>
|
||||
|
||||
<code-pane header="src/app/logger.service"
|
||||
path="dependency-injection/src/app/logger.service.ts">
|
||||
</code-pane>
|
||||
|
||||
</code-tabs>
|
||||
|
||||
In this example, the `getHeroes()` method uses the `Logger` service by logging a message when fetching heroes.
|
||||
|
||||
## What's next
|
||||
|
||||
* [How to configure dependencies in DI](guide/dependency-injection-providers)
|
||||
* [How to use `InjectionTokens` to provide and inject values other than services/classes](guide/dependency-injection-providers#configuring-dependency-providers)
|
||||
* [Dependency Injection in Action](guide/dependency-injection-in-action)
|
||||
|
||||
@reviewed 2022-08-02
|
||||
@@ -213,7 +213,7 @@ It's visually simple: a few properties and the logs produced by a logger.
|
||||
</div>
|
||||
|
||||
The code behind it customizes how and where the DI framework provides dependencies.
|
||||
The use cases illustrate different ways to use the [*provide* object literal](guide/dependency-injection-providers#provide) to associate a definition object with a DI token.
|
||||
The use cases illustrate different ways to use the *provide* object literal to associate a definition object with a DI token.
|
||||
|
||||
<code-example header="hero-of-the-month.component.ts" path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="hero-of-the-month"></code-example>
|
||||
|
||||
@@ -389,8 +389,10 @@ But they did neither.
|
||||
|
||||
When you use a class this way, it's called a *class interface*.
|
||||
|
||||
As mentioned in [DI Providers](guide/dependency-injection-providers#di-and-interfaces), an interface is not a valid DI token because it is a TypeScript artifact that doesn't exist at run time.
|
||||
Use this abstract class interface to get the strong typing of an interface, and also use it as a provider token in the way you would a normal class.
|
||||
As mentioned in [Configuring dependency providers](guide/dependency-injection-providers),
|
||||
an interface is not a valid DI token because it is a TypeScript artifact that doesn't exist at run time.
|
||||
Use this abstract class interface to get the strong typing of an interface,
|
||||
and also use it as a provider token in the way you would a normal class.
|
||||
|
||||
A class interface should define *only* the members that its consumers are allowed to call.
|
||||
Such a narrowing interface helps decouple the concrete class from its consumers.
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Dependency injection in Angular
|
||||
|
||||
When you develop a smaller part of your system, like a module or a class, you may need to use features from other classes. For example, you may need an HTTP service to make backend calls. Dependency Injection, or DI, is a design pattern and mechanism for creating and delivering some parts of an application to other parts of an application that require them. Angular supports this design pattern and you can use it in your applications to increase flexibility and modularity.
|
||||
|
||||
In Angular, dependencies are typically services, but they also can be values, such as strings or functions. An injector for an application (created automatically during bootstrap) instantiates dependencies when needed, using a configured provider of the service or value.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
See the <live-example></live-example> for a working example containing the code snippets in this guide.
|
||||
|
||||
</div>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You should be familiar with the Angular apps in general, and have the fundamental knowledge of Components, Directives, and NgModules. It's highly recommended that you complete the following tutorial:
|
||||
|
||||
[Tour of Heroes application and tutorial](tutorial)
|
||||
|
||||
## Learn about Angular dependency injection
|
||||
|
||||
<div class="card-container">
|
||||
<a href="guide/dependency-injection" class="docs-card" title="Understanding dependency injection">
|
||||
<section>Understanding dependency injection</section>
|
||||
<p>Learn basic principles of dependency injection in Angular.</p>
|
||||
<p class="card-footer">Understanding dependency injection</p>
|
||||
</a>
|
||||
<a href="guide/creating-injectable-service" class="docs-card" title="Creating and injecting service">
|
||||
<section>Creating and injecting service</section>
|
||||
<p>Describes how to create a service and inject it in other services and components.</p>
|
||||
<p class="card-footer">Creating an injectable service</p>
|
||||
</a>
|
||||
<a href="guide/dependency-injection-providers" class="docs-card" title="Configuring dependency providers">
|
||||
<section>Configuring dependency providers</section>
|
||||
<p>Describes how to configure dependencies using the providers field on the @Component and @NgModule decorators. Also describes how to use InjectionToken to provide and inject values in DI, which can be helpful when you want to use a value other than classes as dependencies.</p>
|
||||
<p class="card-footer">Configuring dependency providers</p>
|
||||
</a>
|
||||
<a href="guide/hierarchical-dependency-injection" class="docs-card" title="Hierarchical injectors">
|
||||
<section>Hierarchical injectors</section>
|
||||
<p>Hierarchical DI enables you to share dependencies between different parts of the application only when and if you need to. This is an advanced topic.</p>
|
||||
<p class="card-footer">Hierarchical injectors</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@reviewed 2022-08-02
|
||||
@@ -1,179 +1,116 @@
|
||||
# Dependency providers
|
||||
# Configuring dependency providers
|
||||
|
||||
By configuring providers, you can make services available to the parts of your application that need them.
|
||||
|
||||
A dependency [provider](guide/glossary#provider) configures an injector with a [DI token](guide/glossary#di-token), which that injector uses to provide the runtime version of a dependency value.
|
||||
The Creating and injecting services topic describes how to use classes as dependencies. Besides classes, you can also use other values such as Boolean, string, date, and objects as dependencies. Angular DI provides the necessary APIs to make the dependency configuration flexible, so you can make those values available in DI.
|
||||
|
||||
## Specifying a provider token
|
||||
|
||||
If you specify the service class as the provider token, the default behavior is for the injector to instantiate that class with `new`.
|
||||
If you specify the service class as the provider token, the default behavior is for the injector to instantiate that class using the `new` operator.
|
||||
|
||||
In the following example, the `Logger` class provides a `Logger` instance.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger"></code-example>
|
||||
|
||||
You can, however, configure an injector with an alternative provider to deliver some other object that provides the needed logging functionality.
|
||||
You can, however, configure a DI to use a different class or any other different value to associate with the `Logger` class. So when the `Logger` is injected, this new value is used instead.
|
||||
|
||||
Configure an injector with a service class, and provide a substitute class, an object, or a factory function.
|
||||
In fact, the class provider syntax is a shorthand expression that expands into a provider configuration, defined by the `Provider` interface.
|
||||
|
||||
<a id="token"></a>
|
||||
<a id="injection-token"></a>
|
||||
|
||||
## Dependency injection tokens
|
||||
|
||||
When you configure an [injector](guide/glossary#injector) with a [provider](guide/glossary#provider), you are associating that provider with a [dependency injection token](guide/glossary#di-token), or DI token.
|
||||
The injector lets Angular create a map of any internal dependencies.
|
||||
The DI token acts as a key to that map.
|
||||
|
||||
The dependency value is an instance, and the class type serves as a lookup key.
|
||||
Here, the injector uses the `HeroService` type as the token for looking up `heroService`.
|
||||
|
||||
<code-example header="src/app/injector.component.ts" path="dependency-injection/src/app/injector.component.ts" region="get-hero-service"></code-example>
|
||||
|
||||
When you define a constructor parameter with the `HeroService` class type, Angular knows to inject the service associated with that `HeroService` class token:
|
||||
|
||||
<code-example header="src/app/heroes/hero-list.component.ts" path="dependency-injection/src/app/heroes/hero-list.component.ts" region="ctor-signature"></code-example>
|
||||
|
||||
Though classes provide many dependency values, the expanded `provide` object lets you associate different kinds of providers with a DI token.
|
||||
|
||||
<a id="provide"></a>
|
||||
|
||||
## Defining providers
|
||||
|
||||
The class provider syntax is a shorthand expression that expands into a provider configuration, defined by the [`Provider` interface](api/core/Provider).
|
||||
The following example is the class provider syntax for providing a `Logger` class in the `providers` array.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger"></code-example>
|
||||
|
||||
Angular expands the `providers` value into a full provider object as follows.
|
||||
Angular expands the `providers` value in this case into a full provider object as follows:
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-3" ></code-example>
|
||||
|
||||
The expanded provider configuration is an object literal with two properties:
|
||||
- The `provide` property holds the token that serves as the key for both locating a dependency value and configuring the injector.
|
||||
- The second property is a provider definition object, which tells the injector how to create the dependency value. The provider-definition key can be one of the following:
|
||||
- useClass - this option tells Angular DI to instantiate a provided class when a dependency is injected
|
||||
- useExisting - allows you to alias a token and reference any existing one.
|
||||
- useFactory - allows you to define a function that constructs a dependency.
|
||||
- useValue - provides a static value that should be used as a dependency.
|
||||
|
||||
* The `provide` property holds the [token](#token) that serves as the key for both locating a dependency value and configuring the injector.
|
||||
The section below describes how to use the mentioned provider definition keys.
|
||||
|
||||
* The second property is a provider definition object, which tells the injector how to create the dependency value.
|
||||
The provider-definition key can be `useClass`, as in the example.
|
||||
It can also be `useExisting`, `useValue`, or `useFactory`.
|
||||
Each of these keys provides a different type of dependency, as discussed in the following section.
|
||||
<a id="token"></a>
|
||||
<a id="injection-token"></a>
|
||||
|
||||
<a id="class-provider"></a>
|
||||
|
||||
## Specifying an alternative class provider
|
||||
|
||||
Different classes can provide the same service.
|
||||
For example, the following code tells the injector to return a `BetterLogger` instance when the component asks for a logger using the `Logger` token.
|
||||
### Class providers: useClass
|
||||
The `useClass` provider key lets you create and return a new instance of the specified class.
|
||||
You can use this type of provider to substitute an alternative implementation for a common or default class. The alternative implementation can, for example, implement a different strategy, extend the default class, or emulate the behavior of the real class in a test case.
|
||||
In the following example, the `BetterLogger` class would be instantiated when the `Logger` dependency is requested in a component or any other class.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-4" ></code-example>
|
||||
|
||||
<a id="class-provider-dependencies"></a>
|
||||
|
||||
### Configuring class providers with dependencies
|
||||
|
||||
If the alternative class providers have their own dependencies, specify both providers in the `providers` metadata property of the parent module or component.
|
||||
If the alternative class providers have their own dependencies, specify both providers in the providers metadata property of the parent module or component.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5"></code-example>
|
||||
|
||||
In this example, `EvenBetterLogger` displays the user name in the log message.
|
||||
This logger gets the user from an injected `UserService` instance.
|
||||
In this example, `EvenBetterLogger` displays the user name in the log message. This logger gets the user from an injected `UserService` instance.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger"></code-example>
|
||||
|
||||
The injector needs providers for both this new logging service and its dependent `UserService`.
|
||||
Angular DI knows how to construct the `UserService` dependency, since it has been configured above and is available in the injector.
|
||||
|
||||
<a id="aliased-class-providers"></a>
|
||||
### Alias providers: useExisting
|
||||
|
||||
### Aliasing class providers
|
||||
The `useExisting` provider key lets you map one token to another. In effect, the first token is an alias for the service associated with the second token, creating two ways to access the same service object.
|
||||
|
||||
To alias a class provider, specify the alias and the class provider in the `providers` array with the `useExisting` property.
|
||||
|
||||
In the following example, the injector injects the singleton instance of `NewLogger` when the component asks for either the new or the old logger.
|
||||
In this way, `OldLogger` is an alias for `NewLogger`.
|
||||
In the following example, the injector injects the singleton instance of `NewLogger` when the component asks for either the new or the old logger. In this way, `OldLogger` is an alias for `NewLogger`.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b"></code-example>
|
||||
|
||||
Be sure you don't alias `OldLogger` to `NewLogger` with `useClass`, as this creates two different `NewLogger` instances.
|
||||
Ensure you do not alias `OldLogger` to `NewLogger` with `useClass`, as this creates two different `NewLogger` instances.
|
||||
|
||||
<a id="provideparent"></a>
|
||||
### Factory providers: useFactory
|
||||
The `useFactory` provider key lets you create a dependency object by calling a factory function. With this approach you can create a dynamic value based on information available in the DI and elsewhere in the app.
|
||||
|
||||
## Aliasing a class interface
|
||||
In the following example, only authorized users should see secret heroes in the `HeroService`.
|
||||
Authorization can change during the course of a single application session, as when a different user logs in .
|
||||
|
||||
Generally, writing variations of the same parent alias provider uses [forwardRef](guide/dependency-injection-in-action#forwardref) as follows.
|
||||
To keep security-sensitive information in `UserService` and out of `HeroService`, give the `HeroService` constructor a boolean flag to control display of secret heroes.
|
||||
|
||||
<code-example header="dependency-injection-in-action/src/app/parent-finder.component.ts" path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers"></code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" header="src/app/heroes/hero.service.ts (excerpt)"></code-example>
|
||||
|
||||
To streamline your code, extract that logic into a helper function using the `provideParent()` helper function.
|
||||
To implement the `isAuthorized` flag, use a factory provider to create a new logger instance for `HeroService`.
|
||||
|
||||
<code-example header="dependency-injection-in-action/src/app/parent-finder.component.ts" path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-the-parent"></code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" header="src/app/heroes/hero.service.provider.ts (excerpt)"></code-example>
|
||||
|
||||
Now you can add a parent provider to your components that's easier to read and understand.
|
||||
The factory function has access to `UserService`.
|
||||
You inject both `Logger` and `UserService` into the factory provider so the injector can pass them along to the factory function.
|
||||
|
||||
<code-example header="dependency-injection-in-action/src/app/parent-finder.component.ts" path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-providers"></code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" header="src/app/heroes/hero.service.provider.ts (excerpt)"></code-example>
|
||||
|
||||
### Aliasing multiple class interfaces
|
||||
* The `useFactory` field specifies that the provider is a factory function whose implementation is `heroServiceFactory`.
|
||||
|
||||
To alias multiple parent types, each with its own class interface token, configure `provideParent()` to accept more arguments.
|
||||
* The `deps` property is an array of provider tokens.
|
||||
The `Logger` and `UserService` classes serve as tokens for their own class providers.
|
||||
The injector resolves these tokens and injects the corresponding services into the matching `heroServiceFactory` factory function parameters.
|
||||
|
||||
Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent class interface.
|
||||
Capturing the factory provider in the exported variable, `heroServiceProvider`, makes the factory provider reusable.
|
||||
|
||||
<code-example header="dependency-injection-in-action/src/app/parent-finder.component.ts" path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-parent"></code-example>
|
||||
### Value providers: useValue
|
||||
|
||||
Next, to use `provideParent()` with a different parent type, provide a second argument, here `DifferentParent`.
|
||||
The `useValue` key lets you associate a fixed value with a DI token. Use this technique to provide runtime configuration constants such as website base addresses and feature flags. You can also use a value provider in a unit test to provide mock data in place of a production data service. The next section provides more information about the `useValue` key.
|
||||
|
||||
<code-example header="dependency-injection-in-action/src/app/parent-finder.component.ts" path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="beth-providers"></code-example>
|
||||
## Using an `InjectionToken` object
|
||||
|
||||
<a id="value-provider"></a>
|
||||
Define and use an `InjectionToken` object for choosing a provider token for non-class dependencies. The following example defines a token, `APP_CONFIG` of the type `InjectionToken`.
|
||||
|
||||
## Injecting an object
|
||||
|
||||
To inject an object, configure the injector with the `useValue` option.
|
||||
The following provider object uses the `useValue` key to associate the variable with the `Logger` token.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7"></code-example>
|
||||
|
||||
In this example, `SilentLogger` is an object that fulfills the logger role.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger"></code-example>
|
||||
|
||||
<a id="non-class-dependencies"></a>
|
||||
|
||||
### Injecting a configuration object
|
||||
|
||||
A common use case for object literals is a configuration object.
|
||||
The following configuration object includes the title of the application and the address of a web API endpoint.
|
||||
|
||||
<code-example header="src/app/app.config.ts (excerpt)" path="dependency-injection/src/app/app.config.ts" region="config"></code-example>
|
||||
|
||||
To provide and inject the configuration object, specify the object in the `@NgModule()` `providers` array.
|
||||
|
||||
<code-example header="src/app/app.module.ts (providers)" path="dependency-injection/src/app/app.module.ts" region="providers"></code-example>
|
||||
|
||||
<a id="injectiontoken"></a>
|
||||
|
||||
### Using an `InjectionToken` object
|
||||
|
||||
Define and use an `InjectionToken` object for choosing a provider token for non-class dependencies.
|
||||
The following example defines a token, `APP_CONFIG` of the type `InjectionToken`.
|
||||
|
||||
<code-example header="src/app/app.config.ts" path="dependency-injection/src/app/app.config.ts" region="token"></code-example>
|
||||
<code-example path="dependency-injection/src/app/app.config.ts" region="token" header="src/app/app.config.ts"></code-example>
|
||||
|
||||
The optional type parameter, `<AppConfig>`, and the token description, `app.config`, specify the token's purpose.
|
||||
|
||||
Next, register the dependency provider in the component using the `InjectionToken` object of `APP_CONFIG`.
|
||||
|
||||
<code-example header="src/app/providers.component.ts" path="dependency-injection/src/app/providers.component.ts" region="providers-9"></code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" header="src/app/providers.component.ts" region="providers-9"></code-example>
|
||||
|
||||
Now, inject the configuration object into the constructor with `@Inject()` parameter decorator.
|
||||
|
||||
<code-example header="src/app/app.component.ts" path="dependency-injection/src/app/app.component.2.ts" region="ctor"></code-example>
|
||||
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
<a id="di-and-interfaces"></a>
|
||||
### Interfaces and DI
|
||||
|
||||
#### Interfaces and dependency injection
|
||||
|
||||
Though the TypeScript `AppConfig` interface supports typing within the class, the `AppConfig` interface plays no role in dependency injection.
|
||||
In TypeScript, an interface is a design-time artifact, and doesn't have a runtime representation, or token, that the DI framework can use.
|
||||
Though the TypeScript `AppConfig` interface supports typing within the class, the `AppConfig` interface plays no role in DI.
|
||||
In TypeScript, an interface is a design-time artifact, and does not have a runtime representation, or token, that the DI framework can use.
|
||||
|
||||
When the transpiler changes TypeScript to JavaScript, the interface disappears because JavaScript doesn't have interfaces.
|
||||
|
||||
@@ -183,47 +120,9 @@ Because there is no interface for Angular to find at runtime, the interface cann
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface"></code-example>
|
||||
|
||||
<a id="factory-provider"></a>
|
||||
<a id="factory-providers"></a>
|
||||
|
||||
## Using factory providers
|
||||
## What's next
|
||||
|
||||
To create a changeable, dependent value based on information unavailable before run time, use a factory provider.
|
||||
* [Dependency Injection in Action](guide/dependency-injection-in-action)
|
||||
|
||||
In the following example, only authorized users should see secret heroes in the `HeroService`.
|
||||
Authorization can change during the course of a single application session, as when a different user logs in .
|
||||
|
||||
To keep security-sensitive information in `UserService` and out of `HeroService`, give the `HeroService` constructor a boolean flag to control display of secret heroes.
|
||||
|
||||
<code-example header="src/app/heroes/hero.service.ts (excerpt)" path="dependency-injection/src/app/heroes/hero.service.ts" region="internals"></code-example>
|
||||
|
||||
To implement the `isAuthorized` flag, use a factory provider to create a new logger instance for `HeroService`.
|
||||
|
||||
<code-example header="src/app/heroes/hero.service.provider.ts (excerpt)" path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory"></code-example>
|
||||
|
||||
The factory function has access to `UserService`.
|
||||
You inject both `Logger` and `UserService` into the factory provider so the injector can pass them along to the factory function.
|
||||
|
||||
<code-example header="src/app/heroes/hero.service.provider.ts (excerpt)" path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider"></code-example>
|
||||
|
||||
* The `useFactory` field specifies that the provider is a factory function whose implementation is `heroServiceFactory`
|
||||
* The `deps` property is an array of [provider tokens](#token).
|
||||
The `Logger` and `UserService` classes serve as tokens for their own class providers.
|
||||
The injector resolves these tokens and injects the corresponding services into the matching `heroServiceFactory` factory function parameters.
|
||||
|
||||
Capturing the factory provider in the exported variable, `heroServiceProvider`, makes the factory provider reusable.
|
||||
|
||||
The following side-by-side example shows how `heroServiceProvider` replaces `HeroService` in the `providers` array.
|
||||
|
||||
<code-tabs>
|
||||
<code-pane header="src/app/heroes/heroes.component (v3)" path="dependency-injection/src/app/heroes/heroes.component.ts"></code-pane>
|
||||
<code-pane header="src/app/heroes/heroes.component (v2)" path="dependency-injection/src/app/heroes/heroes.component.1.ts"></code-pane>
|
||||
</code-tabs>
|
||||
|
||||
<!-- links -->
|
||||
|
||||
<!-- external links -->
|
||||
|
||||
<!-- end links -->
|
||||
|
||||
@reviewed 2022-02-28
|
||||
@reviewed 2022-08-02
|
||||
@@ -1,94 +1,84 @@
|
||||
# Dependency injection in Angular
|
||||
# Understanding dependency injection
|
||||
|
||||
Dependencies are services or objects that a class needs to perform its function.
|
||||
Dependency injection, or DI, is a design pattern in which a class requests dependencies from external sources rather than creating them.
|
||||
Dependency injection, or DI, is one of the fundamental concepts in Angular. DI is wired into the Angular framework and allows classes with Angular decorators, such as Components, Directives, Pipes, and Injectables, to configure dependencies that they need.
|
||||
|
||||
Angular's DI framework provides dependencies to a class upon instantiation.
|
||||
Use Angular DI to increase flexibility and modularity in your applications.
|
||||
Two main roles exist in the DI system: dependency consumer and dependency provider.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
Angular facilitates the interaction between dependency consumers and dependency providers using an abstraction called [Injector](guide/glossary#injector). When a dependency is requested, the injector checks its registry to see if there is an instance already available there. If not, a new instance is created and stored in the registry. Angular creates an application-wide injector (also known as "root" injector) during the application bootstrap process, as well as any other injectors as needed. In most cases you don't need to manually create injectors, but you should know that there is a layer that connects providers and consumers.
|
||||
|
||||
See the <live-example></live-example> for a working example containing the code snippets in this guide.
|
||||
This topic covers basic scenarios of how a class can act as a dependency. Angular also allows you to use functions, objects, primitive types such as string or Boolean, or any other types as dependencies. For more information, see [Dependency providers](guide/dependency-injection-providers).
|
||||
|
||||
</div>
|
||||
## Providing dependency
|
||||
|
||||
## Creating an injectable service
|
||||
Imagine there is a class called HeroService that needs to act as a dependency in a component.
|
||||
|
||||
To create a new `HeroService` class in the `src/app/heroes` directory, use the following command.
|
||||
|
||||
<code-example format="shell" header="Generate the heroes/hero service" language="shell">
|
||||
|
||||
ng generate service heroes/hero
|
||||
The first step is to add the @Injectable decorator to show that the class can be injected.
|
||||
|
||||
<code-example language="typescript">
|
||||
@Injectable()
|
||||
class HeroService {}
|
||||
</code-example>
|
||||
|
||||
This command creates the following default `HeroService`.
|
||||
The next step is to make it available in the DI by providing it. A dependency can be provided in multiple places:
|
||||
|
||||
<code-example header="src/app/heroes/hero.service.ts (CLI-generated)" path="dependency-injection/src/app/heroes/hero.service.0.ts"></code-example>
|
||||
* At the Component level, using the `providers` field of the `@Component` decorator. In this case the `HeroService` becomes available to all instances of this component and other components and directives used in the template. For example:
|
||||
|
||||
The `@Injectable()` decorator specifies that Angular can use this class in the DI system.
|
||||
The metadata, `providedIn: 'root'`, means that the `HeroService` is visible throughout the application.
|
||||
<code-example language="typescript">
|
||||
@Component({
|
||||
selector: 'hero-list',
|
||||
template: '...',
|
||||
providers: [HeroService]
|
||||
})
|
||||
class HeroListComponent {}
|
||||
</code-example>
|
||||
|
||||
Next, to get the hero mock data, add a `getHeroes()` method that returns the heroes from `mock.heroes.ts`.
|
||||
When you register a provider at the component level, you get a new instance of the service with each new instance of that component.
|
||||
|
||||
<code-example header="src/app/heroes/hero.service.ts" path="dependency-injection/src/app/heroes/hero.service.3.ts"></code-example>
|
||||
* At the NgModule level, using the `providers` field of the `@NgModule` decorator. In this scenario, the `HeroService` is available to all components, directives and pipes declared in this NgModule. For example:
|
||||
|
||||
For clarity and maintainability, it is recommended that you define components and services in separate files.
|
||||
<code-example language="typescript">
|
||||
@NgModule({
|
||||
declarations: [HeroListComponent]
|
||||
providers: [HeroService]
|
||||
})
|
||||
class HeroListModule {}
|
||||
</code-example>
|
||||
|
||||
If you do combine a component and service in the same file, it is important to define the service first, and then the component.
|
||||
If you define the component before the service, Angular returns a run-time null reference error.
|
||||
When you register a provider with a specific NgModule, the same instance of a service is available to all components in that NgModule.
|
||||
|
||||
<a id="injector-config"></a>
|
||||
<a id="bootstrap"></a>
|
||||
* At the application root level, which allows injecting it into other classes in the application. This can be done by adding the `providedIn: 'root'` field to the `@Injectable` decorator:
|
||||
|
||||
## Injecting services
|
||||
<code-example language="typescript">
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
class HeroService {}
|
||||
</code-example>
|
||||
|
||||
Injecting services lets a component access their features and data.
|
||||
When you provide the service at the root level, Angular creates a single, shared instance of the `HeroService` and injects it into any class that asks for it. Registering the provider in the `@Injectable` metadata also allows Angular to optimize an app by removing the service from the compiled application if it isn't used, a process known as tree-shaking.
|
||||
|
||||
To inject a dependency in a component's `constructor()`, supply a constructor argument with the dependency type.
|
||||
The following example specifies the `HeroService` in the `HeroListComponent` constructor.
|
||||
The type of `heroService` is `HeroService`.
|
||||
## Injecting a dependency
|
||||
|
||||
<code-example header="src/app/heroes/hero-list.component (constructor signature)" path="dependency-injection/src/app/heroes/hero-list.component.ts"
|
||||
region="ctor-signature"></code-example>
|
||||
The most common way to inject a dependency is to declare it in a class constructor. When Angular creates a new instance of a component, directive, or pipe class, it determines which services or other dependencies that class needs by looking at the constructor parameter types. For example, if the `HeroListComponent` needs the `HeroService`, the constructor can look like this:
|
||||
|
||||
For more information, see [Providing dependencies in modules](guide/providers) and [Hierarchical injectors](guide/hierarchical-dependency-injection).
|
||||
<code-example language="typescript">
|
||||
@Component({ … })
|
||||
class HeroListComponent {
|
||||
constructor(private service: HeroService) {}
|
||||
}
|
||||
</code-example>
|
||||
|
||||
<a id="service-needs-service"></a>
|
||||
When Angular discovers that a component depends on a service, it first checks if the injector has any existing instances of that service. If a requested service instance doesn't yet exist, the injector creates one using the registered provider, and adds it to the injector before returning the service to Angular.
|
||||
|
||||
## Using services in other services
|
||||
When all requested services have been resolved and returned, Angular can call the component's constructor with those services as arguments.
|
||||
|
||||
When a service depends on another service, follow the same pattern as injecting into a component.
|
||||
In the following example `HeroService` depends on a `Logger` service to report its activities.
|
||||
|
||||
First, import the `Logger` service.
|
||||
Next, inject the `Logger` service in the `HeroService` `constructor()` by specifying `private logger: Logger` within the parentheses.
|
||||
|
||||
When you create a class whose `constructor()` has parameters, specify the type and metadata about those parameters so that Angular can inject the correct service.
|
||||
|
||||
Here, `constructor()` specifies a type of `Logger` and stores the instance of `Logger` in a private field called `logger`.
|
||||
|
||||
The following code tabs feature the `Logger` service and two versions of `HeroService`.
|
||||
`HeroService (v2)` depends on `Logger` service, while `HeroService (v1)` doesn't depend on the `Logger` service.
|
||||
|
||||
<code-tabs>
|
||||
<code-pane header="src/app/heroes/hero.service (v2)" path="dependency-injection/src/app/heroes/hero.service.2.ts"></code-pane>
|
||||
<code-pane header="src/app/heroes/hero.service (v1)" path="dependency-injection/src/app/heroes/hero.service.1.ts"></code-pane>
|
||||
<code-pane header="src/app/logger.service" path="dependency-injection/src/app/logger.service.ts"></code-pane>
|
||||
</code-tabs>
|
||||
|
||||
In this example, the `getHeroes()` method uses the `Logger` service by logging a message when fetching heroes.
|
||||
<div class="lightbox">
|
||||
<img src="generated/images/guide/architecture/injector-injects.png" alt="Service" class="left">
|
||||
</div>
|
||||
|
||||
## What's next
|
||||
|
||||
* [Dependency providers](guide/dependency-injection-providers)
|
||||
* [DI tokens and providers](guide/dependency-injection-providers)
|
||||
* [Dependency Injection in Action](guide/dependency-injection-in-action)
|
||||
* [Creating and injecting services](guide/creating-injectable-service)
|
||||
* [Dependency Injection in Action](guide/dependency-injection-in-action)
|
||||
|
||||
<!-- links -->
|
||||
|
||||
<!-- external links -->
|
||||
|
||||
<!-- end links -->
|
||||
|
||||
@reviewed 2022-02-28
|
||||
@reviewed 2022-08-02
|
||||
|
||||
@@ -76,6 +76,8 @@ v14 - v17
|
||||
| `@angular/core/testing` | [`async`](#testing) | <!-- v9 --> v12 |
|
||||
| `@angular/forms` | [`FormBuilder.group` legacy options parameter](api/forms/FormBuilder#group) | <!-- v11 --> v14 |
|
||||
| `@angular/platform-server` | [`renderModuleFactory`](#platform-server) | <!-- v13 --> v15 |
|
||||
| `@angular/platform-server` | [`ServerTransferStateModule`](#platform-server) | <!-- v14 --> v16 |
|
||||
| `@angular/platform-browser` | [`BrowserTransferStateModule`](#platform-browser) | <!-- v14 --> v16 |
|
||||
| `@angular/router` | [`relativeLinkResolution`](#relativeLinkResolution) | <!-- v14 --> v16 |
|
||||
| `@angular/router` | [`resolver` argument in `RouterOutletContract.activateWith`](#router) | <!-- v14 --> v16 |
|
||||
| `@angular/router` | [`resolver` field of the `OutletContext` class](#router) | <!-- v14 --> v16 |
|
||||
@@ -159,6 +161,15 @@ In the [API reference section](api) of this site, deprecated APIs are indicated
|
||||
| [`resolver` field of the `OutletContext` class](api/router/OutletContext#resolver) | No replacement needed | v14 | Component factories are not required to create an instance of a component dynamically. Passing a factory resolver via `resolver` class field is no longer needed. |
|
||||
|
||||
|
||||
<a id="platform-browser"></a>
|
||||
|
||||
### @angular/platform-browser
|
||||
|
||||
| API | Replacement | Deprecation announced | Details |
|
||||
|:--- |:--- |:--- |:--- |
|
||||
| [`BrowserTransferStateModule`](api/platform-browser/BrowserTransferStateModule) | No replacement needed. | v14.1 | The `TransferState` class is available for injection without importing additional modules on the client side of a server-rendered application. |
|
||||
|
||||
|
||||
<a id="platform-browser-dynamic"></a>
|
||||
|
||||
### @angular/platform-browser-dynamic
|
||||
@@ -175,6 +186,8 @@ In the [API reference section](api) of this site, deprecated APIs are indicated
|
||||
| API | Replacement | Deprecation announced | Details |
|
||||
|:--- |:--- |:--- |:--- |
|
||||
| [`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. |
|
||||
| [`ServerTransferStateModule`](api/platform-server/ServerTransferStateModule) | No replacement needed. | v14.1 | The `TransferState` class is available for injection without importing additional modules during server side rendering, when `ServerModule` is imported or `renderApplication` function is used for bootstrap. |
|
||||
|
||||
|
||||
<a id="forms"></a>
|
||||
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
# Resolving documentation linter messages
|
||||
|
||||
This topic describes different ways to resolve common messages that the documentation linter produces.
|
||||
|
||||
## Anatomy of a documentation linter message
|
||||
|
||||
This is an example of a message produced by the documentation linter.
|
||||
|
||||
<div class="lightbox">
|
||||
|
||||
<img alt="sample of a lint message" src="generated/images/guide/docs-lint-errors/sample-lint-error.png">
|
||||
|
||||
</div>
|
||||
|
||||
A documentation linter message contains these elements. Starting from the top line:
|
||||
|
||||
<!-- vale Angular.Angular_Spelling = NO -->
|
||||
<!-- vale Angular.Google_Spacing = NO -->
|
||||
<!-- vale Angular.Google_We = NO -->
|
||||
|
||||
* The severity. One of these icons indicates the message's severity:
|
||||
* <img alt="documentation style error icon" src="generated/images/guide/docs-lint-errors/lint-error-icon.png"> **Error** (A red `x` in a circle) Errors must be corrected before the file can be merged.
|
||||
* <img alt="documentation style warning icon" src="generated/images/guide/docs-lint-errors/lint-warn-icon.png"> **Warning** (A yellow exclamation mark in a triangle) Warnings should be corrected before the file is merged.
|
||||
* <img alt="documentation style info icon" src="generated/images/guide/docs-lint-errors/lint-info-icon.png"> **Info** (A blue lower-case `i` in a circle) Informational messages should be corrected before the file is merged.
|
||||
* The style rule message. The style rule message in this example is: *Did you really mean 'sdfdsfsdfdfssd'? It wasn't found in our dictionary.*
|
||||
* The style reference. Some references are linked to a style guide topic that explains the rule. The style reference in this example is: *Vale(Angular.Angular_Spelling)*.
|
||||
* The location of the problem text in the document identified by source line and column as precisely as possible. Some messages might not have the exact location of the text that triggered the message. The location in this example is: *[Ln 8, Col 1]*.
|
||||
* The style test definition file that produced the message, which is linked to the file. The style test definition in this example is: *Angular_Spelling.yml[Ln 1, Col 1]: View rule*.
|
||||
|
||||
<!-- vale Angular.Google_We = YES -->
|
||||
<!-- vale Angular.Google_Spacing = YES -->
|
||||
<!-- vale Angular.Angular_Spelling = YES -->
|
||||
|
||||
## Strategies to improve your documentation
|
||||
|
||||
These tips can help you improve your documentation and remove documentation linter messages.
|
||||
|
||||
### Refer to the style guides
|
||||
|
||||
The lint tool tests against the styles found in these style guides.
|
||||
Most style tests include links to relevant sections in these documents for more information.
|
||||
|
||||
* [Angular documentation style guide][AioGuideDocsStyleGuide]
|
||||
* [Google Developer Documentation Style Guide][GoogleDevelopersStyle]
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Not every style mentioned in the style guides has a test.
|
||||
Style guides and the style tests can change.
|
||||
|
||||
</div>
|
||||
|
||||
### Split up long sentences
|
||||
|
||||
Generally, shorter sentences are easier to read than longer ones.
|
||||
Long sentences can occur when you try to say too much at once.
|
||||
Long sentences, as well as the use of parentheses, semi-colons, or words identified as *too-wordy*, generally require rethinking and rewriting.
|
||||
|
||||
Consider restructuring a long sentence to break its individual ideas into distinct sentences or bullet points.
|
||||
|
||||
### Use lists and tables
|
||||
|
||||
Sentences that contain comma-separated lists might be clearer if presented as a bulleted-list or table.
|
||||
|
||||
Consider changing a comma-separated list of items in a sentence to a list of bullets to make those list items easier to read.
|
||||
|
||||
### Use more common words
|
||||
|
||||
Shorter, more common words are generally easier to read than longer ones.
|
||||
This does not mean you need to write down to the audience. Technical docs should still be precise.
|
||||
Angular docs are read by many people around the world and should use language that the most people can understand.
|
||||
|
||||
If you think a specific term is required even though it is been flagged as uncommon, try to include a short explanation of the term.
|
||||
Also, try adding some context around its first mention.
|
||||
|
||||
Linking a term to another section or topic is also an option, but consider the disruption that causes to the reader before you use it.
|
||||
If you force a reader to go to another page for a definition, they might lose their concentration on the current topic and their primary goal.
|
||||
|
||||
### Use fewer words
|
||||
|
||||
If you can remove a word and not lose the meaning of the sentence, leave it out.
|
||||
|
||||
One common place where removing words can help is in a list of examples with more than two or three items.
|
||||
Before you place the items in a bullet list, consider if only one of the items can convey the desired meaning.
|
||||
Another option might be to replace a list of items with a single term that describes all the elements in your list.
|
||||
|
||||
## More about specific documentation linter messages
|
||||
|
||||
Most documentation linter messages are self-explanatory and include a link to supplementary documentation.
|
||||
Some messages identify areas in that the documentation might need more thought.
|
||||
The following types of messages often occur in areas of the text that should be reconsidered and rewritten to improve the text and remove the message.
|
||||
|
||||
### A word is *too-wordy* or should be replaced by another
|
||||
|
||||
Generally, technical documentation should use a simple and consistent vocabulary to be understood by a wide audience. Words that trigger this message are usually words for which there's a simpler way to convey the same thought.
|
||||
|
||||
<!-- vale Angular.Google_Spacing = NO -->
|
||||
<!-- vale Angular.Google_Headings = NO -->
|
||||
<!-- vale Angular.Google_Ellipses = NO -->
|
||||
<!-- vale Angular.WriteGood_TooWordy = NO -->
|
||||
|
||||
<!-- markdownlint-disable-file MD026 -->
|
||||
|
||||
#### Angular.WriteGood_TooWordy - See if you can rewrite the sentence...
|
||||
|
||||
<!-- vale Angular.WriteGood_TooWordy = YES -->
|
||||
<!-- vale Angular.Google_Ellipses = YES -->
|
||||
<!-- vale Angular.Google_Headings = YES -->
|
||||
<!-- vale Angular.Google_Spacing = YES -->
|
||||
|
||||
Words identified by this style test can usually be replaced by simpler words.
|
||||
If not, sentences with these words should be revised to use simpler language and avoid the word in the message.
|
||||
|
||||
The following table has some common words detected by this type of message and simpler words to try in their place.
|
||||
|
||||
<!-- vale Angular.WriteGood_TooWordy = NO -->
|
||||
|
||||
| *Too-wordy* word | Simpler replacement |
|
||||
|:-- |:-- |
|
||||
|accelerate |speed up |
|
||||
|accomplish |perform, finish |
|
||||
|acquire |get |
|
||||
|additional |more |
|
||||
|adjustment |change |
|
||||
|advantageous |beneficial |
|
||||
|consequently |as a result |
|
||||
|designate |assign |
|
||||
|equivalent |the same |
|
||||
|exclusively |only |
|
||||
|for the most part |generally |
|
||||
|have a tendency to |tend to |
|
||||
|in addition |furthermore |
|
||||
|modify |change or update |
|
||||
|monitor |observe |
|
||||
|necessitate |require |
|
||||
|one particular |one |
|
||||
|point in time |moment |
|
||||
|portion |part |
|
||||
|similar to |like |
|
||||
|validate |verify |
|
||||
|whether or not |whether |
|
||||
|
||||
<!-- vale Angular.WriteGood_TooWordy = YES -->
|
||||
|
||||
<!-- vale Angular.Google_Headings = NO -->
|
||||
|
||||
#### *WordList* messages
|
||||
|
||||
<!-- vale Angular.Google_Headings = YES -->
|
||||
|
||||
The messages about words detected by these style tests generally suggest a better alternative.
|
||||
While the word you used would probably be understood, it most likely triggered this message for one of the following reasons:
|
||||
|
||||
* The suggested works better in a screen-reader context.
|
||||
* The word that you used could produce an unpleasant response in the reader.
|
||||
* The suggested word is simpler, shorter, or easier for more people to understand.
|
||||
* The word you used has other possible variations. The suggested word is the variation to use in the documentation to be consistent.
|
||||
|
||||
<!-- vale Angular.Angular_Spelling = NO -->
|
||||
|
||||
### *Proselint* messages
|
||||
|
||||
The Proselint style tests test for words that are jargon or that could be offensive to some people.
|
||||
|
||||
<!-- vale Angular.Angular_Spelling = YES -->
|
||||
|
||||
Rewrite the text to replace the jargon or offensive language with more inclusive language.
|
||||
|
||||
### *Starting a sentence* messages
|
||||
|
||||
Some words, such as *so* and *there is/are*, aren't necessary at the beginning of a sentence.
|
||||
Sentences that start with the words identified by this message can usually be made shorter, simpler, and clearer by rewriting them without those openings.
|
||||
|
||||
### Cliches
|
||||
|
||||
Cliches should be replaced by more literal text.
|
||||
|
||||
Cliches make it difficult for people who don't understand English to understand the documentation. When cliches are translated by online tools such as Google translate, they can produce confusing results.
|
||||
|
||||
## If all else fails
|
||||
|
||||
The style rules generally guide you in the direction of clearer content, but sometimes you might need to break the rules.
|
||||
If you decide that the best choice for the text conflicts with the linter, mark the text as an exception to linting.
|
||||
|
||||
The documentation linter checks only the content that is rendered as text. It does not test code-formatted text. One common source of false problems is code references that are not formatted as code.
|
||||
|
||||
If you use these exceptions, please limit the amount of text that you exclude from analysis to the fewest lines possible.
|
||||
|
||||
When necessary, you can apply these exceptions to your content.
|
||||
|
||||
1. **General exception**
|
||||
|
||||
A *general exception* allows you to exclude the specified text from all lint testing.
|
||||
|
||||
To apply a general exception, surround the text that you do not want the linter to test with the HTML
|
||||
`comment` elements shown in this example.
|
||||
|
||||
```html
|
||||
|
||||
<!-- vale off -->
|
||||
|
||||
Text the linter does not check for any style problem.
|
||||
|
||||
<!-- vale on -->
|
||||
|
||||
```
|
||||
|
||||
Be sure to leave a blank line before and after each comment.
|
||||
|
||||
2. **Style exception**
|
||||
|
||||
A *style exception* allows you to exclude text from an individual style test.
|
||||
|
||||
To apply a style exception, surround the text that you do not want the linter to test with these HTML
|
||||
`comment` elements. Between these comments, the linter ignores the style test in the comment, but
|
||||
still tests for all other styles that are in use.
|
||||
|
||||
```html
|
||||
<!-- vale Style.Rule = NO -->
|
||||
<!-- vale Style.Rule = YES -->
|
||||
```
|
||||
|
||||
Replace `Style.Rule` in the comments with the style rule reference from the problem message displayed in the IDE.
|
||||
For example, imagine that you got this problem message and you want to use the word it identified as a problem.
|
||||
|
||||
```html
|
||||
Did you really mean 'inlines'? It was not found in our dictionary. Vale(Angular.Angular_Spelling) [Ln 24, Col 59]
|
||||
Angular_Spelling.yml[Ln 1, Col 1]: View rule
|
||||
```
|
||||
|
||||
The `Style.Rule` for this message is the text inside the parentheses: `Angular.Angular_Spelling` in this case.
|
||||
To turn off that style test, use the comments shown in this example.
|
||||
|
||||
```html
|
||||
|
||||
<!-- vale Angular.Angular_Spelling = NO -->
|
||||
|
||||
'inlines' does not display a problem because this text is not spell-checked.
|
||||
Remember that the linter does not check any spelling in this block of text.
|
||||
The linter continues to test all other style rules.
|
||||
|
||||
<!-- vale Angular.Angular_Spelling = YES -->
|
||||
|
||||
```
|
||||
|
||||
<!-- links -->
|
||||
|
||||
[AioGuideDocsStyleGuide]: https://angular.io/guide/docs-style-guide "Angular documentation style guide | Angular"
|
||||
|
||||
<!-- external links -->
|
||||
|
||||
[GoogleDevelopersStyle]: https://developers.google.com/style "About this guide | Google developer documentation style guide | Google Developers"
|
||||
|
||||
<!-- end links -->
|
||||
|
||||
<!-- @reviewed 2022-07-15 -->
|
||||
@@ -162,7 +162,7 @@ The following figure shows the final form.
|
||||
|
||||
| Steps | Details |
|
||||
|:--- |:--- |
|
||||
| Different types of forms and control collection | This tutorial shows how to build a questionaire, which is just one kind of dynamic form. The example uses `FormGroup` to collect a set of controls. For an example of a different type of dynamic form, see the section [Creating dynamic forms](guide/reactive-forms#creating-dynamic-forms "Create dynamic forms with arrays") in the Reactive Forms guide. That example also shows how to use `FormArray` instead of `FormGroup` to collect a set of controls. |
|
||||
| Different types of forms and control collection | This tutorial shows how to build a questionnaire, which is just one kind of dynamic form. The example uses `FormGroup` to collect a set of controls. For an example of a different type of dynamic form, see the section [Creating dynamic forms](guide/reactive-forms#creating-dynamic-forms "Create dynamic forms with arrays") in the Reactive Forms guide. That example also shows how to use `FormArray` instead of `FormGroup` to collect a set of controls. |
|
||||
| Validating user input | The section [Validating form input](guide/reactive-forms#validating-form-input "Basic input validation") introduces the basics of how input validation works in reactive forms. <br /> The [Form validation guide](guide/form-validation "Form validation guide") covers the topic in more depth. |
|
||||
|
||||
<!-- links -->
|
||||
|
||||
@@ -21,7 +21,18 @@ This topic uses the following pictographs.
|
||||
|
||||
</div>
|
||||
|
||||
## Two injector hierarchies
|
||||
The applications you build with Angular can become quite large, and one way to manage this complexity is to split up the application into many small well-encapsulated modules, that are by themselves split up into a well-defined tree of components.
|
||||
|
||||
There can be sections of your page that works in a completely independent way than the rest of the application, with its own local copies of the services and other dependencies that it needs. Some of the services that these sections of the application use might be shared with other parts of the application, or with parent components that are further up in the component tree, while other dependencies are meant to be private.
|
||||
|
||||
With hierarchical dependency injection, you can isolate sections of the application and give them their own private dependencies not shared with the rest of the application, or have parent components share certain dependencies with its child components only but not with the rest of the component tree, and so on. Hierarchical dependency injection enables you to share dependencies between different parts of the application only when and if you need to.
|
||||
|
||||
## Types of injector hierarchies
|
||||
|
||||
Injectors in Angular have rules that you can leverage to
|
||||
achieve the desired visibility of injectables in your applications.
|
||||
By understanding these rules, you can determine in which
|
||||
NgModule, Component, or Directive you should declare a provider.
|
||||
|
||||
Angular has two injector hierarchies:
|
||||
|
||||
@@ -173,7 +184,7 @@ If the component's injector lacks the provider, it passes the request up to its
|
||||
|
||||
The requests keep forwarding up until Angular finds an injector that can handle the request or runs out of ancestor `ElementInjector` hierarchies.
|
||||
|
||||
If Angular doesn't find the provider in any `ElementInjector` hierarchiess, it goes back to the element where the request originated and looks in the `ModuleInjector` hierarchy.
|
||||
If Angular doesn't find the provider in any `ElementInjector` hierarchies, it goes back to the element where the request originated and looks in the `ModuleInjector` hierarchy.
|
||||
If Angular still doesn't find the provider, it throws an error.
|
||||
|
||||
If you have registered a provider for the same DI token at different levels, the first one Angular encounters is the one it uses to resolve the dependency.
|
||||
@@ -454,7 +465,7 @@ Emoji from FlowerService: 🌻
|
||||
|
||||
</code-example>
|
||||
|
||||
In the logical tree, this would be represented as follows:
|
||||
In the logical tree, this is represented as follows:
|
||||
|
||||
<code-example format="html" language="html">
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ This is true for *all* `HttpClient` *methods*.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The [`AsyncPipe`](api/common/AsyncPipe) subscribes and unsubscribes for you automatically.
|
||||
You should always unsubscribe from an observable when a component is destroyed.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Use them to perform the following kinds of operations.
|
||||
|
||||
| Hook method | Purpose | Timing |
|
||||
|:--- |:--- |:--- |
|
||||
| `ngOnChanges()` | Respond when Angular sets or resets data-bound input properties. The method receives a `SimpleChanges` object of current and previous property values. <br /> <div class="alert is-helpful"> **NOTE**: <br /> This happens very frequently, so any operation you perform here impacts performance significantly. </div> See details in [Using change detection hooks](#onchanges) in this document. | Called before `ngOnInit()` \(if the component has bound inputs\) and whenever one or more data-bound input properties change. <br /> <div class="alert is-helpful"> **NOTE**: <br /> If your component has no inputs or you use it without providing any inputs, the framework will not call `ngOnChanges()`. </div> |
|
||||
| `ngOnChanges()` | Respond when Angular sets or resets data-bound input properties. The method receives a `SimpleChanges` object of current and previous property values. <br /> <div class="alert is-helpful"> **NOTE**: <br /> This happens frequently, so any operation you perform here impacts performance significantly. </div> See details in [Using change detection hooks](#onchanges) in this document. | Called before `ngOnInit()` \(if the component has bound inputs\) and whenever one or more data-bound input properties change. <br /> <div class="alert is-helpful"> **NOTE**: <br /> If your component has no inputs or you use it without providing any inputs, the framework will not call `ngOnChanges()`. </div> |
|
||||
| `ngOnInit()` | Initialize the directive or component after Angular first displays the data-bound properties and sets the directive or component's input properties. See details in [Initializing a component or directive](#oninit) in this document. | Called once, after the first `ngOnChanges()`. `ngOnInit()` is still called even when `ngOnChanges()` is not \(which is the case when there are no template-bound inputs\). |
|
||||
| `ngDoCheck()` | Detect and act upon changes that Angular can't or won't detect on its own. See details and example in [Defining custom change detection](#docheck) in this document. | Called immediately after `ngOnChanges()` on every change detection run, and immediately after `ngOnInit()` on the first run. |
|
||||
| `ngAfterContentInit()` | Respond after Angular projects external content into the component's view, or into the view that a directive is in. <br /> See details and example in [Responding to changes in content](#aftercontent) in this document. | Called *once* after the first `ngDoCheck()`. |
|
||||
@@ -121,12 +121,16 @@ The sequence of log messages follows the prescribed hook calling order:
|
||||
|:--- |:--- |
|
||||
| 1 | `OnChanges` |
|
||||
| 2 | `OnInit` |
|
||||
| 3-5 | `DoCheck` |
|
||||
| 6 | `AfterContentInit` |
|
||||
| 7-9 | `AfterContentChecked` |
|
||||
| 10 | `AfterViewInit` |
|
||||
| 11-13 | `AfterViewChecked` |
|
||||
| 14 | `OnDestroy` |
|
||||
| 3 | `DoCheck` |
|
||||
| 4 | `AfterContentInit` |
|
||||
| 5 | `AfterContentChecked` |
|
||||
| 6 | `AfterViewInit` |
|
||||
| 7 | `AfterViewChecked` |
|
||||
| 8 | `DoCheck` |
|
||||
| 9 | `AfterContentChecked` |
|
||||
| 10 | `AfterViewChecked` |
|
||||
| 11 | `OnDestroy` |
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -151,8 +155,8 @@ The example does not perform any initialization or clean-up.
|
||||
It just tracks the appearance and disappearance of an element in the view by recording when the directive itself is instantiated and destroyed.
|
||||
|
||||
A spy directive like this can provide insight into a DOM object that you cannot change directly.
|
||||
You can't touch the implementation of a built-in `<div>`, or modify a third party component.
|
||||
You can, however watch these elements with a directive.
|
||||
You can't access the implementation of a built-in `<div>`, or modify a third party component.
|
||||
You do have the option to watch these elements with a directive.
|
||||
|
||||
The directive defines `ngOnInit()` and `ngOnDestroy()` hooks
|
||||
that log messages to the parent using an injected `LoggerService`.
|
||||
@@ -253,8 +257,8 @@ The `LoggerService.tick_then()` statement postpones the log update for one turn
|
||||
|
||||
#### Write lean hook methods to avoid performance problems
|
||||
|
||||
When you run the *AfterView* sample, notice how frequently Angular calls `AfterViewChecked()`-often when there are no changes of interest.
|
||||
Be very careful about how much logic or computation you put into one of these methods.
|
||||
When you run the *AfterView* sample, notice how frequently Angular calls `AfterViewChecked()` - often when there are no changes of interest.
|
||||
Be careful about how much logic or computation you put into one of these methods.
|
||||
|
||||
<div class="lightbox">
|
||||
|
||||
@@ -284,7 +288,7 @@ AngularJS developers know this technique as *transclusion*.
|
||||
The *AfterContent* sample explores the `AfterContentInit()` and `AfterContentChecked()` hooks that Angular calls *after* Angular projects external content into the component.
|
||||
|
||||
Consider this variation on the [previous *AfterView*](#afterview) example.
|
||||
This time, instead of including the child view within the template, it imports the content from the `AfterContentComponent`'s parent.
|
||||
This time, instead of including the child view within the template, it imports the content from the `AfterContentComponent` hook's parent.
|
||||
The following is the parent's template.
|
||||
|
||||
<code-example header="AfterContentParentComponent (template excerpt)" path="lifecycle-hooks/src/app/after-content-parent.component.ts" region="parent-template"></code-example>
|
||||
@@ -354,7 +358,7 @@ The results are illuminating.
|
||||
|
||||
</div>
|
||||
|
||||
While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it is very expensive.
|
||||
While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it is an expensive hook.
|
||||
This hook is called with enormous frequency —after *every* change detection cycle no matter where the change occurred.
|
||||
It's called over twenty times in this example before the user can do anything.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ For example, you would use a pipe to show a date as **April 15, 1988** rather th
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For the sample application used in this topic, see the <live-example></live-example>.
|
||||
For the sample application used in this topic, see the <live-example name="pipes"></live-example>.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -63,11 +63,11 @@ This means that any services listed in their provider arrays aren't available be
|
||||
When the Angular router lazy-loads a module, it creates a new injector.
|
||||
This injector is a child of the root application injector.
|
||||
Imagine a tree of injectors; there is a single root injector and then a child injector for each lazy loaded module.
|
||||
The router adds all of the providers from the root injector to the child injector.
|
||||
When the router creates a component within the lazy-loaded context, Angular prefers service instances created from these providers to the service instances of the application root injector.
|
||||
This child injector gets populated with all the module-specific providers, if any.
|
||||
Look up resolution for every provider follows the [rules of dependency injection hierarchy](guide/hierarchical-dependency-injection#resolution-rules).
|
||||
|
||||
Any component created within a lazy loaded module's context, such as by router navigation, gets the local instance of the service, not the instance in the root application injector.
|
||||
Components in external modules continue to receive the instance created for the application root.
|
||||
Any component created within a lazy loaded module's context, such as by router navigation, gets its own local instance of child provided services, not the instance in the root application injector.
|
||||
Components in external modules continue to receive the instances created for the application root injector.
|
||||
|
||||
Though you can provide services by lazy loading modules, not all services can be lazy loaded.
|
||||
For instance, some modules only work in the root module, such as the Router.
|
||||
|
||||
@@ -1,57 +1,69 @@
|
||||
# Reviewing Content
|
||||
# Reviewing documentation
|
||||
|
||||
You can review the Angular documentation, even if you never contributed to Angular before.
|
||||
|
||||
Angular developers work best when they have access to accurate and complete documentation.
|
||||
Keeping existing content up-to-date is an essential part of ensuring that all developers have a great documentation experience.
|
||||
Keeping existing documentation up-to-date is an essential part of ensuring that all developers have a great development experience.
|
||||
|
||||
This topic describes how you can help keep Angular content up-to-date by reviewing content.
|
||||
This topic describes how you can help keep the Angular documentation up-to-date by reviewing and updating it.
|
||||
|
||||
## Before you begin
|
||||
|
||||
You can review content even if you've never contributed to Angular before.
|
||||
However, you may find it helpful to have the [Contributing to Angular](https://github.com/angular/angular/blob/main/CONTRIBUTING.md) guide available if you're filing your first pull request in the repository.
|
||||
Review the [Documentation contributor guide][AioDocContributorGuide] to get ready to review to update the documentation.
|
||||
The *Documentation contributor guide* describes how to prepare to update a documentation topic.
|
||||
|
||||
## Reviewing content (`@reviewed`)
|
||||
You can edit a topic in the GitHub web interface or in your favorite code editor or integrated development environment (IDE).
|
||||
In either case, you want to edit in your own fork of the Angular source code repo.
|
||||
|
||||
All of the task-based guides, tutorials, and conceptual topics that you find on Angular.io support a `@reviewed` tag.
|
||||
When present, this tag is followed by the date representing when a given topic was reviewed for accuracy and completeness.
|
||||
On the published topic, this reviewed information appears at the bottom of the topic; for example, `Last reviewed on` followed by the day of the week, month, day, and year.
|
||||
To review a documentation topic, you want to be able to edit the topic.
|
||||
Even if you don't find any problem, you want to update the `@reviewed` tag at the end of the topic before you finish.
|
||||
|
||||
## Finding topics to review
|
||||
|
||||
Find topic a topic that has not been reviewed in the past six months.
|
||||
|
||||
At the bottom of some topics, there's an `@reviewed` tag that shows the date it was last reviewed.
|
||||
This is an example of such a tag taken from the bottom of a topic.
|
||||
|
||||
<div class="lightbox">
|
||||
|
||||
<img alt="Example of the last reviewed date specifying the day of the week, the month, the date, and the year on a page footer." src="generated/images/guide/contributors-guide/last-reviewed.png">
|
||||
<img alt="Example of the last reviewed date entry showing the date the topic was reviewed as month, day, and year" src="generated/images/guide/contributors-guide/last-reviewed.png">
|
||||
|
||||
</div>
|
||||
|
||||
This reviewed date indicates when someone last reviewed the topic to ensure that its contents were accurate.
|
||||
## Reviewing and updating a topic
|
||||
|
||||
You can review a topic using either the GitHub user interface or in an editor on your local machine.
|
||||
You can also review any topic that you like.
|
||||
Previous experience in the subject of the topic is helpful, but not required.
|
||||
To review a topic, use either the GitHub user interface or directly edit the source markdown file of the topic on your own computer.
|
||||
|
||||
**To review a topic:**
|
||||
Feel free to review any topic in which you have interest.
|
||||
Experience with the subject matter is helpful, but not required to provide copy edits.
|
||||
|
||||
1. Navigate to the topic that you want to review.
|
||||
1. Locate the last reviewed date at the bottom of the topic and verify that the topic meets the [review criteria](#review-criteria).
|
||||
**To review and update a documentation topic:**
|
||||
|
||||
If the topic does not have a last reviewed date, you are welcome to add it to the topic.
|
||||
To add a date, use the `YYYY-MM-DD` date format.
|
||||
Example:
|
||||
`@reviewed 2021-03-23`
|
||||
|
||||
1. Read through the topic.
|
||||
1. If the topic requires an update, either [file an issue](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#submit-issue) that describes the update required, or [create a pull request](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#submit-pr) with the update.
|
||||
1. Update the `@reviewed` tag, either through the [GitHub user interface](guide/updating-content-github-ui) or through Angular's [standard pull request process](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#submit-pr).
|
||||
|
||||
<a id="review-criteria"></a>
|
||||
|
||||
### Review criteria
|
||||
|
||||
In general, topics should be reviewed either every six months, or around every major release.
|
||||
1. In your web browser, navigate to the topic that you want to review.
|
||||
2. Locate the last reviewed date at the bottom of the topic. Verify that the topic has not been reviewed within the past six months.
|
||||
3. Read through the topic.
|
||||
4. If the topic requires an update, you can:
|
||||
* [File an issue][GithubAngularAngularBlobMainContributingMdSubmitIssue] that describes the update required.
|
||||
* Fix the issue and [create a pull request][GithubAngularAngularBlobMainContributingMdSubmitPr] with the update.
|
||||
5. If the topic is accurate and doesn't need any revision, update, or add the `@reviewed` tag followed by the date you reviewed the topic. You can do this:
|
||||
* By using the [GitHub user interface][AioGuideUpdatingContentGithubUi]
|
||||
* By editing the file on your computer and creating a [standard pull request process][GithubAngularAngularBlobMainContributingMdSubmitPr] for Angular.
|
||||
|
||||
<!-- links -->
|
||||
|
||||
[AioDocContributorGuide]: guide/contributors-guide-overview "Documentation contributors guide | Angular"
|
||||
|
||||
[AioGuideReviewingContentReviewCriteria]: guide/reviewing-content#review-criteria "Review criteria - Reviewing content | Angular"
|
||||
|
||||
[AioGuideUpdatingContentGithubUi]: guide/updating-content-github-ui "Updating topics through the GitHub user interface | Angular"
|
||||
|
||||
<!-- external links -->
|
||||
|
||||
[GithubAngularAngularBlobMainContributingMd]: https://github.com/angular/angular/blob/main/CONTRIBUTING.md "Contributing to Angular | angular/angular | Github"
|
||||
[GithubAngularAngularBlobMainContributingMdSubmitIssue]: https://github.com/angular/angular/blob/main/CONTRIBUTING.md#submit-issue "Submitting an Issue - Contributing to Angular | angular/angular | GitHub"
|
||||
[GithubAngularAngularBlobMainContributingMdSubmitPr]: https://github.com/angular/angular/blob/main/CONTRIBUTING.md#submit-pr "Submitting a Pull Request (PR) - Contributing to Angular | angular/angular | GitHub"
|
||||
|
||||
<!-- end links -->
|
||||
|
||||
@reviewed 2022-02-28
|
||||
@reviewed 2022-08-08
|
||||
|
||||
@@ -2382,12 +2382,12 @@ Currently, the `AdminModule` does not preload because `CanLoad` is blocking it.
|
||||
|
||||
<a id="preload-canload"></a>
|
||||
|
||||
#### `CanLoad` blocks preload
|
||||
#### `CanLoad` blocks preload of children
|
||||
|
||||
The `PreloadAllModules` strategy does not load feature areas protected by a [CanLoad](#can-load-guard) guard.
|
||||
|
||||
You added a `CanLoad` guard to the route in the `AdminModule` a few steps back to block loading of that module until the user is authorized.
|
||||
That `CanLoad` guard takes precedence over the preload strategy.
|
||||
That `CanLoad` guard takes precedence over the preload strategy for loading children routes.
|
||||
|
||||
If you want to preload a module as well as guard against unauthorized access, remove the `canLoad()` guard method and rely on the [canActivate()](#can-activate-guard) guard alone.
|
||||
|
||||
|
||||
@@ -463,7 +463,7 @@ The link parameters array affords the flexibility to represent any routing depth
|
||||
|
||||
When the router navigates to a new component view, it updates the browser's location and history with a URL for that view.
|
||||
|
||||
Modern HTML5 browsers support [history.pushState](https://developer.mozilla.org/docs/Web/API/History_API/Working_with_the_History_API#adding_and_modifying_history_entries title="HTML5 browser history push-state"), a technique that changes a browser's location and history without triggering a server page request.
|
||||
Modern HTML5 browsers support [history.pushState](https://developer.mozilla.org/docs/Web/API/History_API/Working_with_the_History_API#adding_and_modifying_history_entries "HTML5 browser history push-state"), a technique that changes a browser's location and history without triggering a server page request.
|
||||
The router can compose a "natural" URL that is indistinguishable from one that would otherwise require a page load.
|
||||
|
||||
Here's the Crisis Center URL in this "HTML5 pushState" style:
|
||||
@@ -496,7 +496,7 @@ You also have the option of switching to the `HashLocationStrategy` with an over
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For more information on providers and the bootstrap process, see [Dependency Injection](guide/dependency-injection#bootstrap).
|
||||
For more information on providers and the bootstrap process, see [Dependency Injection](guide/dependency-injection-providers).
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ The following template binds the value of `htmlSnippet`, once by interpolating i
|
||||
Interpolated content is always escaped —the HTML isn't interpreted and the browser displays angle brackets in the element's text content.
|
||||
|
||||
For the HTML to be interpreted, bind it to an HTML property such as `innerHTML`.
|
||||
But binding a value that an attacker might control into `innerHTML` normally causes an XSS vulnerability
|
||||
But binding a value that an attacker might control into `innerHTML` normally causes an XSS vulnerability.
|
||||
For example, one could execute JavaScript in a following way:
|
||||
|
||||
<code-example header="src/app/inner-html-binding.component.ts (class)" path="security/src/app/inner-html-binding.component.ts" region="class"></code-example>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# Service worker configuration
|
||||
|
||||
This topic describes the properties of the service worker configuration file.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
A basic understanding of the following:
|
||||
|
||||
* [Service worker overview](https://developer.chrome.com/docs/workbox/service-worker-overview/)
|
||||
* [Service Worker in Production](guide/service-worker-devops)
|
||||
|
||||
The `ngsw-config.json` configuration file specifies which files and data URLs the Angular service worker should cache and how it should update the cached files and data.
|
||||
@@ -64,9 +67,11 @@ Example patterns:
|
||||
| `/*.html` | Specifies only HTML files in the root |
|
||||
| `!/**/*.map` | Exclude all sourcemaps |
|
||||
|
||||
## Service worker configuration properties
|
||||
|
||||
The following sections describe each property of the configuration file.
|
||||
|
||||
## `appData`
|
||||
### `appData`
|
||||
|
||||
This section enables you to pass any data you want that describes this particular version of the application.
|
||||
The `SwUpdate` service includes that data in the update notifications.
|
||||
@@ -74,12 +79,12 @@ Many applications use this section to provide additional information for the dis
|
||||
|
||||
<a id="index-file"></a>
|
||||
|
||||
## `index`
|
||||
### `index`
|
||||
|
||||
Specifies the file that serves as the index page to satisfy navigation requests.
|
||||
Usually this is `/index.html`.
|
||||
|
||||
## `assetGroups`
|
||||
### `assetGroups`
|
||||
|
||||
*Assets* are resources that are part of the application version that update along with the application.
|
||||
They can include resources loaded from the page's origin as well as third-party resources loaded from CDNs and other external URLs.
|
||||
@@ -134,12 +139,14 @@ interface AssetGroup {
|
||||
|
||||
</code-example>
|
||||
|
||||
### `name`
|
||||
Each `AssetGroup` is defined by the following asset group properties.
|
||||
|
||||
#### `name`
|
||||
|
||||
A `name` is mandatory.
|
||||
It identifies this particular group of assets between versions of the configuration.
|
||||
|
||||
### `installMode`
|
||||
#### `installMode`
|
||||
|
||||
The `installMode` determines how these resources are initially cached.
|
||||
The `installMode` can be either of two values:
|
||||
@@ -151,7 +158,7 @@ The `installMode` can be either of two values:
|
||||
|
||||
Defaults to `prefetch`.
|
||||
|
||||
### `updateMode`
|
||||
#### `updateMode`
|
||||
|
||||
For resources already in the cache, the `updateMode` determines the caching behavior when a new version of the application is discovered.
|
||||
Any resources in the group that have changed since the previous version are updated in accordance with `updateMode`.
|
||||
@@ -163,7 +170,7 @@ Any resources in the group that have changed since the previous version are upda
|
||||
|
||||
Defaults to the value `installMode` is set to.
|
||||
|
||||
### `resources`
|
||||
#### `resources`
|
||||
|
||||
This section describes the resources to cache, broken up into the following groups:
|
||||
|
||||
@@ -172,7 +179,7 @@ This section describes the resources to cache, broken up into the following grou
|
||||
| `files` | Lists patterns that match files in the distribution directory. These can be single files or glob-like patterns that match a number of files. |
|
||||
| `urls` | Includes both URLs and URL patterns that are matched at runtime. These resources are not fetched directly and do not have content hashes, but they are cached according to their HTTP headers. This is most useful for CDNs such as the Google Fonts service. <br /> *\(Negative glob patterns are not supported and `?` will be matched literally; that is, it will not match any character other than `?`.\)* |
|
||||
|
||||
### `cacheQueryOptions`
|
||||
#### `cacheQueryOptions`
|
||||
|
||||
These options are used to modify the matching behavior of requests.
|
||||
They are passed to the browsers `Cache#match` function.
|
||||
@@ -183,7 +190,7 @@ Currently, only the following options are supported:
|
||||
|:--- |:--- |
|
||||
| `ignoreSearch` | Ignore query parameters. Defaults to `false`. |
|
||||
|
||||
## `dataGroups`
|
||||
### `dataGroups`
|
||||
|
||||
Unlike asset resources, data requests are not versioned along with the application.
|
||||
They're cached according to manually-configured policies that are more useful for situations such as API requests and other data dependencies.
|
||||
@@ -236,11 +243,13 @@ export interface DataGroup {
|
||||
|
||||
</code-example>
|
||||
|
||||
### `name`
|
||||
Each `DataGroup` is defined by the following data group properties.
|
||||
|
||||
#### `name`
|
||||
|
||||
Similar to `assetGroups`, every data group has a `name` which uniquely identifies it.
|
||||
|
||||
### `urls`
|
||||
#### `urls`
|
||||
|
||||
A list of URL patterns.
|
||||
URLs that match these patterns are cached according to this data group's policy.
|
||||
@@ -249,7 +258,7 @@ Only non-mutating requests \(GET and HEAD\) are cached.
|
||||
* Negative glob patterns are not supported
|
||||
* `?` is matched literally; that is, it matches *only* the character `?`
|
||||
|
||||
### `version`
|
||||
#### `version`
|
||||
|
||||
Occasionally APIs change formats in a way that is not backward-compatible.
|
||||
A new version of the application might not be compatible with the old API format and thus might not be compatible with existing cached resources from that API.
|
||||
@@ -258,18 +267,22 @@ A new version of the application might not be compatible with the old API format
|
||||
|
||||
`version` is an integer field and defaults to `1`.
|
||||
|
||||
### `cacheConfig`
|
||||
#### `cacheConfig`
|
||||
|
||||
This section defines the policy by which matching requests are cached.
|
||||
The following properties define the policy by which matching requests are cached.
|
||||
|
||||
#### `maxSize`
|
||||
##### `maxSize`
|
||||
|
||||
\(required\) The maximum number of entries, or responses, in the cache.
|
||||
**Required**
|
||||
|
||||
The maximum number of entries, or responses, in the cache.
|
||||
Open-ended caches can grow in unbounded ways and eventually exceed storage quotas, calling for eviction.
|
||||
|
||||
#### `maxAge`
|
||||
##### `maxAge`
|
||||
|
||||
(required) The `maxAge` parameter indicates how long responses are allowed to remain in the cache before being considered invalid and evicted.
|
||||
**Required**
|
||||
|
||||
The `maxAge` parameter indicates how long responses are allowed to remain in the cache before being considered invalid and evicted.
|
||||
`maxAge` is a duration string, using the following unit suffixes:
|
||||
|
||||
| Suffixes | Details |
|
||||
@@ -282,7 +295,7 @@ Open-ended caches can grow in unbounded ways and eventually exceed storage quota
|
||||
|
||||
For example, the string `3d12h` caches content for up to three and a half days.
|
||||
|
||||
#### `timeout`
|
||||
##### `timeout`
|
||||
|
||||
This duration string specifies the network timeout.
|
||||
The network timeout is how long the Angular service worker waits for the network to respond before using a cached response, if configured to do so.
|
||||
@@ -298,7 +311,7 @@ The network timeout is how long the Angular service worker waits for the network
|
||||
|
||||
For example, the string `5s30u` translates to five seconds and 30 milliseconds of network timeout.
|
||||
|
||||
#### `strategy`
|
||||
##### `strategy`
|
||||
|
||||
The Angular service worker can use either of two caching strategies for data resources.
|
||||
|
||||
@@ -309,19 +322,19 @@ The Angular service worker can use either of two caching strategies for data res
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You can also emulate a third strategy, [staleWhileRevalidate](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate), which returns cached data \(if available\), but also fetches fresh data from the network in the background for next time.
|
||||
You can also emulate a third strategy, [staleWhileRevalidate](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate), which returns cached data if it is available, but also fetches fresh data from the network in the background for next time.
|
||||
To use this strategy set `strategy` to `freshness` and `timeout` to `0u` in `cacheConfig`.
|
||||
|
||||
This essentially does the following:
|
||||
|
||||
1. Try to fetch from the network first.
|
||||
1. If the network request does not complete after 0ms \(that is, immediately\), fall back to the cache \(ignoring cache age\).
|
||||
1. Once the network request completes, update the cache for future requests.
|
||||
1. If the resource does not exist in the cache, wait for the network request anyway.
|
||||
2. If the network request does not complete immediately, that is after a timeout of 0 ms, ignore the cache age and fall back to the cached value.
|
||||
3. Once the network request completes, update the cache for future requests.
|
||||
4. If the resource does not exist in the cache, wait for the network request anyway.
|
||||
|
||||
</div>
|
||||
|
||||
#### `cacheOpaqueResponses`
|
||||
##### `cacheOpaqueResponses`
|
||||
|
||||
Whether the Angular service worker should cache opaque responses or not.
|
||||
|
||||
@@ -329,8 +342,8 @@ If not specified, the default value depends on the data group's configured strat
|
||||
|
||||
| Strategies | Details |
|
||||
|:--- |:--- |
|
||||
| Groups with the `freshness` strategy | The default value is `true` \(cache opaque responses\). These groups will request the data anew every time, only falling back to the cached response when offline or on a slow network. Therefore, it doesn't matter if the service worker caches an error response. |
|
||||
| Groups with the `performance` strategy | The default value is `false` \(do not cache opaque responses\). These groups would continue to return a cached response until `maxAge` expires, even if the error was due to a temporary network or server issue. Therefore, it would be problematic for the service worker to cache an error response. |
|
||||
| Groups with the `freshness` strategy | The default value is `true` and the service worker caches opaque responses. These groups will request the data every time and only fall back to the cached response when offline or on a slow network. Therefore, it doesn't matter if the service worker caches an error response. |
|
||||
| Groups with the `performance` strategy | The default value is `false` and the service worker doesn't cache opaque responses. These groups would continue to return a cached response until `maxAge` expires, even if the error was due to a temporary network or server issue. Therefore, it would be problematic for the service worker to cache an error response. |
|
||||
|
||||
<div class="callout is-important">
|
||||
|
||||
@@ -344,27 +357,24 @@ If you are not able to implement CORS —for example, if you don't control t
|
||||
|
||||
</div>
|
||||
|
||||
### `cacheQueryOptions`
|
||||
#### `cacheQueryOptions`
|
||||
|
||||
See [assetGroups](#assetgroups) for details.
|
||||
|
||||
## `navigationUrls`
|
||||
### `navigationUrls`
|
||||
|
||||
This optional section enables you to specify a custom list of URLs that will be redirected to the index file.
|
||||
|
||||
### Handling navigation requests
|
||||
#### Handling navigation requests
|
||||
|
||||
The ServiceWorker redirects navigation requests that don't match any `asset` or `data` group to the specified [index file](#index-file).
|
||||
A request is considered to be a navigation request if:
|
||||
|
||||
* Its [mode](https://developer.mozilla.org/docs/Web/API/Request/mode) is `navigation`
|
||||
* It accepts a `text/html` response \(as determined by the value of the `Accept` header\)
|
||||
* Its URL matches certain criteria \(see the following\)
|
||||
|
||||
By default, these criteria are:
|
||||
|
||||
* The URL must not contain a file extension \(that is, a `.`\) in the last path segment
|
||||
* The URL must not contain `__`
|
||||
* It accepts a `text/html` response as determined by the value of the `Accept` header
|
||||
* Its URL matches the following criteria:
|
||||
* The URL must not contain a file extension \(that is, a `.`\) in the last path segment
|
||||
* The URL must not contain `__`
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -372,10 +382,10 @@ To configure whether navigation requests are sent through to the network or not,
|
||||
|
||||
</div>
|
||||
|
||||
### Matching navigation request URLs
|
||||
#### Matching navigation request URLs
|
||||
|
||||
While these default criteria are fine in most cases, it is sometimes desirable to configure different rules.
|
||||
For example, you might want to ignore specific routes \(that are not part of the Angular app\) and pass them through to the server.
|
||||
For example, you might want to ignore specific routes, such as those that are not part of the Angular app, and pass them through to the server.
|
||||
|
||||
This field contains an array of URLs and [glob-like](#glob-patterns) URL patterns that are matched at runtime.
|
||||
It can contain both negative patterns \(that is, patterns starting with `!`\) and non-negative patterns and URLs.
|
||||
@@ -398,7 +408,7 @@ If the field is omitted, it defaults to:
|
||||
|
||||
<a id="navigation-request-strategy"></a>
|
||||
|
||||
## `navigationRequestStrategy`
|
||||
### `navigationRequestStrategy`
|
||||
|
||||
This optional property enables you to configure how the service worker handles navigation requests:
|
||||
|
||||
@@ -413,7 +423,7 @@ This optional property enables you to configure how the service worker handles n
|
||||
| Possible values | Details |
|
||||
|:--- |:--- |
|
||||
| `'performance'` | The default setting. Serves the specified [index file](#index-file), which is typically cached. |
|
||||
| `'freshness'` | Passes the requests through to the network and falls back to the `performance` behavior when offline. This value is useful when the server redirects the navigation requests elsewhere using an HTTP redirect \(3xx status code\). Reasons for using this value include: <ul> <li> Redirecting to an authentication website when authentication is not handled by the application </li> <li> Redirecting specific URLs to avoid breaking existing links/bookmarks after a website redesign </li> <li> Redirecting to a different website, such as a server-status page, while a page is temporarily down </li> </ul> |
|
||||
| `'freshness'` | Passes the requests through to the network and falls back to the `performance` behavior when offline. This value is useful when the server redirects the navigation requests elsewhere using a `3xx` HTTP redirect status code. Reasons for using this value include: <ul> <li> Redirecting to an authentication website when authentication is not handled by the application </li> <li> Redirecting specific URLs to avoid breaking existing links/bookmarks after a website redesign </li> <li> Redirecting to a different website, such as a server-status page, while a page is temporarily down </li> </ul> |
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
|
||||
@@ -9,21 +9,21 @@ A basic understanding of the following:
|
||||
|
||||
* [Service Worker Communication](guide/service-worker-communications)
|
||||
|
||||
## Service worker and caching of app resources
|
||||
## Service worker and caching of application resources
|
||||
|
||||
Conceptually, imagine the Angular service worker as a forward cache or a CDN edge that is installed in the end user's web browser.
|
||||
The service worker's job is to satisfy requests made by the Angular application for resources or data from a local cache, without needing to wait for the network.
|
||||
Imagine the Angular service worker as a forward cache or a Content Delivery Network (CDN) edge that is installed in the end user's web browser.
|
||||
The service worker responds to requests made by the Angular application for resources or data from a local cache, without needing to wait for the network.
|
||||
Like any cache, it has rules for how content is expired and updated.
|
||||
|
||||
<a id="versions"></a>
|
||||
|
||||
### App versions
|
||||
### Application versions
|
||||
|
||||
In the context of an Angular service worker, a "version" is a collection of resources that represent a specific build of the Angular application.
|
||||
Whenever a new build of the application is deployed, the service worker treats that build as a new version of the application.
|
||||
This is true even if only a single file is updated.
|
||||
At any given time, the service worker might have multiple versions of the application in its cache and it might be serving them simultaneously.
|
||||
For more information, see the [App tabs](guide/service-worker-devops#tabs) section below.
|
||||
For more information, see the [Application tabs](guide/service-worker-devops#tabs) section.
|
||||
|
||||
To preserve application integrity, the Angular service worker groups all files into a version together.
|
||||
The files grouped into a version usually include HTML, JS, and CSS files.
|
||||
@@ -35,15 +35,15 @@ In this scenario, it is not valid to serve the old `index.html`, which calls `st
|
||||
|
||||
This file integrity is especially important when lazy loading modules.
|
||||
A JS bundle might reference many lazy chunks, and the filenames of the lazy chunks are unique to the particular build of the application.
|
||||
If a running application at version `X` attempts to load a lazy chunk, but the server has already updated to version `X + 1`, the lazy loading operation will fail.
|
||||
If a running application at version `X` attempts to load a lazy chunk, but the server has already updated to version `X + 1`, the lazy loading operation fails.
|
||||
|
||||
The version identifier of the application is determined by the contents of all resources, and it changes if any of them change.
|
||||
In practice, the version is determined by the contents of the `ngsw.json` file, which includes hashes for all known content.
|
||||
If any of the cached files change, the file's hash will change in `ngsw.json`, causing the Angular service worker to treat the active set of files as a new version.
|
||||
If any of the cached files change, the file's hash changes in `ngsw.json`. This change causes the Angular service worker to treat the active set of files as a new version.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
`ngsw.json` is the manifest file that is generated at build time based on `ngsw-config.json`.
|
||||
The build process creates the manifest file, `ngsw.json`, using information from `ngsw-config.json`.
|
||||
|
||||
</div>
|
||||
|
||||
@@ -52,25 +52,27 @@ With the versioning behavior of the Angular service worker, an application serve
|
||||
#### Update checks
|
||||
|
||||
Every time the user opens or refreshes the application, the Angular service worker checks for updates to the application by looking for updates to the `ngsw.json` manifest.
|
||||
If an update is found, it is downloaded and cached automatically, and will be served the next time the application is loaded.
|
||||
If an update is found, it is downloaded and cached automatically, and is served the next time the application is loaded.
|
||||
|
||||
### Resource integrity
|
||||
|
||||
One of the potential side effects of long caching is inadvertently caching an invalid resource.
|
||||
In a normal HTTP cache, a hard refresh or cache expiration limits the negative effects of caching an invalid file.
|
||||
A service worker ignores such constraints and effectively long caches the entire application.
|
||||
Consequently, it is essential that the service worker gets the correct content.
|
||||
One of the potential side effects of long caching is inadvertently caching a resource that's not valid.
|
||||
In a normal HTTP cache, a hard refresh or the cache expiring limits the negative effects of caching a file that's not valid.
|
||||
A service worker ignores such constraints and effectively long-caches the entire application.
|
||||
It's important that the service worker gets the correct content, so it keeps hashes of the resources to maintain their integrity.
|
||||
|
||||
#### Hashed content
|
||||
|
||||
To ensure resource integrity, the Angular service worker validates the hashes of all resources for which it has a hash.
|
||||
Typically for an application created with the [Angular CLI](cli), this is everything in the `dist` directory covered by the user's `src/ngsw-config.json` configuration.
|
||||
For an application created with the [Angular CLI](cli), this is everything in the `dist` directory covered by the user's `src/ngsw-config.json` configuration.
|
||||
|
||||
If a particular file fails validation, the Angular service worker attempts to re-fetch the content using a "cache-busting" URL parameter to eliminate the effects of browser or intermediate caching.
|
||||
If that content also fails validation, the service worker considers the entire version of the application to be invalid and it stops serving the application.
|
||||
If necessary, the service worker enters a safe mode where requests fall back on the network, opting not to use its cache if the risk of serving invalid, broken, or outdated content is high.
|
||||
If a particular file fails validation, the Angular service worker attempts to re-fetch the content using a "cache-busting" URL parameter to prevent browser or intermediate caching.
|
||||
If that content also fails validation, the service worker considers the entire version of the application to not be valid and stops serving the application.
|
||||
If necessary, the service worker enters a safe mode where requests fall back on the network. The service worker doesn't use its cache if there's a high risk of serving content that is broken, outdated, or not valid.
|
||||
|
||||
Hash mismatches can occur for a variety of reasons:
|
||||
|
||||
* Caching layers in between the origin server and the end user could serve stale content
|
||||
* Caching layers between the origin server and the end user could serve stale content
|
||||
* A non-atomic deployment could result in the Angular service worker having visibility of partially updated content
|
||||
* Errors during the build process could result in updated resources without `ngsw.json` being updated.
|
||||
The reverse could also happen resulting in an updated `ngsw.json` without updated resources.
|
||||
@@ -80,36 +82,36 @@ Hash mismatches can occur for a variety of reasons:
|
||||
The only resources that have hashes in the `ngsw.json` manifest are resources that were present in the `dist` directory at the time the manifest was built.
|
||||
Other resources, especially those loaded from CDNs, have content that is unknown at build time or are updated more frequently than the application is deployed.
|
||||
|
||||
If the Angular service worker does not have a hash to validate a given resource, it still caches its contents but it honors the HTTP caching headers by using a policy of "stale while revalidate".
|
||||
That is, when HTTP caching headers for a cached resource indicate that the resource has expired, the Angular service worker continues to serve the content and it attempts to refresh the resource in the background.
|
||||
If the Angular service worker does not have a hash to verify a resource is valid, it still caches its contents. At the same time, it honors the HTTP caching headers by using a policy of *stale while revalidate*.
|
||||
The Angular service worker continues to serve a resource even after its HTTP caching headers indicate
|
||||
that it is no longer valid. At the same time, it attempts to refresh the expired resource in the background.
|
||||
This way, broken unhashed resources do not remain in the cache beyond their configured lifetimes.
|
||||
|
||||
<a id="tabs"></a>
|
||||
|
||||
### App tabs
|
||||
### Application tabs
|
||||
|
||||
It can be problematic for an application if the version of resources it's receiving changes suddenly or without warning.
|
||||
See the [App versions](guide/service-worker-devops#versions) section above for a description of such issues.
|
||||
See the [Application versions](guide/service-worker-devops#versions) section for a description of such issues.
|
||||
|
||||
The Angular service worker provides a guarantee: a running application will continue to run the same version of the application.
|
||||
If another instance of the application is opened in a new web browser tab, then the most current version of the app is served.
|
||||
The Angular service worker provides a guarantee: a running application continues to run the same version of the application.
|
||||
If another instance of the application is opened in a new web browser tab, then the most current version of the application is served.
|
||||
As a result, that new tab can be running a different version of the application than the original tab.
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
**IMPORTANT**: <br />
|
||||
This guarantee is **stronger** than that provided by the normal web deployment model.
|
||||
Without a service worker, there is no guarantee that code lazily loaded later in a running application is from the same version as the initial code for the application.
|
||||
Without a service worker, there is no guarantee that lazily loaded code is from the same version as the application's initial code.
|
||||
|
||||
</div>
|
||||
|
||||
There are a few limited reasons why the Angular service worker might change the version of a running application.
|
||||
Some of them are error conditions:
|
||||
The Angular service worker might change the version of a running application under error conditions such as:
|
||||
|
||||
* The current version becomes invalid due to a failed hash
|
||||
* An unrelated error causes the service worker to enter safe mode; that is, temporary deactivation
|
||||
* The current version becomes non-valid due to a failed hash
|
||||
* An unrelated error causes the service worker to enter safe mode and deactivates it temporarily
|
||||
|
||||
The Angular service worker is aware of which versions are in use at any given moment and it cleans up versions when no tab is using them.
|
||||
The Angular service worker cleans up application versions when no tab is using them.
|
||||
|
||||
Other reasons the Angular service worker might change the version of a running application are normal events:
|
||||
|
||||
@@ -119,27 +121,35 @@ Other reasons the Angular service worker might change the version of a running a
|
||||
### Service worker updates
|
||||
|
||||
The Angular service worker is a small script that runs in web browsers.
|
||||
From time to time, the service worker will be updated with bug fixes and feature improvements.
|
||||
From time to time, the service worker is updated with bug fixes and feature improvements.
|
||||
|
||||
The Angular service worker is downloaded when the application is first opened and when the application is accessed after a period of inactivity.
|
||||
If the service worker has changed, the service worker will be updated in the background.
|
||||
If the service worker changes, it's updated in the background.
|
||||
|
||||
Most updates to the Angular service worker are transparent to the app —the old caches are still valid and content is still served
|
||||
normally.
|
||||
However, occasionally a bugfix or feature in the Angular service worker requires the invalidation of old caches.
|
||||
In this case, the application will be refreshed transparently from the network.
|
||||
Most updates to the Angular service worker are transparent to the application. The old caches are still valid and content is still served normally.
|
||||
Occasionally, a bug fix or feature in the Angular service worker might require the invalidation of old caches.
|
||||
In this case, the service worker transparently refreshes the application from the network.
|
||||
|
||||
### Bypassing the service worker
|
||||
|
||||
In some cases, you might want to bypass the service worker entirely and let the browser handle the request instead.
|
||||
An example is when you rely on a feature that is currently not supported in service workers \(for example, [reporting progress on uploaded files](https://github.com/w3c/ServiceWorker/issues/1141)\).
|
||||
In some cases, you might want to bypass the service worker entirely and let the browser handle the request.
|
||||
An example is when you rely on a feature that is currently not supported in service workers, such as [reporting progress on uploaded files](https://github.com/w3c/ServiceWorker/issues/1141).
|
||||
|
||||
To bypass the service worker, set `ngsw-bypass` as a request header, or as a query parameter.
|
||||
\(The value of the header or query parameter is ignored and can be empty or omitted.\)
|
||||
The value of the header or query parameter is ignored and can be empty or omitted.
|
||||
|
||||
### Service worker requests when the server can't be reached
|
||||
|
||||
The service worker processes all requests unless the [service worker is explicitly bypassed](#bypassing-the-service-worker).
|
||||
The service worker either returns a cached response or sends the request to the server, depending on the state and configuration of the cache.
|
||||
The service worker only caches responses to non-mutating requests, such as `GET` and `HEAD`.
|
||||
|
||||
If the service worker receives an error from the server or it doesn't receive a response, it returns an error status that indicates the result of the call.
|
||||
For example, if the service worker doesn't receive a response, it creates a [504 Gateway Timeout](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504) status to return. The `504` status in this example could be returned because the server is offline or the client is disconnected.
|
||||
|
||||
## Debugging the Angular service worker
|
||||
|
||||
Occasionally, it might be necessary to examine the Angular service worker in a running state to investigate issues or to ensure that it is operating as designed.
|
||||
Occasionally, it might be necessary to examine the Angular service worker in a running state to investigate issues or whether it's operating as designed.
|
||||
Browsers provide built-in tools for debugging service workers and the Angular service worker itself includes useful debugging features.
|
||||
|
||||
### Locating and analyzing debugging information
|
||||
@@ -187,7 +197,7 @@ There are two possible degraded states:
|
||||
|
||||
| Degraded states | Details |
|
||||
|:--- |:--- |
|
||||
| `EXISTING_CLIENTS_ONLY` | The service worker does not have a clean copy of the latest known version of the application. Older cached versions are safe to use, so existing tabs continue to run from cache, but new loads of the application will be served from the network. The service worker will try to recover from this state when a new version of the application is detected and installed \(that is, when a new `ngsw.json` is available\). |
|
||||
| `EXISTING_CLIENTS_ONLY` | The service worker does not have a clean copy of the latest known version of the application. Older cached versions are safe to use, so existing tabs continue to run from cache, but new loads of the application will be served from the network. The service worker will try to recover from this state when a new version of the application is detected and installed. This happens when a new `ngsw.json` is available. |
|
||||
| `SAFE_MODE` | The service worker cannot guarantee the safety of using cached data. Either an unexpected error occurred or all cached versions are invalid. All traffic will be served from the network, running as little service worker code as possible. |
|
||||
|
||||
In both cases, the parenthetical annotation provides the
|
||||
@@ -269,9 +279,9 @@ Debug log:
|
||||
|
||||
</code-example>
|
||||
|
||||
Errors that occur within the service worker will be logged here.
|
||||
Errors that occur within the service worker are logged here.
|
||||
|
||||
### Developer Tools
|
||||
### Developer tools
|
||||
|
||||
Browsers such as Chrome provide developer tools for interacting with service workers.
|
||||
Such tools can be powerful when used properly, but there are a few things to keep in mind.
|
||||
@@ -282,23 +292,28 @@ Such tools can be powerful when used properly, but there are a few things to kee
|
||||
* If you look in the Cache Storage viewer, the cache is frequently out of date.
|
||||
Right click the Cache Storage title and refresh the caches.
|
||||
|
||||
* Stopping and starting the service worker in the Service Worker pane triggers a check for updates
|
||||
* Stopping and starting the service worker in the Service Worker pane checks for updates
|
||||
|
||||
## Service Worker Safety
|
||||
## Service worker safety
|
||||
|
||||
Like any complex system, bugs or broken configurations can cause the Angular service worker to act in unforeseen ways.
|
||||
While its design attempts to minimize the impact of such problems, the Angular service worker contains several failsafe mechanisms in case an administrator ever needs to deactivate the service worker quickly.
|
||||
Bugs or broken configurations could cause the Angular service worker to act in unexpected ways.
|
||||
If this happens, the Angular service worker contains several failsafe mechanisms in case an administrator needs to deactivate the service worker quickly.
|
||||
|
||||
### Fail-safe
|
||||
|
||||
To deactivate the service worker, remove or rename the `ngsw.json` file.
|
||||
When the service worker's request for `ngsw.json` returns a `404`, then the service worker removes all of its caches and de-registers itself, essentially self-destructing.
|
||||
To deactivate the service worker, rename the `ngsw.json` file or delete it.
|
||||
When the service worker's request for `ngsw.json` returns a `404`, then the service worker removes all its caches and de-registers itself, essentially self-destructing.
|
||||
|
||||
### Safety Worker
|
||||
### Safety worker
|
||||
|
||||
Also included in the `@angular/service-worker` NPM package is a small script `safety-worker.js`, which when loaded will unregister itself from the browser and remove the service worker caches.
|
||||
<!-- vale Angular.Google_Acronyms = NO -->
|
||||
|
||||
A small script, `safety-worker.js`, is also included in the `@angular/service-worker` NPM package.
|
||||
When loaded, it un-registers itself from the browser and removes the service worker caches.
|
||||
This script can be used as a last resort to get rid of unwanted service workers already installed on client pages.
|
||||
|
||||
<!-- vale Angular.Google_Acronyms = YES -->
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
**IMPORTANT**: <br />
|
||||
@@ -306,11 +321,11 @@ You cannot register this worker directly, as old clients with cached state might
|
||||
|
||||
</div>
|
||||
|
||||
Instead, you must serve the contents of `safety-worker.js` at the URL of the Service Worker script you are trying to unregister, and must continue to do so until you are certain all users have successfully unregistered the old worker.
|
||||
Instead, you must serve the contents of `safety-worker.js` at the URL of the Service Worker script you are trying to unregister. You must continue to do so until you are certain all users have successfully unregistered the old worker.
|
||||
For most sites, this means that you should serve the safety worker at the old Service Worker URL forever.
|
||||
This script can be used both to deactivate `@angular/service-worker` \(and remove the corresponding caches\) as well as any other Service Workers which might have been served in the past on your site.
|
||||
This script can be used to deactivate `@angular/service-worker` and remove the corresponding caches. It also removes any other Service Workers which might have been served in the past on your site.
|
||||
|
||||
### Changing your app's location
|
||||
### Changing your application's location
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
@@ -321,11 +336,11 @@ You might have already encountered the error `The script resource is behind a re
|
||||
</div>
|
||||
|
||||
This can be a problem if you have to change your application's location.
|
||||
If you setup a redirect from the old location \(for example `example.com`\) to the new location \(for example `www.example.com`\) the worker will stop working.
|
||||
If you setup a redirect from the old location, such as `example.com`, to the new location, `www.example.com` in this example, the worker stops working.
|
||||
Also, the redirect won't even trigger for users who are loading the site entirely from Service Worker.
|
||||
The old worker \(registered at `example.com`\) tries to update and sends requests to the old location `example.com` which get redirected to the new location `www.example.com` and create the error `The script resource is behind a redirect, which is disallowed`.
|
||||
The old worker, which was registered at `example.com`, tries to update and sends a request to the old location `example.com`. This request is redirected to the new location `www.example.com` and creates the error: `The script resource is behind a redirect, which is disallowed`.
|
||||
|
||||
To remedy this, you might need to deactivate the old worker using one of the above techniques \([Fail-safe](#fail-safe) or [Safety Worker](#safety-worker)\).
|
||||
To remedy this, you might need to deactivate the old worker using one of the preceding techniques: [Fail-safe](#fail-safe) or [Safety Worker](#safety-worker).
|
||||
|
||||
## More on Angular service workers
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This guide is about structural directives and provides conceptual information on how such directives work, how Angular interprets their shorthand syntax, and how to add template guard properties to catch template type errors.
|
||||
|
||||
Structural directives are directives which change the DOM layout by adding and removing DOM element.
|
||||
Structural directives are directives which change the DOM layout by adding and removing DOM elements.
|
||||
|
||||
Angular provides a set of built-in structural directives (such as `NgIf`, `NgForOf`, `NgSwitch` and others) which are commonly used in all Angular projects. For more information see [Built-in directives](guide/built-in-directives).
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ Debug specs in the browser in the same way that you debug an application.
|
||||
1. Reveal the Karma browser window.
|
||||
See [Set up testing](guide/testing#set-up-testing) if you need help with this step.
|
||||
|
||||
1. Click the **DEBUG** button; it opens a new browser tab and re-runs the tests.
|
||||
1. Open the browser's "Developer Tools" \(`Ctrl-Shift-I` on Windows; `Command-Option-I` in macOS\).
|
||||
1. Pick the "sources" section.
|
||||
1. Open the `1st.spec.ts` test file \(Control/Command-P, then start typing the name of the file\).
|
||||
1. Set a breakpoint in the test.
|
||||
1. Refresh the browser, and it stops at the breakpoint.
|
||||
1. Click the **DEBUG** button to open a new browser tab and re-run the tests.
|
||||
2. Open the browser's **Developer Tools**. On Windows, press `Ctrl-Shift-I`. On macOS, press `Command-Option-I`.
|
||||
3. Pick the **Sources** section.
|
||||
4. Press `Control/Command-P`, and then start typing the name of your test file to open it.
|
||||
5. Set a breakpoint in the test.
|
||||
6. Refresh the browser, and notice how it stops at the breakpoint.
|
||||
|
||||
<div class="lightbox">
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ To adequately test a component, you should test that they work together as inten
|
||||
Such tests require creating the component's host element in the browser DOM, as Angular does, and investigating the component class's interaction with the DOM as described by its template.
|
||||
|
||||
The Angular `TestBed` facilitates this kind of testing as you'll see in the following sections.
|
||||
But in many cases, *testing the component class alone*, without DOM involvement, can validate much of the component's behavior in an straightforward, more obvious way.
|
||||
But in many cases, *testing the component class alone*, without DOM involvement, can validate much of the component's behavior in a straightforward, more obvious way.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ email.reset();
|
||||
console.log(email.value); // null
|
||||
```
|
||||
|
||||
TypeScript will enforce that you always handle the possibility that the control has become `null`. If you want to make this control non-nullable, you may use the `nonNullable` option. This will cause the control to reset to its intial value, instead of `null`:
|
||||
TypeScript will enforce that you always handle the possibility that the control has become `null`. If you want to make this control non-nullable, you may use the `nonNullable` option. This will cause the control to reset to its initial value, instead of `null`:
|
||||
|
||||
```ts
|
||||
const email = new FormControl('angularrox@gmail.com', {nonNullable: true});
|
||||
|
||||
@@ -1687,7 +1687,6 @@ And for the phone list component, a few adjustments to the router make the `Rout
|
||||
[AioGuideAotCompiler]: guide/aot-compiler "Ahead-of-time (AOT) compilation | Angular"
|
||||
[AioGuideBuiltInDirectives]: guide/built-in-directives "Built-in directives | Angular"
|
||||
[AioGuideDependencyInjection]: guide/dependency-injection "Dependency injection in Angular | Angular"
|
||||
[AioGuideDependencyInjectionProvidersFactoryProviders]: guide/dependency-injection-providers#factory-providers "Using factory providers - Dependency providers | Angular"
|
||||
[AioGuideGlossaryLazyLoading]: guide/glossary#lazy-loading "lazy loading - Glossary | Angular"
|
||||
[AioGuideHierarchicalDependencyInjection]: guide/hierarchical-dependency-injection "Hierarchical injectors | Angular"
|
||||
[AioGuideLifecycleHooks]: guide/lifecycle-hooks "Lifecycle hooks | Angular"
|
||||
|
||||
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 361 B |
|
After Width: | Height: | Size: 622 B |
|
After Width: | Height: | Size: 508 B |
|
After Width: | Height: | Size: 447 B |
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 50 KiB |
@@ -101,6 +101,14 @@
|
||||
"bio": "SDE III @cisco | GDE for Angular | Microsoft MVP | Author | Speaker | Passionate Programmer",
|
||||
"groups": ["GDE"]
|
||||
},
|
||||
"antoniocardenas": {
|
||||
"name": "Antonio Cardenas",
|
||||
"picture": "antonio-cardenas.jpg",
|
||||
"twitter": "yeoudev",
|
||||
"website": "https://yeou.dev",
|
||||
"bio": "Antonio is a senior Angular developer from Honduras, focus on immersive experiences and Web Performance, He is a public Speaker, Mentor and Facilitator for entrepreneurship programs as Techstars Startup Weekend, Content Creator and tech writer Cofounder of non profit organization @Macaotech, Love to share knowledge Typescript Coffe and Cats 🐱.",
|
||||
"groups": ["GDE"]
|
||||
},
|
||||
"armenvardanyan95": {
|
||||
"name": "Armen Vardanyan",
|
||||
"picture": "armen-vardanyan.jpg",
|
||||
@@ -161,6 +169,15 @@
|
||||
"bio": "Brian is a software engineer and GDE in Angular with a passion for learning, writing, speaking, teaching and mentoring. Brian has been building web applications for over 20 years and has long been a fanboy of JavaScript. When not in front of his Macbook Pro Brian is in the Rocky Mountains skiing or hiking.",
|
||||
"groups": ["GDE"]
|
||||
},
|
||||
"bobwatson": {
|
||||
"name": "Bob Watson",
|
||||
"lead": "mgechev",
|
||||
"picture": "bob-watson.jpg",
|
||||
"twitter": "bobwatsonphd",
|
||||
"website": "https://docsbydesign.com",
|
||||
"bio": "Bob is the documentation lead for angular.io. When he's not obsessing over the i-dotting and t-crossing of documentation, he enjoys seeing the world from up in the air and below the sea.",
|
||||
"groups": ["Angular"]
|
||||
},
|
||||
"brocco": {
|
||||
"name": "Mike Brocchi",
|
||||
"picture": "mike-brocchi.jpg",
|
||||
@@ -845,6 +862,15 @@
|
||||
"twitter": "SiddAjmera",
|
||||
"bio": "Siddharth is a Full Stack JavaScript Developer and a GDE in Angular. He's passionate about sharing his knowledge on Angular, Firebase and the Web in general. He's the organizer of WebStack, a local community of developers focused on Web, Mobile, Voice and Server related technologies in general. WebStack hosts free monthly meetups every 2nd or 3rd Saturday of the month. Siddharth is also an avid photographer and loves traveling. Find him anywhere on the Web with `SiddAjmera`.",
|
||||
"groups": ["GDE"]
|
||||
},
|
||||
"simonaco": {
|
||||
"name": "Simona Cotin",
|
||||
"picture": "simonacotin.jpg",
|
||||
"twitter": "simona_cotin",
|
||||
"website": "https://github.com/simonaco",
|
||||
"bio": "Engineering manager working on Angular Framework and Tooling",
|
||||
"groups": ["Angular"],
|
||||
"lead": "madleinas"
|
||||
},
|
||||
"simpulton": {
|
||||
"name": "Lukas Ruebbelke",
|
||||
@@ -899,12 +925,6 @@
|
||||
"bio": "Andrew is a software engineer using Angular and .NET. He spends most of his spare time staying up-to-date, helping other people, and experimenting with web tech.",
|
||||
"groups": ["GDE"]
|
||||
},
|
||||
"tmdavisgoogle": {
|
||||
"name": "Tiffany Davis",
|
||||
"bio": "Tiffany is an editor working on Angular documentation.",
|
||||
"groups": ["Angular"],
|
||||
"lead": "mgechev"
|
||||
},
|
||||
"toddmotto": {
|
||||
"name": "Todd Motto",
|
||||
"picture": "toddmotto.jpg",
|
||||
|
||||
@@ -1,4 +1,30 @@
|
||||
[
|
||||
{
|
||||
"name": "NG Poland",
|
||||
"location": "Warsaw, Poland",
|
||||
"linkUrl": "https://ng-poland.pl/",
|
||||
"date": {
|
||||
"start": "2022-10-25",
|
||||
"end": "2022-10-25"
|
||||
},
|
||||
"workshopsDate": {
|
||||
"start": "2022-10-24",
|
||||
"end": "2022-10-24"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "JS Poland",
|
||||
"location": "Warsaw, Poland",
|
||||
"linkUrl": "https://js-poland.pl/",
|
||||
"date": {
|
||||
"start": "2022-10-26",
|
||||
"end": "2022-10-26"
|
||||
},
|
||||
"workshopsDate": {
|
||||
"start": "2022-10-24",
|
||||
"end": "2022-10-24"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "NG-DE",
|
||||
"location": "Berlin, Germany",
|
||||
|
||||
@@ -84,6 +84,18 @@
|
||||
"title": "Angular Rocks",
|
||||
"url": "https://angularrocks.com"
|
||||
},
|
||||
"angular-master-podcast": {
|
||||
"desc": "Listen / Code / Repeat. Everything you need to fall in love with Angular for a long time.",
|
||||
"logo": "https://ng-poland.pl/images/podcast/angular-master-podcast-logo.png",
|
||||
"title": "Angular Master Podcast",
|
||||
"url": "https://anchor.fm/angular-master/"
|
||||
},
|
||||
"javascript-master-podcast": {
|
||||
"desc": "Listen / Code / Repeat. Everything you need to know to become an informed JavaScript developer.",
|
||||
"logo": "https://ng-poland.pl/images/podcast/javascript-master-podcast-logo.png",
|
||||
"title": "JavaScript Master Podcast",
|
||||
"url": "https://anchor.fm/javascript-master/"
|
||||
},
|
||||
"happy-angular": {
|
||||
"desc": "A weekly German podcast for Angular on the go",
|
||||
"logo": "",
|
||||
@@ -128,7 +140,7 @@
|
||||
"desc": "NativeScript is how you build cross-platform, native iOS and Android apps with Angular and TypeScript. Get 100% access to native APIs via JavaScript and reuse of packages from NPM, CocoaPods and Gradle. Open source and backed by Telerik.",
|
||||
"logo": "",
|
||||
"title": "NativeScript",
|
||||
"url": "https://docs.nativescript.org/angular/start/introduction"
|
||||
"url": "https://docs.nativescript.org/tutorial/angular.html"
|
||||
},
|
||||
"angular-miniprogram": {
|
||||
"desc": "MiniProgram Platform for Angular.",
|
||||
@@ -816,6 +828,11 @@
|
||||
"title": "Angular University",
|
||||
"url": "https://angular-university.io/"
|
||||
},
|
||||
"angular-master-dev": {
|
||||
"desc": "Angular Architecture: Strategic Design with Nx - Online Video Course",
|
||||
"title": "Angular Master",
|
||||
"url": "https://angularmaster.dev/"
|
||||
},
|
||||
"-acceleb": {
|
||||
"desc": "Customized, Instructor-Led Angular Training",
|
||||
"title": "Accelebrate",
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
"hidden": false
|
||||
},
|
||||
{
|
||||
"title": "Getting Started",
|
||||
"title": "Getting started",
|
||||
"tooltip": "Set up your environment and learn basic concepts",
|
||||
"children": [
|
||||
{
|
||||
@@ -76,12 +76,12 @@
|
||||
},
|
||||
{
|
||||
"url": "start/start-data",
|
||||
"title": "Managing Data",
|
||||
"title": "Managing data",
|
||||
"tooltip": "Use services and access external data via HTTP."
|
||||
},
|
||||
{
|
||||
"url": "start/start-forms",
|
||||
"title": "Using Forms for User Input",
|
||||
"title": "Using forms for user input",
|
||||
"tooltip": "Learn about fetching and managing data from users with forms."
|
||||
},
|
||||
{
|
||||
@@ -118,22 +118,22 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/lifecycle-hooks",
|
||||
"title": "Component Lifecycle",
|
||||
"title": "Component lifecycle",
|
||||
"tooltip": "Angular calls lifecycle hook methods on directives and components as it creates, changes, and destroys them."
|
||||
},
|
||||
{
|
||||
"url": "guide/view-encapsulation",
|
||||
"title": "View Encapsulation",
|
||||
"title": "View encapsulation",
|
||||
"tooltip": "Describes how component CSS styles are encapsulated into a component's view."
|
||||
},
|
||||
{
|
||||
"url": "guide/component-interaction",
|
||||
"title": "Component Interaction",
|
||||
"title": "Component interaction",
|
||||
"tooltip": "Share information between different directives and components."
|
||||
},
|
||||
{
|
||||
"url": "guide/component-styles",
|
||||
"title": "Component Styles",
|
||||
"title": "Component styles",
|
||||
"tooltip": "Add CSS styles that are specific to a component."
|
||||
},
|
||||
{
|
||||
@@ -143,17 +143,17 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/content-projection",
|
||||
"title": "Content Projection",
|
||||
"title": "Content projection",
|
||||
"tooltip": "Learn how to create reusable components using Angular's content projection feature."
|
||||
},
|
||||
{
|
||||
"url": "guide/dynamic-component-loader",
|
||||
"title": "Dynamic Components",
|
||||
"title": "Dynamic components",
|
||||
"tooltip": "Load components dynamically."
|
||||
},
|
||||
{
|
||||
"url": "guide/elements",
|
||||
"title": "Angular Elements",
|
||||
"title": "Angular elements",
|
||||
"tooltip": "Convert components to Custom Elements."
|
||||
}
|
||||
]
|
||||
@@ -262,29 +262,44 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/attribute-directives",
|
||||
"title": "Attribute Directives",
|
||||
"title": "Attribute directives",
|
||||
"tooltip": "Attribute directives attach behavior to elements."
|
||||
},
|
||||
{
|
||||
"url": "guide/structural-directives",
|
||||
"title": "Structural Directives",
|
||||
"title": "Structural directives",
|
||||
"tooltip": "Structural directives manipulate the layout of the page."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Dependency Injection",
|
||||
"tooltip": "Dependency Injection: creating and injecting services",
|
||||
"title": "Dependency injection",
|
||||
"tooltip": "Dependency injection: creating and injecting services",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/dependency-injection-overview",
|
||||
"title": "Dependency injection in Angular",
|
||||
"tooltip": "Dependency injection is a design pattern in which a class requests dependencies from external sources."
|
||||
},
|
||||
{
|
||||
"url": "guide/dependency-injection",
|
||||
"title": "Angular Dependency Injection",
|
||||
"title": "Understanding dependency injection",
|
||||
"tooltip": "Angular's dependency injection system creates and delivers dependent services to Angular-created classes."
|
||||
},
|
||||
{
|
||||
"url": "guide/creating-injectable-service",
|
||||
"title": "Creating an injectable service",
|
||||
"tooltip": "Example of creating an injectable service."
|
||||
},
|
||||
{
|
||||
"url": "guide/dependency-injection-providers",
|
||||
"title": "DI Providers",
|
||||
"tooltip": "More about the different kinds of providers."
|
||||
"title": "Defining dependency providers",
|
||||
"tooltip": "A provider factory function is a plain function that Angular can call to create a dependency."
|
||||
},
|
||||
{
|
||||
"url": "guide/hierarchical-dependency-injection",
|
||||
"title": "Hierarchical injectors",
|
||||
"tooltip": "Hierarchical dependency injection enables you to share dependencies between different parts of the application only when and if you need to."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -310,23 +325,23 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/change-detection-zone-pollution",
|
||||
"title": "Zone Pollution",
|
||||
"title": "Zone pollution",
|
||||
"tooltip": "Explains how Zone.js works and what are the common pitfalls that can slow an application down"
|
||||
},
|
||||
{
|
||||
"url": "guide/change-detection-slow-computations",
|
||||
"title": "Slow Computations",
|
||||
"title": "Slow computations",
|
||||
"tooltip": "Learn how to make your application faster by discovering and optimizing slow calculations"
|
||||
},
|
||||
{
|
||||
"url": "guide/change-detection-skipping-subtrees",
|
||||
"title": "Skipping Component Subtrees",
|
||||
"title": "Skipping component subtrees",
|
||||
"tooltip": "Learn how to skip change detection in component subtrees using OnPush change detection strategy"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Routing and Navigation",
|
||||
"title": "Routing and navigation",
|
||||
"tooltip": "Learn how to use Angular Router to build in-app navigation between views.",
|
||||
"children": [
|
||||
{
|
||||
@@ -341,17 +356,17 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/router-tutorial",
|
||||
"title": "Tutorial: Routing in Single-page Applications",
|
||||
"title": "Tutorial: routing in single-page applications",
|
||||
"tooltip": "A tutorial that covers many patterns associated with Angular routing."
|
||||
},
|
||||
{
|
||||
"url": "guide/routing-with-urlmatcher",
|
||||
"title": "Tutorial: Creating custom route matches",
|
||||
"title": "Tutorial: creating custom route matches",
|
||||
"tooltip": "Learn how to create a custom URL matcher with the Angular router."
|
||||
},
|
||||
{
|
||||
"url": "guide/router-tutorial-toh",
|
||||
"title": "Tutorial: Adding routing to Tour of Heroes",
|
||||
"title": "Tutorial: adding routing to Tour of Heroes",
|
||||
"tooltip": "Explore how to use Angular's router. Based on the Tour of Heroes example."
|
||||
},
|
||||
{
|
||||
@@ -372,12 +387,12 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/reactive-forms",
|
||||
"title": "Reactive Forms",
|
||||
"title": "Reactive forms",
|
||||
"tooltip": "Create a reactive form using FormBuilder, groups, and arrays."
|
||||
},
|
||||
{
|
||||
"url": "guide/typed-forms",
|
||||
"title": "Strictly Typed Reactive Forms In Depth",
|
||||
"title": "Strictly typed reactive forms in depth",
|
||||
"tooltip": "A deep-dive into the reactive forms types."
|
||||
},
|
||||
{
|
||||
@@ -387,14 +402,14 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/dynamic-form",
|
||||
"title": "Building Dynamic Forms",
|
||||
"title": "Building dynamic forms",
|
||||
"tooltip": "Create dynamic form templates using FormGroup."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "guide/http",
|
||||
"title": "HTTP Client",
|
||||
"title": "HTTP client",
|
||||
"tooltip": "Use HTTP to talk to a remote server."
|
||||
},
|
||||
{
|
||||
@@ -403,47 +418,47 @@
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/testing",
|
||||
"title": "Intro to Testing",
|
||||
"title": "Intro to testing",
|
||||
"tooltip": "Introduction to testing an Angular app."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-code-coverage",
|
||||
"title": "Code Coverage",
|
||||
"title": "Code coverage",
|
||||
"tooltip": "Determine how much of your code is tested."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-services",
|
||||
"title": "Testing Services",
|
||||
"title": "Testing services",
|
||||
"tooltip": "How to test services."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-components-basics",
|
||||
"title": "Basics of Testing Components",
|
||||
"title": "Basics of testing components",
|
||||
"tooltip": "The fundamentals of how to test components."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-components-scenarios",
|
||||
"title": "Component Testing Scenarios",
|
||||
"title": "Component testing scenarios",
|
||||
"tooltip": "Use cases for testing components."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-attribute-directives",
|
||||
"title": "Testing Attribute Directives",
|
||||
"title": "Testing attribute directives",
|
||||
"tooltip": "How to test attribute directives."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-pipes",
|
||||
"title": "Testing Pipes",
|
||||
"title": "Testing pipes",
|
||||
"tooltip": "Writing tests for pipes."
|
||||
},
|
||||
{
|
||||
"url": "guide/test-debugging",
|
||||
"title": "Debugging Tests",
|
||||
"title": "Debugging tests",
|
||||
"tooltip": "How to debug tests."
|
||||
},
|
||||
{
|
||||
"url": "guide/testing-utility-apis",
|
||||
"title": "Testing Utility APIs",
|
||||
"title": "Testing utility APIs",
|
||||
"tooltip": "Features of the Angular testing utilities."
|
||||
}
|
||||
]
|
||||
@@ -562,7 +577,7 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/route-animations",
|
||||
"title": "Route Transition Animations",
|
||||
"title": "Route transition animations",
|
||||
"tooltip": "Animate route transitions."
|
||||
}
|
||||
]
|
||||
@@ -578,44 +593,44 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/service-worker-getting-started",
|
||||
"title": "Getting Started",
|
||||
"title": "Getting started",
|
||||
"tooltip": "Enabling the service worker in a CLI project and observing behavior in the browser."
|
||||
},
|
||||
{
|
||||
"url": "guide/app-shell",
|
||||
"title": "App Shell",
|
||||
"title": "App shell",
|
||||
"tooltip": "Render a portion of your app quickly to improve the startup experience."
|
||||
},
|
||||
{
|
||||
"url": "guide/service-worker-communications",
|
||||
"title": "Service Worker Communication",
|
||||
"title": "Service worker communication",
|
||||
"tooltip": "Services that enable you to interact with an Angular service worker."
|
||||
},
|
||||
{
|
||||
"url": "guide/service-worker-notifications",
|
||||
"title": "Service Worker Notifications",
|
||||
"title": "Service worker notifications",
|
||||
"tooltip": "Configuring service worker notification behavior."
|
||||
},
|
||||
{
|
||||
"url": "guide/service-worker-devops",
|
||||
"title": "Service Worker in Production",
|
||||
"title": "Service worker in production",
|
||||
"tooltip": "Running apps with service workers, managing app update, debugging, and killing apps."
|
||||
},
|
||||
{
|
||||
"url": "guide/service-worker-config",
|
||||
"title": "Service Worker Configuration",
|
||||
"title": "Service worker configuration",
|
||||
"tooltip": "Configuring service worker caching behavior."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "guide/web-worker",
|
||||
"title": "Web Workers",
|
||||
"title": "Web workers",
|
||||
"tooltip": "Using web workers for background processing."
|
||||
},
|
||||
{
|
||||
"url": "guide/universal",
|
||||
"title": "Server-side Rendering",
|
||||
"title": "Server-side rendering",
|
||||
"tooltip": "Render HTML server-side with Angular Universal."
|
||||
},
|
||||
{
|
||||
@@ -626,7 +641,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Best Practices",
|
||||
"title": "Best practices",
|
||||
"tooltip": "Learn how to build robust, scalable applications.",
|
||||
"children": [
|
||||
{
|
||||
@@ -641,32 +656,32 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/updating",
|
||||
"title": "Keeping Up-to-Date",
|
||||
"title": "Keeping up-to-date",
|
||||
"tooltip": "Information about updating Angular applications and libraries to the latest version."
|
||||
},
|
||||
{
|
||||
"url": "guide/property-binding-best-practices",
|
||||
"title": "Property Binding Best Practices",
|
||||
"title": "Property binding best practices",
|
||||
"tooltip": "Use property binding efficiently."
|
||||
},
|
||||
{
|
||||
"url": "guide/lazy-loading-ngmodules",
|
||||
"title": "Lazy Loading Feature Modules",
|
||||
"title": "Lazy loading feature modules",
|
||||
"tooltip": "Lazy load modules to speed up your apps."
|
||||
},
|
||||
{
|
||||
"url": "guide/lightweight-injection-tokens",
|
||||
"title": "Lightweight Injection Tokens for Libraries",
|
||||
"title": "Lightweight injection tokens for libraries",
|
||||
"tooltip": "Optimize client app size by designing library services with lightweight injection tokens."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Angular Tools",
|
||||
"title": "Angular tools",
|
||||
"tooltip": "Tools to help you build your Angular applications.",
|
||||
"children": [
|
||||
{
|
||||
"title": "Dev Workflow",
|
||||
"title": "Development workflow",
|
||||
"tooltip": "Build, and deployment information.",
|
||||
"children": [
|
||||
{
|
||||
@@ -675,46 +690,46 @@
|
||||
"tooltip": "Learn how to deploy your Angular app."
|
||||
},
|
||||
{
|
||||
"title": "AOT Compiler",
|
||||
"title": "AOT compiler",
|
||||
"tooltip": "Understanding ahead-of-time compilation.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/aot-compiler",
|
||||
"title": "Ahead-of-Time Compilation",
|
||||
"title": "Ahead-of-Time compilation",
|
||||
"tooltip": "Learn why and how to use the Ahead-of-Time (AOT) compiler."
|
||||
},
|
||||
{
|
||||
"url": "guide/angular-compiler-options",
|
||||
"title": "Angular Compiler Options",
|
||||
"title": "Angular compiler options",
|
||||
"tooltip": "Configuring AOT compilation."
|
||||
},
|
||||
{
|
||||
"url": "guide/aot-metadata-errors",
|
||||
"title": "AOT Metadata Errors",
|
||||
"title": "AOT metadata errors",
|
||||
"tooltip": "Troubleshooting AOT compilation."
|
||||
},
|
||||
{
|
||||
"url": "guide/template-typecheck",
|
||||
"title": "Template Type-checking",
|
||||
"title": "Template type-checking",
|
||||
"tooltip": "Template type-checking in Angular."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "guide/build",
|
||||
"title": "Building & Serving",
|
||||
"title": "Building & serving",
|
||||
"tooltip": "Building and serving Angular apps."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "guide/cli-builder",
|
||||
"title": "CLI Builders",
|
||||
"title": "CLI builders",
|
||||
"tooltip": "Using builders to customize Angular CLI."
|
||||
},
|
||||
{
|
||||
"url": "guide/language-service",
|
||||
"title": "Language Service",
|
||||
"title": "Language service",
|
||||
"tooltip": "Use Angular Language Service to speed up dev time."
|
||||
},
|
||||
{
|
||||
@@ -733,12 +748,12 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/schematics-authoring",
|
||||
"title": "Authoring Schematics",
|
||||
"title": "Authoring schematics",
|
||||
"tooltip": "Understand the structure of a schematic."
|
||||
},
|
||||
{
|
||||
"url": "guide/schematics-for-libraries",
|
||||
"title": "Schematics for Libraries",
|
||||
"title": "Schematics for libraries",
|
||||
"tooltip": "Use schematics to integrate your library with the Angular CLI."
|
||||
}
|
||||
]
|
||||
@@ -760,68 +775,68 @@
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt0",
|
||||
"title": "Create a Project",
|
||||
"title": "Create a project",
|
||||
"tooltip": "Creating the application shell"
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt1",
|
||||
"title": "1. The Hero Editor",
|
||||
"title": "1. The hero editor",
|
||||
"tooltip": "Part 1: Build a simple editor"
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt2",
|
||||
"title": "2. Display a List",
|
||||
"title": "2. Display a list",
|
||||
"tooltip": "Part 2: Build a master/detail page with a list of heroes."
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt3",
|
||||
"title": "3. Create a Feature Component",
|
||||
"title": "3. Create a feature component",
|
||||
"tooltip": "Part 3: Refactor the master/detail views into separate components."
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt4",
|
||||
"title": "4. Add Services",
|
||||
"title": "4. Add services",
|
||||
"tooltip": "Part 4: Create a reusable service to manage hero data."
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt5",
|
||||
"title": "5. Add Navigation",
|
||||
"title": "5. Add navigation",
|
||||
"tooltip": "Part 5: Add the Angular router and navigate among the views."
|
||||
},
|
||||
{
|
||||
"url": "tutorial/toh-pt6",
|
||||
"title": "6. Get Data from a Server",
|
||||
"title": "6. Get data from a server",
|
||||
"tooltip": "Part 6: Use HTTP to retrieve and save hero data."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "guide/forms",
|
||||
"title": "Building a Template-driven Form",
|
||||
"title": "Building a template-driven form",
|
||||
"tooltip": "Create a template-driven form using directives and Angular template syntax."
|
||||
},
|
||||
{
|
||||
"title": "Angular Libraries",
|
||||
"title": "Angular libraries",
|
||||
"tooltip": "Extending Angular with shared libraries.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/libraries",
|
||||
"title": "Libraries Overview",
|
||||
"title": "Libraries overview",
|
||||
"tooltip": "Understand how and when to use or create libraries."
|
||||
},
|
||||
{
|
||||
"url": "guide/using-libraries",
|
||||
"title": "Using Published Libraries",
|
||||
"title": "Using published libraries",
|
||||
"tooltip": "Integrate published libraries into an app."
|
||||
},
|
||||
{
|
||||
"url": "guide/creating-libraries",
|
||||
"title": "Creating Libraries",
|
||||
"title": "Creating libraries",
|
||||
"tooltip": "Extend Angular by creating, publishing, and using your own libraries."
|
||||
},
|
||||
{
|
||||
"url": "guide/angular-package-format",
|
||||
"title": "Angular Package Format",
|
||||
"title": "Angular package format",
|
||||
"tooltip": "Information about the Angular Package Format."
|
||||
}
|
||||
]
|
||||
@@ -845,7 +860,7 @@
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/releases",
|
||||
"title": "Release Practices",
|
||||
"title": "Release practices",
|
||||
"tooltip": "Angular versioning, release, support, and deprecation policies and practices."
|
||||
},
|
||||
{
|
||||
@@ -855,12 +870,12 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/browser-support",
|
||||
"title": "Browser Support",
|
||||
"title": "Browser support",
|
||||
"tooltip": "Browser support and polyfills guide."
|
||||
},
|
||||
{
|
||||
"url": "guide/update-to-latest-version",
|
||||
"title": "Update to Version 14",
|
||||
"title": "Update to version 14",
|
||||
"tooltip": "Support to update your application to version 14"
|
||||
},
|
||||
{
|
||||
@@ -874,22 +889,22 @@
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/upgrade",
|
||||
"title": "Upgrading Instructions",
|
||||
"title": "Upgrade instructions",
|
||||
"tooltip": "Incrementally upgrade an AngularJS application to Angular."
|
||||
},
|
||||
{
|
||||
"url": "guide/upgrade-setup",
|
||||
"title": "Setup for Upgrading from AngularJS",
|
||||
"title": "Setup for upgrading from AngularJS",
|
||||
"tooltip": "Use code from the Angular QuickStart seed as part of upgrading from AngularJS."
|
||||
},
|
||||
{
|
||||
"url": "guide/upgrade-performance",
|
||||
"title": "Upgrading for Performance",
|
||||
"title": "Upgrading for performance",
|
||||
"tooltip": "Upgrade from AngularJS to Angular in a more flexible way."
|
||||
},
|
||||
{
|
||||
"url": "guide/ajs-quick-reference",
|
||||
"title": "AngularJS-Angular Concepts",
|
||||
"title": "AngularJS to Angular concepts",
|
||||
"tooltip": "Learn how AngularJS concepts and techniques map to Angular."
|
||||
}
|
||||
]
|
||||
@@ -901,36 +916,31 @@
|
||||
"tooltip": "Reference guides for Angular features and tools.",
|
||||
"children": [
|
||||
{
|
||||
"title": "Conceptual Reference",
|
||||
"title": "Conceptual reference",
|
||||
"tooltip": "Reference documentation that explains how Angular features work.",
|
||||
"children": [
|
||||
{
|
||||
"title": "Angular Concepts",
|
||||
"title": "Angular concepts",
|
||||
"tooltip": "Introduction to basic concepts for Angular applications.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/architecture",
|
||||
"title": "Intro to Basic Concepts",
|
||||
"title": "Intro to basic concepts",
|
||||
"tooltip": "Basic building blocks of Angular applications."
|
||||
},
|
||||
{
|
||||
"url": "guide/architecture-modules",
|
||||
"title": "Intro to Modules",
|
||||
"title": "Intro to modules",
|
||||
"tooltip": "About NgModules."
|
||||
},
|
||||
{
|
||||
"url": "guide/architecture-components",
|
||||
"title": "Intro to Components",
|
||||
"title": "Intro to components",
|
||||
"tooltip": "About Components, Templates, and Views."
|
||||
},
|
||||
{
|
||||
"url": "guide/architecture-services",
|
||||
"title": "Intro to Services and DI",
|
||||
"tooltip": "About services and dependency injection."
|
||||
},
|
||||
{
|
||||
"url": "guide/architecture-next-steps",
|
||||
"title": "Next Steps",
|
||||
"title": "Next steps",
|
||||
"tooltip": "Beyond the basics."
|
||||
},
|
||||
{
|
||||
@@ -956,22 +966,22 @@
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/file-structure",
|
||||
"title": "Project File Structure",
|
||||
"title": "Project file structure",
|
||||
"tooltip": "How your Angular workspace looks on your filesystem."
|
||||
},
|
||||
{
|
||||
"url": "guide/workspace-config",
|
||||
"title": "Workspace Configuration",
|
||||
"title": "Workspace configuration",
|
||||
"tooltip": "The \"angular.json\" file contains workspace and project configuration defaults for Angular CLI commands."
|
||||
},
|
||||
{
|
||||
"url": "guide/npm-packages",
|
||||
"title": "npm Dependencies",
|
||||
"title": "npm dependencies",
|
||||
"tooltip": "Description of npm packages required at development time and at runtime."
|
||||
},
|
||||
{
|
||||
"url": "guide/typescript-configuration",
|
||||
"title": "TypeScript Configuration",
|
||||
"title": "TypeScript configuration",
|
||||
"tooltip": "TypeScript configuration for Angular developers."
|
||||
},
|
||||
{
|
||||
@@ -988,7 +998,7 @@
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/ngmodules",
|
||||
"title": "NgModules Introduction",
|
||||
"title": "NgModules introduction",
|
||||
"tooltip": "Use NgModules to make your apps efficient."
|
||||
},
|
||||
{
|
||||
@@ -998,37 +1008,37 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/bootstrapping",
|
||||
"title": "Launching Apps with a Root Module",
|
||||
"title": "Launching apps with a root module",
|
||||
"tooltip": "Tell Angular how to construct and bootstrap the app in the root \"AppModule\"."
|
||||
},
|
||||
{
|
||||
"url": "guide/frequent-ngmodules",
|
||||
"title": "Frequently Used NgModules",
|
||||
"title": "Frequently used NgModules",
|
||||
"tooltip": "Introduction to the most frequently used NgModules."
|
||||
},
|
||||
{
|
||||
"url": "guide/module-types",
|
||||
"title": "Types of Feature Modules",
|
||||
"title": "Types of feature modules",
|
||||
"tooltip": "Description of the different types of feature modules."
|
||||
},
|
||||
{
|
||||
"url": "guide/entry-components",
|
||||
"title": "Entry Components",
|
||||
"title": "Entry components",
|
||||
"tooltip": "All about entry components in Angular."
|
||||
},
|
||||
{
|
||||
"url": "guide/feature-modules",
|
||||
"title": "Feature Modules",
|
||||
"title": "Feature modules",
|
||||
"tooltip": "Create feature modules to organize your code."
|
||||
},
|
||||
{
|
||||
"url": "guide/providers",
|
||||
"title": "Providing Dependencies",
|
||||
"title": "Providing dependencies",
|
||||
"tooltip": "Providing dependencies to NgModules."
|
||||
},
|
||||
{
|
||||
"url": "guide/singleton-services",
|
||||
"title": "Singleton Services",
|
||||
"title": "Singleton services",
|
||||
"tooltip": "Creating singleton services."
|
||||
},
|
||||
{
|
||||
@@ -1054,12 +1064,12 @@
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/observables",
|
||||
"title": "Observables Overview",
|
||||
"title": "Observables overview",
|
||||
"tooltip": "Using observables to pass values synchronously or asynchronously."
|
||||
},
|
||||
{
|
||||
"url": "guide/rx-library",
|
||||
"title": "The RxJS Library",
|
||||
"title": "The RxJS library",
|
||||
"tooltip": "A library for reactive programming using observables to compose asynchronous or callback-based code."
|
||||
},
|
||||
{
|
||||
@@ -1069,12 +1079,12 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/practical-observable-usage",
|
||||
"title": "Practical Usage",
|
||||
"title": "Practical usage",
|
||||
"tooltip": "Domains in which observables are particularly useful."
|
||||
},
|
||||
{
|
||||
"url": "guide/comparing-observables",
|
||||
"title": "Compare to Other Techniques",
|
||||
"title": "Compare to other techniques",
|
||||
"tooltip": "How observables compare to promises and other message passing techniques."
|
||||
}
|
||||
]
|
||||
@@ -1083,14 +1093,9 @@
|
||||
"title": "Dependency injection",
|
||||
"tooltip": "Using dependency injection in Angular.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/hierarchical-dependency-injection",
|
||||
"title": "Hierarchical Injectors",
|
||||
"tooltip": "An injector tree parallels the component tree and supports nested dependencies."
|
||||
},
|
||||
{
|
||||
"url": "guide/dependency-injection-in-action",
|
||||
"title": "DI in Action",
|
||||
"title": "DI in action",
|
||||
"tooltip": "Techniques for dependency injection."
|
||||
}
|
||||
]
|
||||
@@ -1107,19 +1112,19 @@
|
||||
"url": "cli"
|
||||
},
|
||||
{
|
||||
"title": "Usage Analytics",
|
||||
"title": "Usage analytics",
|
||||
"tooltip": "For administrators, guide to gathering usage analytics from your users.",
|
||||
"url": "cli/usage-analytics-gathering"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "API Reference",
|
||||
"title": "API reference",
|
||||
"tooltip": "Details of the Angular packages, classes, interfaces, and other types.",
|
||||
"url": "api"
|
||||
},
|
||||
{
|
||||
"title": "Error Reference",
|
||||
"title": "Error reference",
|
||||
"tooltip": "Details of the errors that can be thrown by Angular.",
|
||||
"children": [
|
||||
{
|
||||
@@ -1130,7 +1135,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Extended Diagnostic Reference",
|
||||
"title": "Extended diagnostic reference",
|
||||
"tooltip": "Details of extended diagnostics that can be emitted by the Angular compiler",
|
||||
"children": [
|
||||
{
|
||||
@@ -1147,66 +1152,71 @@
|
||||
},
|
||||
{
|
||||
"url": "guide/glossary",
|
||||
"title": "Angular Glossary",
|
||||
"title": "Angular glossary",
|
||||
"tooltip": "Brief definitions of the most important words in the Angular vocabulary."
|
||||
},
|
||||
{
|
||||
"title": "Angular Style and Usage",
|
||||
"title": "Angular coding style",
|
||||
"tooltip": "Summaries of Angular syntax, coding, and doc styles.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/cheatsheet",
|
||||
"title": "Quick Reference",
|
||||
"title": "Quick reference",
|
||||
"tooltip": "A quick guide to common Angular coding techniques."
|
||||
},
|
||||
{
|
||||
"url": "guide/styleguide",
|
||||
"title": "Coding Style Guide",
|
||||
"title": "Coding style guide",
|
||||
"tooltip": "Guidelines for writing Angular code."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Documentation contributor guide",
|
||||
"tooltip": "Describes how to contribute to the Angular documentation.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/contributors-guide-overview",
|
||||
"title": "Overview",
|
||||
"tooltip": "Provides an overview of how to contribute to Angular's documentation."
|
||||
},
|
||||
{
|
||||
"title": "Content Contributor's Guide",
|
||||
"tooltip": "Describes how to contribute to the Angular documentation.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/contributors-guide-overview",
|
||||
"title": "Overview",
|
||||
"tooltip": "Provides an overview of how to contribute to Angular's documentation."
|
||||
},
|
||||
{
|
||||
"url": "guide/reviewing-content",
|
||||
"title": "Reviewing content",
|
||||
"tooltip": "Keep Angular content up-to-date by reviewing topics for accuracy."
|
||||
},
|
||||
{
|
||||
"url": "guide/updating-search-keywords",
|
||||
"title": "Updating search keywords",
|
||||
"tooltip": "Help users find the content they need by adding additional search keywords."
|
||||
},
|
||||
{
|
||||
"url": "guide/updating-content-github-ui",
|
||||
"title": "Updating content using GitHub UI",
|
||||
"tooltip": "Update topics in the browser through the GitHub user interface."
|
||||
},
|
||||
{
|
||||
"url": "guide/docs-style-guide",
|
||||
"title": "Documentation Style Guide",
|
||||
"tooltip": "Style guide for documentation authors."
|
||||
},
|
||||
{
|
||||
"url": "guide/localizing-angular",
|
||||
"title": "Angular doc localization guidelines",
|
||||
"tooltip": "Learn about the guidelines for localizing Angular documentation."
|
||||
},
|
||||
{
|
||||
"url": "guide/localized-documentation",
|
||||
"title": "Localized documentation",
|
||||
"tooltip": "A list of localized versions of the Angular documentation",
|
||||
"hidden": true
|
||||
}
|
||||
]
|
||||
"url": "guide/reviewing-content",
|
||||
"title": "Reviewing documentation",
|
||||
"tooltip": "Keep Angular content up-to-date by reviewing topics for accuracy."
|
||||
},
|
||||
{
|
||||
"url": "guide/updating-search-keywords",
|
||||
"title": "Updating search keywords",
|
||||
"tooltip": "Help users find the content they need by adding additional search keywords."
|
||||
},
|
||||
{
|
||||
"url": "guide/updating-content-github-ui",
|
||||
"title": "Updating content using GitHub UI",
|
||||
"tooltip": "Update topics in the browser through the GitHub user interface."
|
||||
},
|
||||
{
|
||||
"url": "guide/docs-style-guide",
|
||||
"title": "Documentation style guide",
|
||||
"tooltip": "Style guide for documentation authors."
|
||||
},
|
||||
{
|
||||
"url": "guide/docs-lint-errors",
|
||||
"title": "Resolving documentation linter messages",
|
||||
"tooltip": "More information about documentation lint errors."
|
||||
},
|
||||
{
|
||||
"url": "guide/localizing-angular",
|
||||
"title": "Angular doc localization guidelines",
|
||||
"tooltip": "Learn about the guidelines for localizing Angular documentation."
|
||||
},
|
||||
{
|
||||
"url": "guide/localized-documentation",
|
||||
"title": "Localized documentation",
|
||||
"tooltip": "A list of localized versions of the Angular documentation",
|
||||
"hidden": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1222,12 +1232,12 @@
|
||||
},
|
||||
{
|
||||
"url": "resources",
|
||||
"title": "Resource Listing",
|
||||
"title": "Resource listing",
|
||||
"tooltip": "Angular tools, training, and blogs from around the web."
|
||||
},
|
||||
{
|
||||
"url": "presskit",
|
||||
"title": "Press Kit",
|
||||
"title": "Press kit",
|
||||
"tooltip": "Press contacts, logos, and branding."
|
||||
},
|
||||
{
|
||||
@@ -1237,7 +1247,7 @@
|
||||
},
|
||||
{
|
||||
"url": "analytics",
|
||||
"title": "Usage Analytics",
|
||||
"title": "Usage analytics",
|
||||
"tooltip": "Angular Usage Analytics"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -74,7 +74,7 @@ After the browser refreshes, the list of heroes appears.
|
||||
|
||||
<header>Interactive elements</header>
|
||||
|
||||
Inside the `<li>` element, add a `<button>` element to wrap the hero's details, and then make the hero clickable. To improve accessibility, use HTML elements that are inherently interactive instead of adding an event listeners to a non-interactive elements. In this case, the interactive `<button>` element is instead of adding an event to the `<li>` element.
|
||||
Inside the `<li>` element, add a `<button>` element to wrap the hero's details, and then make the hero clickable. To improve accessibility, use HTML elements that are inherently interactive instead of adding an event listener to a non-interactive element. In this case, the interactive `<button>` element is used instead of adding an event to the `<li>` element.
|
||||
|
||||
For more details on accessibility, see [Accessibility in Angular](guide/accessibility).
|
||||
|
||||
|
||||
@@ -300,16 +300,6 @@ The component's `delete()` method immediately removes the *hero-to-delete* from
|
||||
|
||||
There's really nothing for the component to do with the `Observable` returned by `heroService.deleteHero()` **but it must subscribe anyway**.
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
If you neglect to `subscribe()`, the service can't send the delete request to the server.
|
||||
As a rule, an `Observable` *does nothing* until something subscribes.
|
||||
|
||||
Confirm this for yourself by temporarily removing the `subscribe()`, clicking **Dashboard**, then clicking **Heroes**.
|
||||
This shows the full list of heroes again.
|
||||
|
||||
</div>
|
||||
|
||||
Next, add a `deleteHero()` method to `HeroService` like this.
|
||||
|
||||
<code-example header="src/app/hero.service.ts (delete)" path="toh-pt6/src/app/hero.service.ts" region="deleteHero"></code-example>
|
||||
@@ -323,6 +313,16 @@ Notice the following key points:
|
||||
|
||||
Refresh the browser and try the new delete capability.
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
If you neglect to `subscribe()`, the service can't send the delete request to the server.
|
||||
As a rule, an `Observable` *does nothing* until something subscribes.
|
||||
|
||||
Confirm this for yourself by temporarily removing the `subscribe()`, clicking **Dashboard**, then clicking **Heroes**.
|
||||
This shows the full list of heroes again.
|
||||
|
||||
</div>
|
||||
|
||||
## Search by name
|
||||
|
||||
In this last exercise, you learn to chain `Observable` operators together so you can reduce the number of similar HTTP requests to consume network bandwidth economically.
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
{"type": 301, "source": "/getting-started", "destination": "/start"},
|
||||
{"type": 301, "source": "/getting-started/:rest*", "destination": "/start/:rest*"},
|
||||
{"type": 301, "source": "/guide/displaying-data", "destination": "/start#template-syntax"},
|
||||
{"type": 301, "source": "/guide/ivy", "destination": "https://v12.angular.io/guide/ivy"},
|
||||
{"type": 301, "source": "/guide/updating-to-version-10", "destination": "https://v10.angular.io/guide/updating-to-version-10"},
|
||||
{"type": 301, "source": "/guide/updating-to-version-11", "destination": "https://v11.angular.io/guide/updating-to-version-11"},
|
||||
{"type": 301, "source": "/guide/updating-to-version-12", "destination": "/guide/update-to-latest-version"},
|
||||
|
||||
@@ -118,6 +118,7 @@
|
||||
"!/guide/cli-quickstart/{0,1}",
|
||||
"!/guide/displaying-data/{0,1}",
|
||||
"!/guide/i18n/{0,1}",
|
||||
"!/guide/ivy/{0,1}",
|
||||
"!/guide/learning-angular*",
|
||||
"!/guide/metadata/{0,1}",
|
||||
"!/guide/ngmodule/{0,1}",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"build-local": "yarn ~~build",
|
||||
"prebuild-local-ci": "yarn setup-local-ci",
|
||||
"build-local-ci": "yarn ~~build --progress=false",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js a764b37fa",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 67ac90a75",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint && yarn security-lint",
|
||||
"test": "yarn check-env && ng test",
|
||||
"test:bazel": "bazelisk test //aio:test",
|
||||
@@ -100,12 +100,12 @@
|
||||
"@angular/router": "^14.1.0-next",
|
||||
"@angular/service-worker": "^14.1.0-next",
|
||||
"rxjs": "~7.5.0",
|
||||
"safevalues": "^0.1.8",
|
||||
"safevalues": "^0.3.1",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/architect-cli": "0.1401.0-next.4",
|
||||
"@angular-devkit/architect-cli": "0.1401.0-rc.3",
|
||||
"@angular-devkit/build-angular": "^14.1.0-next",
|
||||
"@angular-eslint/builder": "^14.0.0",
|
||||
"@angular-eslint/eslint-plugin": "^14.0.0",
|
||||
@@ -119,8 +119,8 @@
|
||||
"@types/node": "^12.7.9",
|
||||
"@types/trusted-types": "^2.0.2",
|
||||
"@types/xregexp": "^4.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.30.6",
|
||||
"@typescript-eslint/parser": "5.30.6",
|
||||
"@typescript-eslint/eslint-plugin": "5.33.0",
|
||||
"@typescript-eslint/parser": "5.33.0",
|
||||
"@yarnpkg/lockfile": "^1.1.0",
|
||||
"archiver": "^5.3.0",
|
||||
"assert": "^2.0.0",
|
||||
@@ -147,16 +147,16 @@
|
||||
"html": "^1.0.0",
|
||||
"ignore": "^5.1.8",
|
||||
"image-size": "^1.0.0",
|
||||
"jasmine": "~4.2.0",
|
||||
"jasmine-core": "~4.2.0",
|
||||
"jasmine": "~4.3.0",
|
||||
"jasmine-core": "~4.3.0",
|
||||
"jasmine-spec-reporter": "~7.0.0",
|
||||
"jsdom": "^20.0.0",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"json5": "^2.2.0",
|
||||
"karma": "~6.3.2",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.0.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.0.0",
|
||||
"light-server": "^2.9.1",
|
||||
"lighthouse": "^9.0.0",
|
||||
|
||||
@@ -13,7 +13,7 @@ import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
|
||||
import { svg } from 'app/shared/security';
|
||||
import { scriptUrl, unwrapScriptUrlForSink } from 'safevalues';
|
||||
import { trustedResourceUrl, unwrapResourceUrl } from 'safevalues';
|
||||
|
||||
import { AppComponent } from 'app/app.component';
|
||||
import { CustomIconRegistry, SVG_ICONS } from 'app/shared/custom-icon-registry';
|
||||
@@ -156,7 +156,7 @@ export const svgIconProviders = [
|
||||
SharedModule,
|
||||
ServiceWorkerModule.register(
|
||||
// Make sure service worker is loaded with a TrustedScriptURL
|
||||
unwrapScriptUrlForSink(scriptUrl`/ngsw-worker.js`),
|
||||
unwrapResourceUrl(trustedResourceUrl`/ngsw-worker.js`) as string,
|
||||
{enabled: environment.production}),
|
||||
],
|
||||
declarations: [
|
||||
|
||||
@@ -5,8 +5,8 @@ import { PrettyPrinter } from './pretty-printer.service';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { unwrapHtmlForSink } from 'safevalues';
|
||||
import { htmlFromStringKnownToSatisfyTypeContract } from 'safevalues/unsafe/reviewed';
|
||||
import { unwrapHtml } from 'safevalues';
|
||||
import { htmlSafeByReview } from 'safevalues/restricted/reviewed';
|
||||
import { fromOuterHTML } from 'app/shared/security';
|
||||
|
||||
/**
|
||||
@@ -149,7 +149,7 @@ export class CodeComponent implements OnChanges {
|
||||
private setCodeHtml(formattedCode: TrustedHTML) {
|
||||
// **Security:** Code example content is provided by docs authors and as such its considered to
|
||||
// be safe for innerHTML purposes.
|
||||
this.codeContainer.nativeElement.innerHTML = unwrapHtmlForSink(formattedCode);
|
||||
this.codeContainer.nativeElement.innerHTML = unwrapHtml(formattedCode);
|
||||
}
|
||||
|
||||
/** Gets the textContent of the displayed code element. */
|
||||
@@ -198,7 +198,7 @@ function leftAlign(text: TrustedHTML): TrustedHTML {
|
||||
}
|
||||
});
|
||||
|
||||
return htmlFromStringKnownToSatisfyTypeContract(
|
||||
return htmlSafeByReview(
|
||||
lines.map(line => line.slice(indent)).join('\n').trim(),
|
||||
'safe manipulation of existing trusted HTML');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { htmlFromStringKnownToSatisfyTypeContract } from 'safevalues/unsafe/reviewed';
|
||||
import { htmlSafeByReview } from 'safevalues/restricted/reviewed';
|
||||
|
||||
import { from, Observable } from 'rxjs';
|
||||
import { first, map, share } from 'rxjs/operators';
|
||||
@@ -51,7 +51,7 @@ export class PrettyPrinter {
|
||||
return this.prettyPrintOne.pipe(
|
||||
map(ppo => {
|
||||
try {
|
||||
return htmlFromStringKnownToSatisfyTypeContract(
|
||||
return htmlSafeByReview(
|
||||
ppo(code, language, linenums), 'prettify.js modifies already trusted HTML inline');
|
||||
} catch (err) {
|
||||
const msg = `Could not format code that begins '${code.toString().slice(0, 50)}...'.`;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
||||
import { AsyncSubject, Observable, of } from 'rxjs';
|
||||
import { catchError, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { htmlEscape } from 'safevalues';
|
||||
import { htmlFromStringKnownToSatisfyTypeContract } from 'safevalues/unsafe/reviewed';
|
||||
import { htmlSafeByReview } from 'safevalues/restricted/reviewed';
|
||||
|
||||
import { DocumentContents, UnsafeDocumentContents } from './document-contents';
|
||||
export { DocumentContents } from './document-contents';
|
||||
@@ -17,7 +17,7 @@ export const FETCHING_ERROR_ID = 'fetching-error';
|
||||
|
||||
export const CONTENT_URL_PREFIX = 'generated/';
|
||||
export const DOC_CONTENT_URL_PREFIX = CONTENT_URL_PREFIX + 'docs/';
|
||||
const FETCHING_ERROR_CONTENTS = (path: string) => htmlFromStringKnownToSatisfyTypeContract(`
|
||||
const FETCHING_ERROR_CONTENTS = (path: string) => htmlSafeByReview(`
|
||||
<div class="nf-container l-flex-wrap flex-center">
|
||||
<div class="nf-icon material-icons">error_outline</div>
|
||||
<div class="nf-response l-flex-wrap center">
|
||||
@@ -74,7 +74,7 @@ export class DocumentService {
|
||||
null :
|
||||
// SECURITY: HTML is authored by the documentation team and is fetched directly
|
||||
// from the server
|
||||
htmlFromStringKnownToSatisfyTypeContract(data.contents, '^')
|
||||
htmlSafeByReview(data.contents, '^')
|
||||
})),
|
||||
catchError((error: HttpErrorResponse) =>
|
||||
error.status === 404 ? this.getFileNotFoundDoc(id) : this.getErrorDoc(id, error)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Meta, Title } from '@angular/platform-browser';
|
||||
|
||||
import { Observable, asapScheduler, of, lastValueFrom } from 'rxjs';
|
||||
import { EMPTY_HTML, htmlEscape } from 'safevalues';
|
||||
import { htmlFromStringKnownToSatisfyTypeContract } from 'safevalues/unsafe/reviewed';
|
||||
import { htmlSafeByReview } from 'safevalues/restricted/reviewed';
|
||||
|
||||
import { FILE_NOT_FOUND_ID, FETCHING_ERROR_ID } from 'app/documents/document.service';
|
||||
import { CustomElementsModule } from 'app/custom-elements/custom-elements.module';
|
||||
@@ -319,7 +319,7 @@ describe('DocViewerComponent', () => {
|
||||
beforeEach(() => swapViewsSpy.and.callThrough());
|
||||
|
||||
it('should display the document contents', async () => {
|
||||
const contents = htmlFromStringKnownToSatisfyTypeContract('<h1>Hello,</h1> <div>world!</div>', 'constant HTML');
|
||||
const contents = htmlSafeByReview('<h1>Hello,</h1> <div>world!</div>', 'constant HTML');
|
||||
await doRender(contents);
|
||||
|
||||
expect(docViewerEl.innerHTML).toContain(contents.toString());
|
||||
|
||||
@@ -7,7 +7,7 @@ import {fromInnerHTML} from 'app/shared/security';
|
||||
import {TocService} from 'app/shared/toc.service';
|
||||
import {asapScheduler, Observable, of, timer} from 'rxjs';
|
||||
import {catchError, observeOn, switchMap, takeUntil, tap} from 'rxjs/operators';
|
||||
import {EMPTY_HTML, unwrapHtmlForSink} from 'safevalues';
|
||||
import {EMPTY_HTML, unwrapHtml} from 'safevalues';
|
||||
|
||||
|
||||
// Constants
|
||||
@@ -65,9 +65,9 @@ export class DocViewerComponent implements OnDestroy {
|
||||
private elementsLoader: ElementsLoader) {
|
||||
this.hostElement = elementRef.nativeElement;
|
||||
|
||||
// Security: the initialDocViewerContent comes from the prerendered DOM and is considered to be
|
||||
// secure
|
||||
this.hostElement.innerHTML = unwrapHtmlForSink(initialDocViewerContent);
|
||||
// Security: the initialDocViewerContent comes from the prerendered DOM and is
|
||||
// considered to be secure
|
||||
this.hostElement.innerHTML = unwrapHtml(initialDocViewerContent) as string;
|
||||
|
||||
if (this.hostElement.firstElementChild) {
|
||||
this.currViewContainer = this.hostElement.firstElementChild as HTMLElement;
|
||||
@@ -141,7 +141,7 @@ export class DocViewerComponent implements OnDestroy {
|
||||
} else {
|
||||
// Security: `doc.contents` is always authored by the documentation team
|
||||
// and is considered to be safe.
|
||||
this.nextViewContainer.innerHTML = unwrapHtmlForSink(doc.contents);
|
||||
this.nextViewContainer.innerHTML = unwrapHtml(doc.contents) as string;
|
||||
}
|
||||
}),
|
||||
tap(() => addTitleAndToc = this.prepareTitleAndToc(this.nextViewContainer, doc.id)),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
|
||||
import { scriptUrl, unwrapScriptUrlForSink } from 'safevalues';
|
||||
import { trustedResourceUrl, unwrapResourceUrl } from 'safevalues';
|
||||
|
||||
import { formatErrorEventForAnalytics } from './analytics-format-error';
|
||||
import { WindowToken } from '../shared/window';
|
||||
@@ -82,7 +82,7 @@ export class AnalyticsService {
|
||||
private _installGlobalSiteTag() {
|
||||
const window = this.window;
|
||||
const url: TrustedScriptURL =
|
||||
scriptUrl`https://www.googletagmanager.com/gtag/js?id=${environment.googleAnalyticsId}`;
|
||||
trustedResourceUrl`https://www.googletagmanager.com/gtag/js?id=${environment.googleAnalyticsId}`;
|
||||
|
||||
// Note: This cannot be an arrow function as `gtag.js` expects an actual `Arguments`
|
||||
// instance with e.g. `callee` to be set. Do not attempt to change this and keep this
|
||||
@@ -103,7 +103,7 @@ export class AnalyticsService {
|
||||
|
||||
const el = window.document.createElement('script');
|
||||
el.async = true;
|
||||
el.src = unwrapScriptUrlForSink(url);
|
||||
el.src = unwrapResourceUrl(url) as string;
|
||||
window.document.head.appendChild(el);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { HttpClient } from '@angular/common/http';
|
||||
import { ErrorHandler } from '@angular/core';
|
||||
import { MatIconRegistry } from '@angular/material/icon';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { unwrapHtmlForSink } from 'safevalues';
|
||||
import { unwrapHtml } from 'safevalues';
|
||||
import { concatHtmls } from 'safevalues/builders/html_builders';
|
||||
import { CustomIconRegistry, SvgIconInfo } from './custom-icon-registry';
|
||||
import { svg } from './security';
|
||||
@@ -69,6 +69,6 @@ describe('CustomIconRegistry', () => {
|
||||
|
||||
function createSvg(svgSrc: TrustedHTML): SVGElement {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = unwrapHtmlForSink(svgSrc);
|
||||
div.innerHTML = unwrapHtml(svgSrc) as string;
|
||||
return div.querySelector('svg') as SVGElement;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { of } from 'rxjs';
|
||||
import { MatIconRegistry } from '@angular/material/icon';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { unwrapHtmlForSink } from 'safevalues';
|
||||
import { unwrapHtml } from 'safevalues';
|
||||
|
||||
/**
|
||||
* Use SVG_ICONS (and SvgIconInfo) as "multi" providers to provide the SVG source
|
||||
@@ -76,7 +76,7 @@ export class CustomIconRegistry extends MatIconRegistry {
|
||||
const div = document.createElement('DIV');
|
||||
|
||||
// SECURITY: the source for the SVG icons is provided in code by trusted developers
|
||||
div.innerHTML = unwrapHtmlForSink(svgIcon.svgSource);
|
||||
div.innerHTML = unwrapHtml(svgIcon.svgSource) as string;
|
||||
|
||||
const svgElement = div.querySelector('svg') as SVGElement;
|
||||
nsIconMap[svgIcon.name] = svgElement;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { htmlFromStringKnownToSatisfyTypeContract } from 'safevalues/unsafe/reviewed';
|
||||
import { htmlSafeByReview } from 'safevalues/restricted/reviewed';
|
||||
|
||||
export function fromInnerHTML(el: Element): TrustedHTML {
|
||||
// SECURITY: Existing innerHTML content is already trusted.
|
||||
return htmlFromStringKnownToSatisfyTypeContract(el.innerHTML, '^');
|
||||
return htmlSafeByReview(el.innerHTML, '^');
|
||||
}
|
||||
|
||||
export function fromOuterHTML(el: Element): TrustedHTML {
|
||||
// SECURITY: Existing outerHTML content is already trusted.
|
||||
return htmlFromStringKnownToSatisfyTypeContract(el.outerHTML, '^');
|
||||
return htmlSafeByReview(el.outerHTML, '^');
|
||||
}
|
||||
|
||||
export function svg(constantSvg: TemplateStringsArray): TrustedHTML {
|
||||
// SECURITY: Template literal argument with no interpolation is constant, and
|
||||
// hence trusted.
|
||||
return htmlFromStringKnownToSatisfyTypeContract(constantSvg[0], '^');
|
||||
return htmlSafeByReview(constantSvg[0], '^');
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Inject, Injectable } from '@angular/core';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
import { ScrollSpyInfo, ScrollSpyService } from 'app/shared/scroll-spy.service';
|
||||
import { unwrapHtmlForSink } from 'safevalues';
|
||||
import { unwrapHtml } from 'safevalues';
|
||||
import { fromInnerHTML } from './security';
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ export class TocService {
|
||||
// - Mark the HTML as trusted to be used with `[innerHTML]`.
|
||||
private extractHeadingSafeHtml(heading: HTMLHeadingElement) {
|
||||
const div: HTMLDivElement = this.document.createElement('div');
|
||||
div.innerHTML = unwrapHtmlForSink(fromInnerHTML(heading));
|
||||
div.innerHTML = unwrapHtml(fromInnerHTML(heading)) as string;
|
||||
|
||||
// Remove any `.github-links` or `.header-link` elements (along with their content).
|
||||
div.querySelectorAll('.github-links, .header-link').forEach(link => link.remove());
|
||||
|
||||
@@ -48,6 +48,7 @@ aio-contributor {
|
||||
width: calc(100% - 4px);
|
||||
height: calc(100% - 4px);
|
||||
clip-path: $angular-shape-polygon;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.contributor-social-links {
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
// scope.
|
||||
|
||||
import { of } from 'rxjs';
|
||||
import { unwrapHtmlForSink } from 'safevalues';
|
||||
import { htmlFromStringKnownToSatisfyTypeContract } from 'safevalues/unsafe/reviewed';
|
||||
import { unwrapHtml } from 'safevalues';
|
||||
import { htmlSafeByReview } from 'safevalues/restricted/reviewed';
|
||||
|
||||
export class MockPrettyPrinter {
|
||||
formatCode(code: TrustedHTML, language?: string, linenums?: number | boolean) {
|
||||
const linenumsStr = (linenums === undefined) ? '' : `, linenums: ${linenums}`;
|
||||
return of(htmlFromStringKnownToSatisfyTypeContract(
|
||||
return of(htmlSafeByReview(
|
||||
`Formatted code (language: ${language || 'auto'}${linenumsStr}): ${
|
||||
unwrapHtmlForSink(code)}`,
|
||||
unwrapHtml(code)}`,
|
||||
'safe transformation of existing TrustedHTML'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +205,7 @@
|
||||
/guide/displaying-data --> /start#template-syntax
|
||||
/guide/i18n --> /guide/i18n-overview
|
||||
/guide --> /docs
|
||||
/guide/ivy --> https://v12.angular.io/guide/ivy
|
||||
/guide/learning-angular --> /start
|
||||
/guide/learning-angular.html --> /start
|
||||
/guide/metadata --> /guide/aot-compiler
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
# Angular documentation lint tool
|
||||
|
||||
The files in this directory support the [Vale][ValeMain] documentation lint tool for use while you edit [angular.io][AioMain] documentation files.
|
||||
|
||||
*Linting* is an automated process that lint tools, or *linters*, perform to identify stylistic errors in program source code.
|
||||
Vale is a lint tool that performs a similar analysis of documentation source code.
|
||||
For more information about lint tools, linting, and their history, see [*Lint* in Wikipedia][WikipediaWikiLintSoftware].
|
||||
|
||||
## Use Vale to lint your documentation
|
||||
|
||||
[Vale][ValeMain] is an open source, command-line tool that reviews writing style and phrases in your documentation.
|
||||
You can add it to your integrated development environment \(IDE\) or code editor to test your documentation edits before you submit your pull request.
|
||||
Using it can help improve the documentation's readability and the consistency of terminology and voice across a range of topics and contributors.
|
||||
|
||||
The lint tool is helpful and has been tailored to the Angular documentation style guide.
|
||||
Unfortunately, it is not as powerful as the grammar and spelling features you find in contemporary document editors.
|
||||
While Vale helps, it doesn't replace the reviewers that you're currently using to help you write great documentation.
|
||||
|
||||
**To use Vale in your documentation:**
|
||||
|
||||
1. [Install Vale][AioToolsDocLinterReadmeInstallValeInYourVsCodeIde] in your Visual Studio Code IDE.
|
||||
To install and use Vale in a different IDE, see [Installation][ValeDocsValeCliInstallation].
|
||||
2. Fix the problems that Vale identifies in your IDE.
|
||||
|
||||
Log any problem you encounter with the documentation linter as a [new docs issue][GithubAngularAngularIssuesNewAssigneesLabelsTemplate3DocsBugYaml] in the repo.
|
||||
Be sure to specify that this is a linter issue in your description.
|
||||
|
||||
## Fix documentation problems identified by Vale
|
||||
|
||||
Vale evaluates a markdown \(`.md`\) file when you open it in the IDE and each time you save it. Vale does not review your document as you make changes in it.
|
||||
|
||||
When Vale identifies a problem, it can take a simple word substitution or rewriting several sentences or paragraphs to fix it.
|
||||
Sometimes, you might need to take a step back from the text and consider other alternatives.
|
||||
|
||||
To find the best way to fix a problem that Vale identified, think of your reader, be flexible, and keep an open mind.
|
||||
The documentation should be understood by a range of readers, some of whom might not have much experience with Angular or even web development.
|
||||
|
||||
> **Note**
|
||||
> Remember that when a developer is reading documentation, they are often doing so while thinking about other things, such as coding their Angular application.
|
||||
> Keeping the docs easy to read, helps developers concentrate on their coding.
|
||||
|
||||
These are some tips to help you resolve the issues that the lint tool identifies and to help you make your docs easier to read.
|
||||
|
||||
### Refer to the style guides
|
||||
|
||||
The lint tool tests against the styles found in these style guides.
|
||||
Most style tests include links to relevant sections in these documents for more information.
|
||||
|
||||
* [Angular documentation style guide][AioGuideDocsStyleGuide]
|
||||
* [Google Developer Documentation Style Guide][GoogleDevelopersStyle]
|
||||
|
||||
> **Note**
|
||||
> Not every style mentioned in the style guides has a test.
|
||||
> Style guides and the style tests can change.
|
||||
|
||||
### Split up long sentences
|
||||
|
||||
Generally, shorter sentences are easier to read than longer ones.
|
||||
Long sentences can occur when you try to say too much at once.
|
||||
Long sentences, as well as the use of parentheses, semi-colons, or words identified as *too-wordy*, generally require rethinking and rewriting.
|
||||
Consider restructuring a long sentence to break its individual ideas into distinct sentences or bullet points.
|
||||
|
||||
### Use lists and tables
|
||||
|
||||
Sentences that contain comma-separated lists might be clearer if presented as a bulleted-list or table.
|
||||
Consider changing a comma-separated list of items in a sentence to a list of bullets to make those list items easier to read.
|
||||
|
||||
### Use more common words
|
||||
|
||||
Shorter, more common words are generally easier to read than longer ones.
|
||||
This does not mean you need to write down to the audience. Technical docs should still be precise.
|
||||
Angular docs are read by many people around the world and should use language that the most people can understand.
|
||||
|
||||
If you think a specific term is required even though it is been flagged as uncommon, try to include a short explanation of the term.
|
||||
Also, try adding some context around its first mention.
|
||||
Linking a term to another section or definition is also an option, but consider the disruption that causes to the reader before you use it.
|
||||
If you force a reader to go to another page for a definition, they might lose their concentration on the current topic and their primary goal.
|
||||
|
||||
### Use fewer words
|
||||
|
||||
If you can remove a word and not lose the meaning of the sentence, leave it out.
|
||||
|
||||
One common place where removing words can help is in a list of examples with more than two or three items.
|
||||
Before you place the items in a bullet list, consider if only one of the items can convey the desired meaning.
|
||||
Another option might be to replace a list of items with a single term that describes all the elements in your list.
|
||||
|
||||
### If all else fails
|
||||
|
||||
The style rules generally guide you in the direction of clearer content, but sometimes you might need to break the rules.
|
||||
If you decide that the best choice for the text conflicts with the linter, mark the text as an exception to linting.
|
||||
|
||||
If you use these exceptions, please limit the amount of text that you exclude from analysis to the fewest lines possible.
|
||||
|
||||
When necessary, you can apply these exceptions to your content.
|
||||
|
||||
1. **General exception**
|
||||
|
||||
A *general exception* allows you to exclude the specified text from all lint testing.
|
||||
|
||||
To apply a general exception, surround the text that you do not want the linter to test with the HTML
|
||||
`comment` elements shown in this example.
|
||||
|
||||
```markdown
|
||||
|
||||
<!-- vale off -->
|
||||
|
||||
Text the linter does not check for any style problem.
|
||||
|
||||
<!-- vale on -->
|
||||
|
||||
```
|
||||
|
||||
Be sure to leave a blank line before and after each comment.
|
||||
|
||||
2. **Style exception**
|
||||
|
||||
A *style exception* allows you to exclude text from an individual style test.
|
||||
|
||||
To apply a style exception, surround the text that you do not want the linter to test with these HTML
|
||||
`comment` elements. Between these comments, the linter ignores the style test in the comment, but
|
||||
still tests for all other styles that are in use.
|
||||
|
||||
```markdown
|
||||
<!-- vale Style.Rule = NO -->
|
||||
<!-- vale Style.Rule = YES -->
|
||||
```
|
||||
|
||||
Replace `Style.Rule` in the comments with the style and rule from the problem message displayed in the IDE.
|
||||
For example, imagine that you got this problem message and you want to use the word it identified as a problem.
|
||||
|
||||
```markdown
|
||||
Did you really mean 'inlines'? It was not found in our dictionary. Vale(Angular.Angular_Spelling) [Ln 24, Col 59]
|
||||
```
|
||||
|
||||
The `Style.Rule` for this message is the text inside the parentheses: `Angular.Angular_Spelling` in this case.
|
||||
To turn off that style test, use the comments shown in this example.
|
||||
|
||||
```markdown
|
||||
|
||||
<!-- vale Angular.Angular_Spelling = NO -->
|
||||
|
||||
'inlines' does not display a problem because the linter does not spell check this text.
|
||||
|
||||
Remember that the linter does not catch any other spelling errors in this block of text.
|
||||
|
||||
The linter continues to test all other style rules.
|
||||
|
||||
<!-- vale Angular.Angular_Spelling = YES -->
|
||||
|
||||
```
|
||||
|
||||
## Install Vale on your development system
|
||||
|
||||
Complete these procedures to install the Vale documentation linter on your development system.
|
||||
|
||||
Vale can be installed on Windows, macOS, and Linux, and it can be integrated into many IDEs.
|
||||
The instructions for installing it into Visual Studio Code on Windows and macOS are summarized here.
|
||||
For more information, or to install Vale in another IDE or on another operating system, see [Vale installation documentation][ValeDocsValeCliInstallation].
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you proceed, make sure that:
|
||||
|
||||
1. You have cloned the [`angular/angular` repo][GithubAngularAngular] on your system. You can work in a fork of the `angular/angular` repo,
|
||||
but it's easier if your Vale installation uses an up-to-date clone of the `angular/angular` repo.
|
||||
2. You have a development or writing environment for `angular.io` in the `angular/angular` repo and you can build the `angular.io` docs.
|
||||
For information about how to create the authoring environment for `angular.io`, see [Angular documentation project][GithubAngularAngularTreeMainAioAngularDocumentationProjectHttpsangulario].
|
||||
3. You have the required package manager installed on your system. If not, install it before continuing.
|
||||
* [homebrew][BrewMain] on your macOS system.
|
||||
* [chocolatey][Chocolatey] on your Windows system.
|
||||
|
||||
### Install the Vale command-line tool
|
||||
|
||||
Open the following tools on your development system.
|
||||
|
||||
1. Open a command-line tool:
|
||||
* `terminal` on macOS, for example
|
||||
* `Windows PowerShell` on Windows
|
||||
2. Open Visual Studio Code.
|
||||
|
||||
Follow the instructions for the OS on your development system.
|
||||
To install Vale on a different OS, see [Installation][ValeDocsValeCliInstallation].
|
||||
|
||||
#### Install Vale on macOS
|
||||
|
||||
In your command-line tool:
|
||||
|
||||
<!-- vale Angular.WriteGood_Illusions = NO -->
|
||||
<!-- vale Angular.Angular_CliReference = NO -->
|
||||
|
||||
1. Run `brew install vale` to install Vale.
|
||||
2. Get the path to the `vale` application to use as the value of the **Vale > Vale CLI : Path** setting.
|
||||
1. In the command-line tool, run `which vale`.
|
||||
Save the returned path for use in the next procedure.
|
||||
|
||||
2. If this command does not return a path, restart macOS and repeat this step.
|
||||
3. Get the path to the configuration file, `vale.ini`, to use in the **Vale > Vale CLI : Config** setting.
|
||||
1. In the command-line tool, navigate to your `git` working directory with the `angular/angular` repo.
|
||||
2. Run `git pull` to update the repo.
|
||||
3. In your clone of the `angular/angular` repo, navigate to the `aio/tools/doc-linter` directory.
|
||||
4. In the `aio/tools/doc-linter` directory, confirm that you have the `vale.ini` file.
|
||||
5. Run `pwd` to get the full path to `vale.ini` to use in the next procedure.
|
||||
|
||||
<!-- vale Angular.Angular_CliReference = YES -->
|
||||
<!-- vale Angular.WriteGood_Illusions = YES -->
|
||||
|
||||
#### Install Vale on Windows
|
||||
|
||||
In your command-line tool:
|
||||
|
||||
<!-- vale Angular.WriteGood_Illusions = NO -->
|
||||
<!-- vale Angular.Angular_CliReference = NO -->
|
||||
|
||||
1. Run `choco install vale` to install Vale.
|
||||
2. Get the path to the `vale` application to use as the value of the **Vale > Vale CLI : Path** setting.
|
||||
1. In the command-line tool, run `where vale.exe`.
|
||||
Save the returned path for use in the next procedure.
|
||||
|
||||
2. If this command does not return a path, restart Windows and repeat this step.
|
||||
3. Get the path to the configuration file, `vale.ini`, to use in the **Vale > Vale CLI : Config** setting.
|
||||
1. In the command-line tool, navigate to your `git` working directory with the `angular/angular` repo.
|
||||
2. Run `git pull` to update the repo.
|
||||
3. In your clone of the `angular/angular` repo, navigate to the `aio\tools\doc-linter` directory.
|
||||
4. In the `aio\tools\doc-linter` directory, confirm that you have the `vale.ini` file.
|
||||
5. Run `cd` to get the full path to `vale.ini` to use in the next procedure.
|
||||
|
||||
<!-- vale Angular.Angular_CliReference = YES -->
|
||||
<!-- vale Angular.WriteGood_Illusions = YES -->
|
||||
|
||||
### Install the Vale extension for Visual Studio Code
|
||||
|
||||
This procedure is the same for macOS and Windows.
|
||||
To install Vale in a different IDE, see [Installation][ValeDocsValeCliInstallation].
|
||||
|
||||
<!-- vale Angular.WriteGood_Illusions = NO -->
|
||||
<!-- vale Angular.Angular_CliReference = NO -->
|
||||
|
||||
**To install and configure the Visual Studio Code Vale extension:**
|
||||
|
||||
1. In Visual Studio Code, go to the **Extensions** pane.
|
||||
2. In the Visual Studio Code **Extensions** pane:
|
||||
1. In the search box, enter `Vale`.
|
||||
2. In the Vale extension entry, choose, **Install**.
|
||||
3. Access the settings in Visual Studio Code for the Vale extension.
|
||||
* On macOS, in the **Code** menu, choose **Preferences**, and then choose **Settings**.
|
||||
* On Windows, in the **File** menu, choose **Preferences**, and then choose **Settings**.
|
||||
4. In the **User** settings, navigate to **Extensions > Vale**.
|
||||
5. Update the Vale extension settings as shown.
|
||||
* **Vale : Config**, enter the full path to `vale.ini` from the preceding procedure and append the `vale.ini` filename.
|
||||
* **Vale : Min Alert Level**, choose `inherited` or `suggestion`.
|
||||
* **Vale : Path**, enter the full path of the `vale` application that `which vale` or `where vale.exe` returned in a preceding step.
|
||||
6. Restart Visual Studio Code to apply the new settings.
|
||||
|
||||
<!-- vale Angular.Angular_CliReference = YES -->
|
||||
<!-- vale Angular.WriteGood_Illusions = YES -->
|
||||
|
||||
### Known issues with the Visual Studio Code extension
|
||||
|
||||
If you're having problems with the `0.15.0` version of the Vale extension to Visual Studio Code, try:
|
||||
|
||||
1. Uninstalling version `0.15.0` of the Vale extension.
|
||||
2. Installing version `0.14.2` of the Vale extension.
|
||||
|
||||
If you're using version `0.14.2`, the Vale extension settings to use are:
|
||||
|
||||
<!-- vale Angular.WriteGood_Illusions = NO -->
|
||||
<!-- vale Angular.Angular_CliReference = NO -->
|
||||
|
||||
* **Vale > Core : Use CLI**, checked.
|
||||
* **Vale > Server : Lint Context**, `0`.
|
||||
* **Vale > Server : Provide Fixes**, unchecked.
|
||||
* **Vale > Server : Server URL**, leave empty.
|
||||
* **Vale > Vale CLI : Config**, enter the full path to `vale.ini` from the preceding procedure and append the `vale.ini` filename.
|
||||
* **Vale > Vale CLI : Min Alert Level**, choose `inherited` or `suggestion`.
|
||||
* **Vale > Vale CLI : Path**, enter the full path of the `vale` application that `which vale` or `where vale.exe` returned in a preceding step.
|
||||
|
||||
<!-- vale Angular.Angular_CliReference = YES -->
|
||||
<!-- vale Angular.WriteGood_Illusions = YES -->
|
||||
|
||||
## Update the Vale command-line tool
|
||||
|
||||
* To update Vale on macOS to the latest version, in your command-line tool, run `brew upgrade vale`.
|
||||
|
||||
* To update Vale on Windows to the latest version, in a PowerShell window, run `choco update vale`.
|
||||
|
||||
## Update the Vale style tests
|
||||
|
||||
The style tests are stored in your local clone of the [`angular/angular` repo][GithubAngularAngular] and
|
||||
are updated automatically when you pull the latest code to your system.
|
||||
If you're using another branch or fork of the `angular/angular` repo, be sure to also update that branch or fork.
|
||||
|
||||
<!-- links -->
|
||||
|
||||
[AioGuideDocsStyleGuide]: https://angular.io/guide/docs-style-guide "Angular documentation style guide | Angular"
|
||||
|
||||
[AioMain]: https://angular.io "Angular"
|
||||
|
||||
[AioToolsDocLinterReadmeInstallValeInYourVsCodeIde]: #install-vale-on-your-development-system "Install Vale | "
|
||||
|
||||
<!-- external links -->
|
||||
|
||||
[BrewMain]: https://brew.sh "homebrew"
|
||||
|
||||
[Chocolatey]: https://chocolatey.org/ "Chocolatey"
|
||||
|
||||
[GithubAngularAngular]: https://github.com/angular/angular "angular/angular | GitHub"
|
||||
|
||||
[GithubAngularAngularIssuesNewAssigneesLabelsTemplate3DocsBugYaml]: https://github.com/angular/angular/issues/new?assignees=&labels=&template=3-docs-bug.yaml "Issue: Docs or angular.io Bug Report | angular/angular | GitHub"
|
||||
|
||||
[GithubAngularAngularTreeMainAioAngularDocumentationProjectHttpsangulario]: https://github.com/angular/angular/tree/main/aio#angular-documentation-project-httpsangulario "Angular documentation project [AioMain] | angular/angular/aio | GitHub"
|
||||
|
||||
[GoogleDevelopersStyle]: https://developers.google.com/style "About this guide | Google developer documentation style guide | Google Developers"
|
||||
|
||||
[ValeDocsValeCliInstallation]: https://vale.sh/docs/vale-cli/installation "Installation | Vale.sh"
|
||||
|
||||
[ValeMain]: https://vale.sh "Vale.sh"
|
||||
|
||||
[WikipediaWikiLintSoftware]: https://en.wikipedia.org/wiki/Lint_%28software%29 "Lint (software) | Wikipedia"
|
||||
|
||||
<!-- end links -->
|
||||
|
||||
<!-- @reviewed 2022-07-15 -->
|
||||
@@ -0,0 +1,205 @@
|
||||
SET UTF-8
|
||||
TRY esianrtolcdugmphbyfvkwzESIANRTOLCDUGMPHBYFVKWZ'
|
||||
ICONV 1
|
||||
ICONV ’ '
|
||||
NOSUGGEST !
|
||||
|
||||
# ordinal numbers
|
||||
COMPOUNDMIN 1
|
||||
# only in compounds: 1th, 2th, 3th
|
||||
ONLYINCOMPOUND c
|
||||
# compound rules:
|
||||
# 1. [0-9]*1[0-9]th (10th, 11th, 12th, 56714th, etc.)
|
||||
# 2. [0-9]*[02-9](1st|2nd|3rd|[4-9]th) (21st, 22nd, 123rd, 1234th, etc.)
|
||||
COMPOUNDRULE 2
|
||||
COMPOUNDRULE n*1t
|
||||
COMPOUNDRULE n*mp
|
||||
WORDCHARS 0123456789
|
||||
|
||||
PFX A Y 1
|
||||
PFX A 0 re .
|
||||
|
||||
PFX I Y 1
|
||||
PFX I 0 in .
|
||||
|
||||
PFX U Y 1
|
||||
PFX U 0 un .
|
||||
|
||||
PFX C Y 1
|
||||
PFX C 0 de .
|
||||
|
||||
PFX E Y 1
|
||||
PFX E 0 dis .
|
||||
|
||||
PFX F Y 1
|
||||
PFX F 0 con .
|
||||
|
||||
PFX K Y 1
|
||||
PFX K 0 pro .
|
||||
|
||||
SFX V N 2
|
||||
SFX V e ive e
|
||||
SFX V 0 ive [^e]
|
||||
|
||||
SFX N Y 3
|
||||
SFX N e ion e
|
||||
SFX N y ication y
|
||||
SFX N 0 en [^ey]
|
||||
|
||||
SFX X Y 3
|
||||
SFX X e ions e
|
||||
SFX X y ications y
|
||||
SFX X 0 ens [^ey]
|
||||
|
||||
SFX H N 2
|
||||
SFX H y ieth y
|
||||
SFX H 0 th [^y]
|
||||
|
||||
SFX Y Y 1
|
||||
SFX Y 0 ly .
|
||||
|
||||
SFX G Y 2
|
||||
SFX G e ing e
|
||||
SFX G 0 ing [^e]
|
||||
|
||||
SFX J Y 2
|
||||
SFX J e ings e
|
||||
SFX J 0 ings [^e]
|
||||
|
||||
SFX D Y 4
|
||||
SFX D 0 d e
|
||||
SFX D y ied [^aeiou]y
|
||||
SFX D 0 ed [^ey]
|
||||
SFX D 0 ed [aeiou]y
|
||||
|
||||
SFX T N 4
|
||||
SFX T 0 st [eg]
|
||||
SFX T y iest [^aeiou]y
|
||||
SFX T 0 est [aeiou]y
|
||||
SFX T 0 est [^ey]
|
||||
|
||||
SFX R Y 4
|
||||
SFX R 0 r e
|
||||
SFX R y ier [^aeiou]y
|
||||
SFX R 0 er [aeiou]y
|
||||
SFX R 0 er [^ey]
|
||||
|
||||
SFX Z Y 4
|
||||
SFX Z 0 rs e
|
||||
SFX Z y iers [^aeiou]y
|
||||
SFX Z 0 ers [aeiou]y
|
||||
SFX Z 0 ers [^ey]
|
||||
|
||||
SFX S Y 4
|
||||
SFX S y ies [^aeiou]y
|
||||
SFX S 0 s [aeiou]y
|
||||
SFX S 0 es [sxzh]
|
||||
SFX S 0 s [^sxzhy]
|
||||
|
||||
SFX P Y 3
|
||||
SFX P y iness [^aeiou]y
|
||||
SFX P 0 ness [aeiou]y
|
||||
SFX P 0 ness [^y]
|
||||
|
||||
SFX M Y 1
|
||||
SFX M 0 's .
|
||||
|
||||
SFX B Y 3
|
||||
SFX B 0 able [^aeiou]
|
||||
SFX B 0 able ee
|
||||
SFX B e able [^aeiou]e
|
||||
|
||||
SFX L Y 1
|
||||
SFX L 0 ment .
|
||||
|
||||
REP 90
|
||||
REP a ei
|
||||
REP ei a
|
||||
REP a ey
|
||||
REP ey a
|
||||
REP ai ie
|
||||
REP ie ai
|
||||
REP alot a_lot
|
||||
REP are air
|
||||
REP are ear
|
||||
REP are eir
|
||||
REP air are
|
||||
REP air ere
|
||||
REP ere air
|
||||
REP ere ear
|
||||
REP ere eir
|
||||
REP ear are
|
||||
REP ear air
|
||||
REP ear ere
|
||||
REP eir are
|
||||
REP eir ere
|
||||
REP ch te
|
||||
REP te ch
|
||||
REP ch ti
|
||||
REP ti ch
|
||||
REP ch tu
|
||||
REP tu ch
|
||||
REP ch s
|
||||
REP s ch
|
||||
REP ch k
|
||||
REP k ch
|
||||
REP f ph
|
||||
REP ph f
|
||||
REP gh f
|
||||
REP f gh
|
||||
REP i igh
|
||||
REP igh i
|
||||
REP i uy
|
||||
REP uy i
|
||||
REP i ee
|
||||
REP ee i
|
||||
REP j di
|
||||
REP di j
|
||||
REP j gg
|
||||
REP gg j
|
||||
REP j ge
|
||||
REP ge j
|
||||
REP s ti
|
||||
REP ti s
|
||||
REP s ci
|
||||
REP ci s
|
||||
REP k cc
|
||||
REP cc k
|
||||
REP k qu
|
||||
REP qu k
|
||||
REP kw qu
|
||||
REP o eau
|
||||
REP eau o
|
||||
REP o ew
|
||||
REP ew o
|
||||
REP oo ew
|
||||
REP ew oo
|
||||
REP ew ui
|
||||
REP ui ew
|
||||
REP oo ui
|
||||
REP ui oo
|
||||
REP ew u
|
||||
REP u ew
|
||||
REP oo u
|
||||
REP u oo
|
||||
REP u oe
|
||||
REP oe u
|
||||
REP u ieu
|
||||
REP ieu u
|
||||
REP ue ew
|
||||
REP ew ue
|
||||
REP uff ough
|
||||
REP oo ieu
|
||||
REP ieu oo
|
||||
REP ier ear
|
||||
REP ear ier
|
||||
REP ear air
|
||||
REP air ear
|
||||
REP w qu
|
||||
REP qu w
|
||||
REP z ss
|
||||
REP ss z
|
||||
REP shun tion
|
||||
REP shun sion
|
||||
REP shun cion
|
||||
REP size cise
|
||||
@@ -0,0 +1,8 @@
|
||||
# tests for <img> elements without alt attribute
|
||||
extends: existence
|
||||
message: '<img> elements need the alt attribute to be the first attribute.'
|
||||
link: https://angular.io/guide/docs-style-guide#images
|
||||
level: error
|
||||
scope: raw
|
||||
raw:
|
||||
- '<img (?!alt=)[^\>]+>'
|
||||
@@ -0,0 +1,8 @@
|
||||
# tests for blank lines after an HTML tag or tag group
|
||||
extends: existence
|
||||
message: 'HTML in the Markdown should be followed by a blank line.'
|
||||
link: https://angular.io/guide/docs-style-guide#markdown-and-html
|
||||
level: error
|
||||
scope: raw
|
||||
raw:
|
||||
- '[\r\n]\<.+\>[\r\n](?!(\s*[\r\n\<]))'
|
||||
@@ -0,0 +1,8 @@
|
||||
# tests for blank lines before an HTML tag or tag group
|
||||
extends: existence
|
||||
message: 'HTML in the Markdown should be preceded by a blank line.'
|
||||
link: https://angular.io/guide/docs-style-guide#markdown-and-html
|
||||
level: error
|
||||
scope: raw
|
||||
raw:
|
||||
- '(?<![\r\n\>])[\r\n]\<.+\>[\r\n]'
|
||||
@@ -0,0 +1,8 @@
|
||||
# code and CLI references that should be marked as code
|
||||
extends: existence
|
||||
message: 'Clarify if this refers to a specific CLI, a specific ng command, or a command-line tool.'
|
||||
link: 'https://angular.io/guide/glossary#command-line-interface-cli'
|
||||
ignorecase: false
|
||||
level: warning
|
||||
tokens:
|
||||
- '((?<!Angular )CLI(?! [B|b]uilder))'
|
||||
@@ -0,0 +1,8 @@
|
||||
# tests for strings qualified by back-ticks are surrounded by spaces or punctuation
|
||||
extends: existence
|
||||
message: 'The code term should be followed by a space or punctuation.'
|
||||
link: https://angular.io/guide/docs-style-guide#when-to-use-code-font
|
||||
level: error
|
||||
scope: raw
|
||||
tokens:
|
||||
- '\s\x60[^\x60]+\x60[^ .;\n\r]'
|
||||
@@ -0,0 +1,8 @@
|
||||
# code and CLI references that should be marked as code
|
||||
extends: existence
|
||||
message: "If '%s' is a code reference, make sure it's marked correctly."
|
||||
link: 'https://developers.google.com/style/code-in-text'
|
||||
scope: sentence
|
||||
ignorecase: false
|
||||
level: suggestion
|
||||
tokens: (ng [a-z]+)
|
||||
@@ -0,0 +1,17 @@
|
||||
# Inclusuve language tested by Angular CI/CD
|
||||
# tests for strings that must be replaced before the content is published
|
||||
extends: substitution
|
||||
message: "Use '%s' instead of '%s'."
|
||||
link: 'https://developers.google.com/style/word-list'
|
||||
level: error
|
||||
ignorecase: true
|
||||
action:
|
||||
name: replace
|
||||
swap:
|
||||
master: primary|main|leader|active|writer
|
||||
slave: secondary|node|worker|replica|passive
|
||||
'white[\s_-]*list': include list|allow list
|
||||
'black[\s_-]*list': exclude list|deny list
|
||||
grandfathered: legacied|exempted
|
||||
'sanity[\s_-]*check': confidence check
|
||||
'man[\s_-]*hours': person-hours|human-hours
|
||||
@@ -0,0 +1,11 @@
|
||||
# tests for NOTE strings that are not bolded
|
||||
extends: existence
|
||||
message: 'Alert heading must be bold.'
|
||||
link: https://angular.io/guide/docs-style-guide#alerts-and-callouts
|
||||
level: error
|
||||
ignorecase: false
|
||||
scope: raw
|
||||
raw:
|
||||
- (?<!\*\*)NOTE[^\*]*(?!\*\*)
|
||||
- (?<!\*\*)TIP[^\*]*(?!\*\*)
|
||||
- (?<!\*\*)IMPORTANT[^\*]*(?!\*\*)
|
||||
@@ -0,0 +1,7 @@
|
||||
extends: occurrence
|
||||
message: 'Try to keep your sentences shorter than 26 words. Shorter sentences tend to be easier to read.'
|
||||
link: https://developers.google.com/style/accessibility#ease-of-reading
|
||||
level: suggestion
|
||||
scope: sentence
|
||||
max: 26
|
||||
token: '[\w-]+'
|
||||