Compare commits
333 Commits
11.0.0-nex
...
zone.js-0.
Author | SHA1 | Date | |
---|---|---|---|
a38293d213 | |||
4dc8d83d84 | |||
492e236ae6 | |||
34b74cecb6 | |||
81aa119739 | |||
49197d12a0 | |||
9ec2bad4dc | |||
e8d47c2d41 | |||
765fa337e3 | |||
6570292672 | |||
4610093c87 | |||
a3812c6cbd | |||
96f59f64a0 | |||
5bda62c51d | |||
f9f3c54c9a | |||
7669827436 | |||
178a0437b7 | |||
34dbba4a90 | |||
ef608bff83 | |||
06d0d8e31d | |||
60f761b4c2 | |||
93437bbc16 | |||
85be258845 | |||
56ab6d56e9 | |||
6e18d2dacc | |||
9bfb508b87 | |||
497af77d7a | |||
822b838fbc | |||
ac0016cd82 | |||
eb9eebbb45 | |||
aee2d3f994 | |||
8f1317f06f | |||
a83693ddd9 | |||
dd66aa290a | |||
0e5a945ed4 | |||
90e9574ac7 | |||
cdf642b609 | |||
a5d8d03edb | |||
5cfe81f919 | |||
0ec7043490 | |||
c4becca0e4 | |||
b0a8b69ae8 | |||
93ee05d92a | |||
783a5bd7bb | |||
c6350116b5 | |||
898be92f70 | |||
f6d5cdfbd7 | |||
5913e5c4e8 | |||
929e0df377 | |||
c53f19ac47 | |||
ffc3332dcd | |||
8fd25d9614 | |||
a051d97429 | |||
0001dbdede | |||
cbef410e3f | |||
db37c17e02 | |||
61e6ca9b1f | |||
78a33eeef9 | |||
584f37cfca | |||
7d4929918d | |||
b642f0bf45 | |||
520911efd4 | |||
0875fd2360 | |||
c4266fb729 | |||
5ce71e0fbc | |||
437e563507 | |||
b0b4953fd6 | |||
72f4497f11 | |||
96dd78e0c6 | |||
c9b36f9150 | |||
c76dda6aeb | |||
6947ceaf44 | |||
5471789664 | |||
952710b43b | |||
2736a43ecb | |||
f4fee86f77 | |||
47eab61cad | |||
0accd1e68d | |||
2c0282f4c2 | |||
6650d71fe2 | |||
65997c0649 | |||
1d6e67478e | |||
5038e5741b | |||
11485d96fb | |||
acfce0ba1b | |||
ed81588c79 | |||
87274e3eec | |||
a5a7845593 | |||
f35e9158b2 | |||
cee393d0da | |||
118f37b174 | |||
e930b0cd19 | |||
62945efab7 | |||
4cc5982e58 | |||
a84976fdfc | |||
bf717b1a31 | |||
9f3388e491 | |||
b2342d4116 | |||
7f689a291a | |||
ca4ef61c06 | |||
4a1c12c773 | |||
0e733f3689 | |||
0a16e60afa | |||
f84b3786dc | |||
4b06ef75aa | |||
42a164f522 | |||
049b453149 | |||
dace4aacb6 | |||
63624a2d46 | |||
4604fe9ed2 | |||
01ec0f0933 | |||
de66818cf6 | |||
4ede190571 | |||
d4a51ae594 | |||
74a9e126d5 | |||
293270696b | |||
f28b451b0c | |||
5fa012efb8 | |||
aeec223db1 | |||
f2fca6d58e | |||
72755eadd2 | |||
3975dd90a6 | |||
05672ab136 | |||
70e13dc31e | |||
246de9aaad | |||
07a66fc4c2 | |||
6e0a8eb268 | |||
21dfb33b27 | |||
3c474ecf56 | |||
ee03408c44 | |||
d8c05347c3 | |||
faa81dcdd1 | |||
e10b3e22ac | |||
57f8dd2978 | |||
bc69182bdd | |||
82d54fe8c3 | |||
a48c8edd83 | |||
75e90fa456 | |||
26afcdd86c | |||
f3150223da | |||
f4f1bcc997 | |||
4beaddcb83 | |||
89c5255b8c | |||
5dbf357224 | |||
3bbbf39b58 | |||
904adb72d2 | |||
4fe673d518 | |||
d3bd00b7bf | |||
c214cad2b4 | |||
db56cf18ba | |||
f2341e2372 | |||
f7213cb57a | |||
4bce21358d | |||
6205ed0fcc | |||
d66d82a9b5 | |||
889470663d | |||
ac54019315 | |||
26574a7c46 | |||
a7e1eb5312 | |||
2cc3ac0299 | |||
be98a45dd9 | |||
555ae7431d | |||
7dd0db6d4f | |||
1f5d7dc394 | |||
8c16330895 | |||
21213e1531 | |||
fb12c617a4 | |||
ea1968317e | |||
5b15e5eeae | |||
bd5e425c14 | |||
79620f5139 | |||
ac3aa046e5 | |||
23e2188192 | |||
5903e8ad65 | |||
9d04b95166 | |||
eec9f4a84e | |||
8e3710349d | |||
d1f5654f0c | |||
6ae1f198e6 | |||
1a0e7cca2d | |||
c8d757624b | |||
31e42f0947 | |||
ddc9e8e47a | |||
1e3f810f8f | |||
e8084ff906 | |||
827245fdb6 | |||
8b7acc4f8f | |||
393ce5574b | |||
2aeaedc519 | |||
8f66540152 | |||
8f11b516f8 | |||
c8958d76db | |||
f6052a915d | |||
37ed4bd9ff | |||
efe0e03fe1 | |||
ded9aeb447 | |||
b16a69c7a4 | |||
a6971ba89a | |||
93c3d8f9fd | |||
1b70dc931d | |||
3ba97ab391 | |||
e2997ed5ee | |||
bf010b9a07 | |||
387fd89b3f | |||
e991fee6bf | |||
06525cfed3 | |||
e9a8f9f705 | |||
3f9be429fc | |||
4fd635a03a | |||
53a7ed4317 | |||
2df39ee338 | |||
2b6b9c95f9 | |||
c5e178997c | |||
cb7164a236 | |||
4a9f4520ce | |||
914d7099ee | |||
e459dc7c42 | |||
fd7146cc71 | |||
5ef2c983b9 | |||
c74917a7d5 | |||
494a2f3be4 | |||
fc3b3fe39e | |||
5448e84cf0 | |||
7bd18fca19 | |||
5a86fb33ba | |||
5db84d7221 | |||
eb32b6bd6b | |||
b2579d43cd | |||
9fb541787c | |||
812615bb99 | |||
2ede800f0c | |||
f96dcc5ce0 | |||
372a6cf8d7 | |||
b9dce19b3d | |||
964ac1542a | |||
8b01d42e5d | |||
474ecf6d00 | |||
caa7f9808f | |||
69bb49e530 | |||
9dccaa9570 | |||
e1c11a36c7 | |||
c1b47bcfaa | |||
d7ff8d765c | |||
3a598cf5ed | |||
b041c118e3 | |||
6af638d58e | |||
9e0e763156 | |||
758d0e2045 | |||
4744c229db | |||
4dfe0fa068 | |||
3b919ef10f | |||
7b2aac97df | |||
daf8b7f100 | |||
5f815c0565 | |||
c7d5555dfb | |||
323be39297 | |||
0dd859757a | |||
239968d2f1 | |||
cf9176eb46 | |||
2f6777df18 | |||
4ca1c736bb | |||
f2c00aab8d | |||
9aff09b827 | |||
e790c8547e | |||
b627f7f02e | |||
7f6f8bbffb | |||
e4f4d18e7e | |||
837889f0a4 | |||
bd7d8744fa | |||
4c8766573d | |||
8422633b8a | |||
37297cfed7 | |||
19d543f71e | |||
f979914d4e | |||
40975e06c6 | |||
ffe89fb07d | |||
15ea811f05 | |||
4baabf9cd3 | |||
7244e1b4e3 | |||
b1682526dd | |||
75610505c6 | |||
ba3f4c26bb | |||
c8f056beb6 | |||
a2068523fd | |||
145ab3d7e0 | |||
3082f7378b | |||
297b123151 | |||
a93605f2a4 | |||
15dfd3439a | |||
123bff7cb6 | |||
6158dc16b4 | |||
b0a43872a8 | |||
856e74ac98 | |||
6ae3b68acf | |||
49f27e31ed | |||
1a62f74496 | |||
2b1b7180db | |||
fdd4fa0009 | |||
984ed39195 | |||
0c0c54d615 | |||
dfbfabc052 | |||
f3f6a42342 | |||
e498ea9b5a | |||
a91f0f6b82 | |||
d5ddb9f340 | |||
b68fab547a | |||
16a560119a | |||
88d7bb8386 | |||
2d6105a784 | |||
95b8a8706a | |||
d92a0dd72f | |||
55485713a3 | |||
d3169c533e | |||
e4424863c2 | |||
d795a00137 | |||
7fb388f929 | |||
02c6bff9cf | |||
ef28e15884 | |||
a33d630a21 | |||
c4b8964424 | |||
dd8d8c8289 | |||
129107191c | |||
722699fb0c | |||
5614258cc7 | |||
e62a918542 | |||
49ee90b1b5 | |||
7849fdde09 | |||
97adc27207 | |||
41bc2701c4 | |||
a765530277 | |||
5b33798796 | |||
44074499dc | |||
ba54671993 |
4
.bazelrc
4
.bazelrc
@ -74,8 +74,8 @@ test --test_output=errors
|
|||||||
# Trick bazel into treating BUILD files under integration/bazel as being regular files
|
# Trick bazel into treating BUILD files under integration/bazel as being regular files
|
||||||
# This lets us glob() up all the files inside this integration test to make them inputs to tests
|
# This lets us glob() up all the files inside this integration test to make them inputs to tests
|
||||||
# (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it)
|
# (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it)
|
||||||
build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e
|
build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e
|
||||||
query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e
|
query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e
|
||||||
|
|
||||||
################################
|
################################
|
||||||
# Temporary Settings for Ivy #
|
# Temporary Settings for Ivy #
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
3.2.0
|
3.5.1
|
||||||
# [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117]
|
# [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117]
|
||||||
# When updating the Bazel version you also need to update the RBE toolchains version in package.bzl
|
# When updating the Bazel version you also need to update the RBE toolchains version in package.bzl
|
||||||
|
@ -14,8 +14,8 @@ build --repository_cache=/home/circleci/bazel_repository_cache
|
|||||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||||
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
|
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
|
||||||
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
|
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
|
||||||
build --local_cpu_resources=8
|
build --local_cpu_resources=20
|
||||||
build --local_ram_resources=14336
|
build --local_ram_resources=32768
|
||||||
|
|
||||||
# All build executed remotely should be done using our RBE configuration.
|
# All build executed remotely should be done using our RBE configuration.
|
||||||
build:remote --google_default_credentials
|
build:remote --google_default_credentials
|
||||||
|
@ -11,8 +11,8 @@ try-import %workspace%/.circleci/bazel.common.rc
|
|||||||
build --repository_cache=C:/Users/circleci/bazel_repository_cache
|
build --repository_cache=C:/Users/circleci/bazel_repository_cache
|
||||||
|
|
||||||
# Manually set the local resources used in windows CI runs
|
# Manually set the local resources used in windows CI runs
|
||||||
build --local_ram_resources=13500
|
build --local_ram_resources=120000
|
||||||
build --local_cpu_resources=4
|
build --local_cpu_resources=32
|
||||||
|
|
||||||
# All windows jobs run on master and should use http caching
|
# All windows jobs run on master and should use http caching
|
||||||
build --remote_http_cache=https://storage.googleapis.com/angular-team-cache
|
build --remote_http_cache=https://storage.googleapis.com/angular-team-cache
|
||||||
|
@ -27,8 +27,6 @@ var_3: &cache_key v7-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum
|
|||||||
# folder will contain all previously used versions and ultimately cause the cache restoring to
|
# folder will contain all previously used versions and ultimately cause the cache restoring to
|
||||||
# be slower due to its growing size.
|
# be slower due to its growing size.
|
||||||
var_4: &cache_key_fallback v7-angular-node-12-{{ checksum ".bazelversion" }}
|
var_4: &cache_key_fallback v7-angular-node-12-{{ checksum ".bazelversion" }}
|
||||||
var_3_win: &cache_key_win v7-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
|
||||||
var_4_win: &cache_key_win_fallback v7-angular-win-node-12-{{ checksum ".bazelversion" }}
|
|
||||||
|
|
||||||
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
|
# 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.
|
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
|
||||||
@ -80,7 +78,7 @@ executors:
|
|||||||
|
|
||||||
windows-executor:
|
windows-executor:
|
||||||
working_directory: ~/ng
|
working_directory: ~/ng
|
||||||
resource_class: windows.medium
|
resource_class: windows.2xlarge
|
||||||
# CircleCI windows VMs do have the GitBash shell available:
|
# CircleCI windows VMs do have the GitBash shell available:
|
||||||
# https://github.com/CircleCI-Public/windows-preview-docs#shells
|
# https://github.com/CircleCI-Public/windows-preview-docs#shells
|
||||||
# But in this specific case we really should not use it because Bazel must not be ran from
|
# But in this specific case we really should not use it because Bazel must not be ran from
|
||||||
@ -183,23 +181,12 @@ commands:
|
|||||||
setup_win:
|
setup_win:
|
||||||
description: Setup windows node environment
|
description: Setup windows node environment
|
||||||
steps:
|
steps:
|
||||||
# Use the Linux workspace directly, as it already has checkout, rebased and node modules.
|
- checkout
|
||||||
- custom_attach_workspace
|
|
||||||
# Install Bazel pre-requisites that aren't in the preconfigured CircleCI Windows VM.
|
# Install Bazel pre-requisites that aren't in the preconfigured CircleCI Windows VM.
|
||||||
- run: ./.circleci/windows-env.ps1
|
- run: ./.circleci/windows-env.ps1
|
||||||
- run: node --version
|
- run: node --version
|
||||||
- run: yarn --version
|
- run: yarn --version
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- *cache_key_win
|
|
||||||
- *cache_key_win_fallback
|
|
||||||
# Reinstall to get windows binaries.
|
|
||||||
- run: yarn install --frozen-lockfile --non-interactive
|
- run: yarn install --frozen-lockfile --non-interactive
|
||||||
# Install @bazel/bazelisk globally and use that for the first run.
|
|
||||||
# Workaround for https://github.com/bazelbuild/rules_nodejs/issues/894
|
|
||||||
# NB: the issue was for @bazel/bazel but the same problem applies to @bazel/bazelisk
|
|
||||||
- run: yarn global add @bazel/bazelisk@$env:BAZELISK_VERSION
|
|
||||||
- run: bazelisk info
|
|
||||||
|
|
||||||
notify_webhook_on_fail:
|
notify_webhook_on_fail:
|
||||||
description: Notify a webhook about failure
|
description: Notify a webhook about failure
|
||||||
@ -273,6 +260,7 @@ jobs:
|
|||||||
- run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check
|
- run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check
|
||||||
- run: yarn -s ts-circular-deps:check
|
- run: yarn -s ts-circular-deps:check
|
||||||
- run: yarn -s ng-dev pullapprove verify
|
- run: yarn -s ng-dev pullapprove verify
|
||||||
|
- run: yarn -s ng-dev ngbot verify
|
||||||
- run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE
|
- run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@ -748,7 +736,8 @@ jobs:
|
|||||||
cp dist/bin/packages/zone.js/npm_package/bundles/zone-mix.umd.js ./packages/zone.js/test/extra/ &&
|
cp dist/bin/packages/zone.js/npm_package/bundles/zone-mix.umd.js ./packages/zone.js/test/extra/ &&
|
||||||
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/ &&
|
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/ &&
|
||||||
yarn --cwd packages/zone.js electrontest
|
yarn --cwd packages/zone.js electrontest
|
||||||
- run: yarn --cwd packages/zone.js jesttest
|
- run: yarn --cwd packages/zone.js jest:test
|
||||||
|
- run: yarn --cwd packages/zone.js jest:nodetest
|
||||||
- run: yarn --cwd packages/zone.js/test/typings install --frozen-lockfile --non-interactive
|
- run: yarn --cwd packages/zone.js/test/typings install --frozen-lockfile --non-interactive
|
||||||
- run: yarn --cwd packages/zone.js/test/typings test
|
- run: yarn --cwd packages/zone.js/test/typings test
|
||||||
|
|
||||||
@ -764,12 +753,6 @@ jobs:
|
|||||||
# Probably a powershell command parsing thing. There's no problem using a yarn script though.
|
# Probably a powershell command parsing thing. There's no problem using a yarn script though.
|
||||||
command: yarn circleci-win-ve
|
command: yarn circleci-win-ve
|
||||||
no_output_timeout: 45m
|
no_output_timeout: 45m
|
||||||
# Save bazel repository cache to use on subsequent runs.
|
|
||||||
# We don't save node_modules because it's faster to use the linux workspace and reinstall.
|
|
||||||
- save_cache:
|
|
||||||
key: *cache_key_win
|
|
||||||
paths:
|
|
||||||
- "C:/Users/circleci/bazel_repository_cache"
|
|
||||||
|
|
||||||
test_ivy_aot_win:
|
test_ivy_aot_win:
|
||||||
executor: windows-executor
|
executor: windows-executor
|
||||||
@ -861,26 +844,25 @@ workflows:
|
|||||||
- build-npm-packages
|
- build-npm-packages
|
||||||
- build-ivy-npm-packages
|
- build-ivy-npm-packages
|
||||||
- legacy-unit-tests-saucelabs
|
- legacy-unit-tests-saucelabs
|
||||||
- components-repo-unit-tests:
|
# Temporarily disabled components-repo-unit-tests to update rules_nodejs to 2.0.0. Breaking changes in
|
||||||
requires:
|
# rules_nodejs create a dependency sandwich between angular/angular & angular/components that are very
|
||||||
- build-npm-packages
|
# difficult and time consuming to resolve and involve patching @angular/bazel in components repo such
|
||||||
|
# as https://github.com/angular/components/commit/9e7ba251207df77164d73d66620e619bcbc4d2ad. It is simpler to
|
||||||
|
# 1) land angular/angular upgrade to rule_nodejs 2.0.0 which has breaking changes
|
||||||
|
# 2) land angular/components upgrade to rules_nodejs 2.0.0 using the @angular/bazel builds snapshot
|
||||||
|
# 3) update angular/angular to the landed components commit and re-enable these tests
|
||||||
|
# - components-repo-unit-tests:
|
||||||
|
# requires:
|
||||||
|
# - build-npm-packages
|
||||||
- test_zonejs:
|
- test_zonejs:
|
||||||
requires:
|
requires:
|
||||||
- setup
|
- setup
|
||||||
# Windows Jobs
|
|
||||||
# These are very slow so we run them on non-PRs only for now.
|
|
||||||
# TODO: remove the filter when CircleCI makes Windows FS faster.
|
|
||||||
# The Windows jobs are only run after their non-windows counterparts finish successfully.
|
|
||||||
# This isn't strictly necessary as there is no artifact dependency, but helps economize
|
|
||||||
# CI resources by not attempting to build when we know should fail.
|
|
||||||
- test_win:
|
- test_win:
|
||||||
<<: *skip_on_pull_requests
|
|
||||||
requires:
|
requires:
|
||||||
- test
|
- setup
|
||||||
- test_ivy_aot_win:
|
- test_ivy_aot_win:
|
||||||
<<: *skip_on_pull_requests
|
|
||||||
requires:
|
requires:
|
||||||
- test_ivy_aot
|
- setup
|
||||||
|
|
||||||
monitoring:
|
monitoring:
|
||||||
jobs:
|
jobs:
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
# https://docs.bazel.build/versions/master/install-windows.html
|
# https://docs.bazel.build/versions/master/install-windows.html
|
||||||
# https://docs.bazel.build/versions/master/windows.html
|
# https://docs.bazel.build/versions/master/windows.html
|
||||||
# Install MSYS2 and packages
|
# Install MSYS2 and packages
|
||||||
choco install msys2 --version 20180531.0.0 --no-progress --package-parameters "/NoUpdate"
|
choco install msys2 --version 20200903.0.0 --no-progress --package-parameters "/NoUpdate"
|
||||||
C:\tools\msys64\usr\bin\bash.exe -l -c "pacman --needed --noconfirm -S zip unzip patch diffutils git"
|
C:\tools\msys64\usr\bin\bash.exe -l -c "pacman --needed --noconfirm -S zip unzip patch diffutils"
|
||||||
|
|
||||||
# Add PATH modifications to the Powershell profile. This is the win equivalent of .bash_profile.
|
# Add PATH modifications to the Powershell profile. This is the win equivalent of .bash_profile.
|
||||||
# https://docs.microsoft.com/en-us/previous-versions//bb613488(v=vs.85)
|
# https://docs.microsoft.com/en-us/previous-versions//bb613488(v=vs.85)
|
||||||
@ -41,7 +41,8 @@ copy .circleci\bazel.windows.rc ${Env:USERPROFILE}\.bazelrc
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Install specific version of node.
|
# Install specific version of node.
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
choco install nodejs --version 12.14.1 --no-progress
|
nvm install 12.14.1
|
||||||
|
nvm use 12.14.1
|
||||||
|
|
||||||
# These Bazel prereqs aren't needed because the CircleCI image already includes them.
|
# These Bazel prereqs aren't needed because the CircleCI image already includes them.
|
||||||
# choco install yarn --version 1.16.0 --no-progress
|
# choco install yarn --version 1.16.0 --no-progress
|
||||||
|
27
.github/angular-robot.yml
vendored
27
.github/angular-robot.yml
vendored
@ -38,6 +38,7 @@ merge:
|
|||||||
- "modules/benchmarks/**"
|
- "modules/benchmarks/**"
|
||||||
- "modules/system.d.ts"
|
- "modules/system.d.ts"
|
||||||
- "packages/**"
|
- "packages/**"
|
||||||
|
- "dev-infra/benchmark/driver-utilities/**"
|
||||||
# list of patterns to ignore for the files changed by the PR
|
# list of patterns to ignore for the files changed by the PR
|
||||||
exclude:
|
exclude:
|
||||||
- "packages/*"
|
- "packages/*"
|
||||||
@ -47,7 +48,10 @@ merge:
|
|||||||
- "packages/bazel/src/ng_package/**"
|
- "packages/bazel/src/ng_package/**"
|
||||||
- "packages/bazel/src/protractor/**"
|
- "packages/bazel/src/protractor/**"
|
||||||
- "packages/bazel/src/schematics/**"
|
- "packages/bazel/src/schematics/**"
|
||||||
|
- "packages/compiler-cli/src/ngcc/**"
|
||||||
|
- "packages/compiler-cli/linker/**"
|
||||||
- "packages/compiler-cli/ngcc/**"
|
- "packages/compiler-cli/ngcc/**"
|
||||||
|
- "packages/compiler-cli/src/ngtsc/sourcemaps/**"
|
||||||
- "packages/docs/**"
|
- "packages/docs/**"
|
||||||
- "packages/elements/schematics/**"
|
- "packages/elements/schematics/**"
|
||||||
- "packages/examples/**"
|
- "packages/examples/**"
|
||||||
@ -55,6 +59,8 @@ merge:
|
|||||||
- "packages/localize/**"
|
- "packages/localize/**"
|
||||||
- "packages/private/**"
|
- "packages/private/**"
|
||||||
- "packages/service-worker/**"
|
- "packages/service-worker/**"
|
||||||
|
- "packages/common/locales/**"
|
||||||
|
- "packages/http/**"
|
||||||
- "**/.gitignore"
|
- "**/.gitignore"
|
||||||
- "**/.gitkeep"
|
- "**/.gitkeep"
|
||||||
- "**/yarn.lock"
|
- "**/yarn.lock"
|
||||||
@ -68,6 +74,7 @@ merge:
|
|||||||
- "packages/**/integrationtest/**"
|
- "packages/**/integrationtest/**"
|
||||||
- "packages/**/test/**"
|
- "packages/**/test/**"
|
||||||
- "packages/zone.js/*"
|
- "packages/zone.js/*"
|
||||||
|
- "packages/zone.js/dist/**"
|
||||||
- "packages/zone.js/doc/**"
|
- "packages/zone.js/doc/**"
|
||||||
- "packages/zone.js/example/**"
|
- "packages/zone.js/example/**"
|
||||||
- "packages/zone.js/scripts/**"
|
- "packages/zone.js/scripts/**"
|
||||||
@ -135,24 +142,28 @@ triage:
|
|||||||
# arrays of labels that determine if an issue has been fully triaged
|
# arrays of labels that determine if an issue has been fully triaged
|
||||||
l2TriageLabels:
|
l2TriageLabels:
|
||||||
-
|
-
|
||||||
- "type: bug/fix"
|
- "P0"
|
||||||
- "severity*"
|
|
||||||
- "freq*"
|
|
||||||
- "comp: *"
|
- "comp: *"
|
||||||
-
|
-
|
||||||
- "type: feature"
|
- "P1"
|
||||||
- "comp: *"
|
- "comp: *"
|
||||||
-
|
-
|
||||||
- "type: refactor"
|
- "P2"
|
||||||
- "comp: *"
|
- "comp: *"
|
||||||
-
|
-
|
||||||
- "type: RFC / Discussion / question"
|
- "P3"
|
||||||
- "comp: *"
|
- "comp: *"
|
||||||
-
|
-
|
||||||
- "type: confusing"
|
- "P4"
|
||||||
- "comp: *"
|
- "comp: *"
|
||||||
-
|
-
|
||||||
- "type: use-case"
|
- "P5"
|
||||||
|
- "comp: *"
|
||||||
|
-
|
||||||
|
- "feature"
|
||||||
|
- "comp: *"
|
||||||
|
-
|
||||||
|
- "discussion"
|
||||||
- "comp: *"
|
- "comp: *"
|
||||||
|
|
||||||
# options for the triage PR plugin
|
# options for the triage PR plugin
|
||||||
|
@ -105,9 +105,9 @@ Fixes #<issue number>
|
|||||||
# │ │
|
# │ │
|
||||||
# │ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
# │ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
||||||
# │ elements|forms|http|language-service|localize|platform-browser|
|
# │ elements|forms|http|language-service|localize|platform-browser|
|
||||||
# │ platform-browser-dynamic|platform-server|platform-webworker|
|
# │ platform-browser-dynamic|platform-server|router|service-worker|
|
||||||
# │ platform-webworker-dynamic|router|service-worker|upgrade|zone.js|
|
# │ upgrade|zone.js|packaging|changelog|dev-infra|docs-infra|migrations|
|
||||||
# │ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve
|
# │ ngcc|ve
|
||||||
# │ https://github.com/angular/angular/blob/master/CONTRIBUTING.md#scope
|
# │ https://github.com/angular/angular/blob/master/CONTRIBUTING.md#scope
|
||||||
# │
|
# │
|
||||||
# └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
|
# └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
|
||||||
|
@ -9,11 +9,11 @@ export const caretaker: CaretakerConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Merge Assistance Queue',
|
name: 'Merge Assistance Queue',
|
||||||
query: `is:pr is:open status:success label:"action: merge-assistance"`,
|
query: `is:pr is:open label:"action: merge-assistance"`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Primary Triage Queue',
|
name: 'Initial Triage Queue',
|
||||||
query: `is:open is:issue no:milestone`,
|
query: `is:open no:milestone`,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@ import {commitMessage} from './commit-message';
|
|||||||
import {format} from './format';
|
import {format} from './format';
|
||||||
import {github} from './github';
|
import {github} from './github';
|
||||||
import {merge} from './merge';
|
import {merge} from './merge';
|
||||||
|
import {release} from './release';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
commitMessage,
|
commitMessage,
|
||||||
@ -10,4 +11,5 @@ module.exports = {
|
|||||||
github,
|
github,
|
||||||
merge,
|
merge,
|
||||||
caretaker,
|
caretaker,
|
||||||
|
release,
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {DevInfraMergeConfig} from '../dev-infra/pr/merge/config';
|
import {DevInfraMergeConfig} from '../dev-infra/pr/merge/config';
|
||||||
import {getDefaultTargetLabelConfiguration} from '../dev-infra/pr/merge/defaults';
|
import {getDefaultTargetLabelConfiguration} from '../dev-infra/pr/merge/defaults';
|
||||||
import {github} from './github';
|
import {github} from './github';
|
||||||
|
import {release} from './release';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
|
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
|
||||||
@ -13,7 +14,9 @@ export const merge: DevInfraMergeConfig['merge'] = async api => {
|
|||||||
mergeReadyLabel: /^action: merge(-assistance)?/,
|
mergeReadyLabel: /^action: merge(-assistance)?/,
|
||||||
caretakerNoteLabel: 'action: merge-assistance',
|
caretakerNoteLabel: 'action: merge-assistance',
|
||||||
commitMessageFixupLabel: 'commit message fixup',
|
commitMessageFixupLabel: 'commit message fixup',
|
||||||
labels: await getDefaultTargetLabelConfiguration(api, github, '@angular/core'),
|
// We can pick any of the NPM packages as we are in a monorepo where all packages are
|
||||||
|
// published together with the same version and branching.
|
||||||
|
labels: await getDefaultTargetLabelConfiguration(api, github, release),
|
||||||
requiredBaseCommits: {
|
requiredBaseCommits: {
|
||||||
// PRs that target either `master` or the patch branch, need to be rebased
|
// PRs that target either `master` or the patch branch, need to be rebased
|
||||||
// on top of the latest commit message validation fix.
|
// on top of the latest commit message validation fix.
|
||||||
|
33
.ng-dev/release.ts
Normal file
33
.ng-dev/release.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import {join} from 'path';
|
||||||
|
import {exec} from 'shelljs';
|
||||||
|
import {ReleaseConfig} from '../dev-infra/release/config';
|
||||||
|
|
||||||
|
/** Configuration for the `ng-dev release` command. */
|
||||||
|
export const release: ReleaseConfig = {
|
||||||
|
npmPackages: [
|
||||||
|
'@angular/animations',
|
||||||
|
'@angular/bazel',
|
||||||
|
'@angular/common',
|
||||||
|
'@angular/compiler',
|
||||||
|
'@angular/compiler-cli',
|
||||||
|
'@angular/core',
|
||||||
|
'@angular/elements',
|
||||||
|
'@angular/forms',
|
||||||
|
'@angular/language-service',
|
||||||
|
'@angular/localize',
|
||||||
|
'@angular/platform-browser',
|
||||||
|
'@angular/platform-browser-dynamic',
|
||||||
|
'@angular/platform-server',
|
||||||
|
'@angular/platform-webworker',
|
||||||
|
'@angular/platform-webworker-dynamic',
|
||||||
|
'@angular/router',
|
||||||
|
'@angular/service-worker',
|
||||||
|
'@angular/upgrade',
|
||||||
|
],
|
||||||
|
// TODO: Implement release package building here.
|
||||||
|
buildPackages: async () => [],
|
||||||
|
// TODO: This can be removed once there is an org-wide tool for changelog generation.
|
||||||
|
generateReleaseNotesForHead: async () => {
|
||||||
|
exec('yarn -s gulp changelog', {cwd: join(__dirname, '../')});
|
||||||
|
},
|
||||||
|
};
|
@ -186,6 +186,7 @@ groups:
|
|||||||
- IgorMinar # Igor Minar
|
- IgorMinar # Igor Minar
|
||||||
- jbogarthyde # Judy Bogart
|
- jbogarthyde # Judy Bogart
|
||||||
- jelbourn # Jeremy Elbourn
|
- jelbourn # Jeremy Elbourn
|
||||||
|
- jessicajaniuk # Jessica Janiuk
|
||||||
- JiaLiPassion # Jia Li
|
- JiaLiPassion # Jia Li
|
||||||
- JoostK # Joost Koehoorn
|
- JoostK # Joost Koehoorn
|
||||||
- josephperrott # Joey Perrott
|
- josephperrott # Joey Perrott
|
||||||
@ -284,7 +285,7 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- alxhub
|
- alxhub
|
||||||
- crisbeto
|
- crisbeto
|
||||||
- devversion
|
# OOO as of 2020-09-28 - devversion
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
@ -303,8 +304,6 @@ groups:
|
|||||||
'packages/platform-browser/**',
|
'packages/platform-browser/**',
|
||||||
'packages/examples/platform-browser/**',
|
'packages/examples/platform-browser/**',
|
||||||
'packages/platform-browser-dynamic/**',
|
'packages/platform-browser-dynamic/**',
|
||||||
'packages/platform-webworker/**',
|
|
||||||
'packages/platform-webworker-dynamic/**',
|
|
||||||
'packages/examples/common/**',
|
'packages/examples/common/**',
|
||||||
'packages/docs/**',
|
'packages/docs/**',
|
||||||
'aio/content/guide/accessibility.md',
|
'aio/content/guide/accessibility.md',
|
||||||
@ -326,6 +325,7 @@ groups:
|
|||||||
'aio/content/examples/component-interaction/**',
|
'aio/content/examples/component-interaction/**',
|
||||||
'aio/content/images/guide/component-interaction/**',
|
'aio/content/images/guide/component-interaction/**',
|
||||||
'aio/content/guide/component-styles.md',
|
'aio/content/guide/component-styles.md',
|
||||||
|
'aio/content/guide/view-encapsulation.md',
|
||||||
'aio/content/examples/component-styles/**',
|
'aio/content/examples/component-styles/**',
|
||||||
'aio/content/guide/dependency-injection.md',
|
'aio/content/guide/dependency-injection.md',
|
||||||
'aio/content/examples/dependency-injection/**',
|
'aio/content/examples/dependency-injection/**',
|
||||||
@ -406,6 +406,7 @@ groups:
|
|||||||
'aio/content/guide/structural-directives.md',
|
'aio/content/guide/structural-directives.md',
|
||||||
'aio/content/examples/structural-directives/**',
|
'aio/content/examples/structural-directives/**',
|
||||||
'aio/content/guide/svg-in-templates.md',
|
'aio/content/guide/svg-in-templates.md',
|
||||||
|
'aio/content/guide/style-precedence.md',
|
||||||
'aio/content/images/guide/structural-directives/**',
|
'aio/content/images/guide/structural-directives/**',
|
||||||
'aio/content/guide/template-statements.md',
|
'aio/content/guide/template-statements.md',
|
||||||
'aio/content/guide/user-input.md',
|
'aio/content/guide/user-input.md',
|
||||||
@ -419,7 +420,7 @@ groups:
|
|||||||
- atscott
|
- atscott
|
||||||
- ~kara # do not request reviews from Kara, but allow her to approve PRs
|
- ~kara # do not request reviews from Kara, but allow her to approve PRs
|
||||||
- mhevery
|
- mhevery
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
@ -662,7 +663,7 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- AndrewKushnir
|
- AndrewKushnir
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
@ -679,7 +680,7 @@ groups:
|
|||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
@ -697,7 +698,7 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
@ -723,7 +724,7 @@ groups:
|
|||||||
- IgorMinar
|
- IgorMinar
|
||||||
- mhevery
|
- mhevery
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
reviews:
|
reviews:
|
||||||
request: -1 # request reviews from everyone
|
request: -1 # request reviews from everyone
|
||||||
required: 2 # require at least 2 approvals
|
required: 2 # require at least 2 approvals
|
||||||
@ -1115,11 +1116,13 @@ groups:
|
|||||||
'docs/DEBUG.md',
|
'docs/DEBUG.md',
|
||||||
'docs/DEBUG_COMPONENTS_REPO_IVY.md',
|
'docs/DEBUG_COMPONENTS_REPO_IVY.md',
|
||||||
'docs/DEVELOPER.md',
|
'docs/DEVELOPER.md',
|
||||||
|
'docs/FIXUP_COMMITS.md',
|
||||||
'docs/GITHUB_PROCESS.md',
|
'docs/GITHUB_PROCESS.md',
|
||||||
'docs/PR_REVIEW.md',
|
'docs/PR_REVIEW.md',
|
||||||
'docs/SAVED_REPLIES.md',
|
'docs/SAVED_REPLIES.md',
|
||||||
'docs/TOOLS.md',
|
'docs/TOOLS.md',
|
||||||
'docs/TRIAGE_AND_LABELS.md',
|
'docs/TRIAGE_AND_LABELS.md',
|
||||||
|
'docs/images/**',
|
||||||
'goldens/*',
|
'goldens/*',
|
||||||
'modules/*',
|
'modules/*',
|
||||||
'packages/*',
|
'packages/*',
|
||||||
@ -1150,7 +1153,7 @@ groups:
|
|||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- devversion
|
# OOO as of 2020-09-28 - devversion
|
||||||
- filipesilva
|
- filipesilva
|
||||||
- gkalpak
|
- gkalpak
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
@ -1184,7 +1187,7 @@ groups:
|
|||||||
- atscott
|
- atscott
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- petebacondarwin
|
- petebacondarwin
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
reviews:
|
reviews:
|
||||||
request: 4 # Request reviews from four people
|
request: 4 # Request reviews from four people
|
||||||
required: 3 # Require that three people approve
|
required: 3 # Require that three people approve
|
||||||
@ -1212,7 +1215,7 @@ groups:
|
|||||||
- atscott
|
- atscott
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- petebacondarwin
|
- petebacondarwin
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
reviews:
|
reviews:
|
||||||
request: 4 # Request reviews from four people
|
request: 4 # Request reviews from four people
|
||||||
required: 2 # Require that two people approve
|
required: 2 # Require that two people approve
|
||||||
@ -1240,7 +1243,7 @@ groups:
|
|||||||
- atscott
|
- atscott
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- petebacondarwin
|
- petebacondarwin
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
####################################################################################
|
####################################################################################
|
||||||
|
@ -44654,7 +44654,7 @@ const FOLDERS_IGNORE = [
|
|||||||
const DEFAULT_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([...FOLDERS_IGNORE,
|
const DEFAULT_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([...FOLDERS_IGNORE,
|
||||||
|
|
||||||
// ignore cruft
|
// ignore cruft
|
||||||
'yarn.lock', '.lock-wscript', '.wafpickle-{0..9}', '*.swp', '._*', 'npm-debug.log', 'yarn-error.log', '.npmrc', '.yarnrc', '.npmignore', '.gitignore', '.DS_Store']);
|
'yarn.lock', '.lock-wscript', '.wafpickle-{0..9}', '*.swp', '._*', 'npm-debug.log', 'yarn-error.log', '.npmrc', '.yarnrc', '.yarnrc.yml', '.npmignore', '.gitignore', '.DS_Store']);
|
||||||
|
|
||||||
const NEVER_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([
|
const NEVER_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([
|
||||||
// never ignore these files
|
// never ignore these files
|
||||||
@ -44663,6 +44663,7 @@ const NEVER_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([
|
|||||||
function packWithIgnoreAndHeaders(cwd, ignoreFunction, { mapHeader } = {}) {
|
function packWithIgnoreAndHeaders(cwd, ignoreFunction, { mapHeader } = {}) {
|
||||||
return tar.pack(cwd, {
|
return tar.pack(cwd, {
|
||||||
ignore: ignoreFunction,
|
ignore: ignoreFunction,
|
||||||
|
sort: true,
|
||||||
map: header => {
|
map: header => {
|
||||||
const suffix = header.name === '.' ? '' : `/${header.name}`;
|
const suffix = header.name === '.' ? '' : `/${header.name}`;
|
||||||
header.name = `package${suffix}`;
|
header.name = `package${suffix}`;
|
||||||
@ -46678,7 +46679,7 @@ function mkdirfix (name, opts, cb) {
|
|||||||
/* 194 */
|
/* 194 */
|
||||||
/***/ (function(module, exports) {
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.22.4","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}}
|
module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.22.5","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}}
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
/* 195 */
|
/* 195 */
|
||||||
@ -98338,7 +98339,7 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def
|
|||||||
|
|
||||||
const bundle = yield fetchBundle(config, bundleUrl);
|
const bundle = yield fetchBundle(config, bundleUrl);
|
||||||
|
|
||||||
const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.js`);
|
const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.cjs`);
|
||||||
reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`);
|
reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`);
|
||||||
yield (_fs || _load_fs()).mkdirp(path.dirname(yarnPath));
|
yield (_fs || _load_fs()).mkdirp(path.dirname(yarnPath));
|
||||||
yield (_fs || _load_fs()).writeFile(yarnPath, bundle);
|
yield (_fs || _load_fs()).writeFile(yarnPath, bundle);
|
||||||
@ -100190,7 +100191,7 @@ let main = exports.main = (() => {
|
|||||||
|
|
||||||
const config = new (_config || _load_config()).default(reporter);
|
const config = new (_config || _load_config()).default(reporter);
|
||||||
const outputWrapperEnabled = (0, (_conversion || _load_conversion()).boolifyWithDefault)(process.env.YARN_WRAP_OUTPUT, true);
|
const outputWrapperEnabled = (0, (_conversion || _load_conversion()).boolifyWithDefault)(process.env.YARN_WRAP_OUTPUT, true);
|
||||||
const shouldWrapOutput = outputWrapperEnabled && !(_commander || _load_commander()).default.json && command.hasWrapper((_commander || _load_commander()).default, (_commander || _load_commander()).default.args);
|
const shouldWrapOutput = outputWrapperEnabled && !(_commander || _load_commander()).default.json && command.hasWrapper((_commander || _load_commander()).default, (_commander || _load_commander()).default.args) && !(commandName === 'init' && (_commander || _load_commander()).default[`2`]);
|
||||||
|
|
||||||
if (shouldWrapOutput) {
|
if (shouldWrapOutput) {
|
||||||
reporter.header(commandName, { name: 'yarn', version: (_yarnVersion || _load_yarnVersion()).version });
|
reporter.header(commandName, { name: 'yarn', version: (_yarnVersion || _load_yarnVersion()).version });
|
||||||
@ -100604,7 +100605,7 @@ let start = (() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (yarnPath.endsWith(`.js`)) {
|
if (/\.[cm]?js$/.test(yarnPath)) {
|
||||||
exitCode = yield (0, (_child || _load_child()).spawnp)(process.execPath, [yarnPath, ...argv], opts);
|
exitCode = yield (0, (_child || _load_child()).spawnp)(process.execPath, [yarnPath, ...argv], opts);
|
||||||
} else {
|
} else {
|
||||||
exitCode = yield (0, (_child || _load_child()).spawnp)(yarnPath, argv, opts);
|
exitCode = yield (0, (_child || _load_child()).spawnp)(yarnPath, argv, opts);
|
2
.yarnrc
2
.yarnrc
@ -2,4 +2,4 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
yarn-path ".yarn/releases/yarn-1.22.4.js"
|
yarn-path ".yarn/releases/yarn-1.22.5.js"
|
||||||
|
@ -34,7 +34,7 @@ filegroup(
|
|||||||
filegroup(
|
filegroup(
|
||||||
name = "angularjs_scripts",
|
name = "angularjs_scripts",
|
||||||
srcs = [
|
srcs = [
|
||||||
# We also declare the unminfied AngularJS files since these can be used for
|
# We also declare the unminified AngularJS files since these can be used for
|
||||||
# local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts)
|
# local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts)
|
||||||
"@npm//:node_modules/angular/angular.js",
|
"@npm//:node_modules/angular/angular.js",
|
||||||
"@npm//:node_modules/angular/angular.min.js",
|
"@npm//:node_modules/angular/angular.min.js",
|
||||||
@ -47,3 +47,9 @@ filegroup(
|
|||||||
"@npm//:node_modules/angular-mocks-1.6/angular-mocks.js",
|
"@npm//:node_modules/angular-mocks-1.6/angular-mocks.js",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Detect if the build is running under --stamp
|
||||||
|
config_setting(
|
||||||
|
name = "stamp",
|
||||||
|
values = {"stamp": "true"},
|
||||||
|
)
|
||||||
|
271
CHANGELOG.md
271
CHANGELOG.md
@ -1,3 +1,274 @@
|
|||||||
|
<a name="11.0.0-next.6"></a>
|
||||||
|
# 11.0.0-next.6 (2020-10-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler-cli:** type checking of expressions within ICUs ([#39072](https://github.com/angular/angular/issues/39072)) ([0a16e60](https://github.com/angular/angular/commit/0a16e60)), closes [#39064](https://github.com/angular/angular/issues/39064)
|
||||||
|
* **core:** migrate relative link resolution with single quotes ([#39102](https://github.com/angular/angular/issues/39102)) ([049b453](https://github.com/angular/angular/commit/049b453)), closes [#39082](https://github.com/angular/angular/issues/39082)
|
||||||
|
* **core:** use Trusted Types policy in inert DOM builder ([#39208](https://github.com/angular/angular/issues/39208)) ([7d49299](https://github.com/angular/angular/commit/7d49299))
|
||||||
|
* **core:** use Trusted Types policy in named_array_type ([#39209](https://github.com/angular/angular/issues/39209)) ([f6d5cdf](https://github.com/angular/angular/commit/f6d5cdf))
|
||||||
|
* **router:** Allow undefined inputs on routerLink ([#39151](https://github.com/angular/angular/issues/39151)) ([b0b4953](https://github.com/angular/angular/commit/b0b4953))
|
||||||
|
* **router:** create schematic for preserveQueryParams ([#38762](https://github.com/angular/angular/issues/38762)) ([93ee05d](https://github.com/angular/angular/commit/93ee05d))
|
||||||
|
* **router:** remove preserveQueryParams symbol ([#38762](https://github.com/angular/angular/issues/38762)) ([783a5bd](https://github.com/angular/angular/commit/783a5bd))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **core:** add automated migration to replace async with waitForAsync ([#39212](https://github.com/angular/angular/issues/39212)) ([5ce71e0](https://github.com/angular/angular/commit/5ce71e0))
|
||||||
|
* **core:** add automated migration to replace ViewEncapsulation.Native ([#38882](https://github.com/angular/angular/issues/38882)) ([0e733f3](https://github.com/angular/angular/commit/0e733f3))
|
||||||
|
* **core:** add initialNavigation schematic ([#36926](https://github.com/angular/angular/issues/36926)) ([0ec7043](https://github.com/angular/angular/commit/0ec7043))
|
||||||
|
* **core:** add Trusted Types workaround for Function constructor ([#39209](https://github.com/angular/angular/issues/39209)) ([5913e5c](https://github.com/angular/angular/commit/5913e5c))
|
||||||
|
* **core:** create internal Trusted Types module ([#39207](https://github.com/angular/angular/issues/39207)) ([0875fd2](https://github.com/angular/angular/commit/0875fd2))
|
||||||
|
* **core:** depend on type definitions for Trusted Types ([#39207](https://github.com/angular/angular/issues/39207)) ([c4266fb](https://github.com/angular/angular/commit/c4266fb))
|
||||||
|
* **core:** remove ViewEncapsulation.Native ([#38882](https://github.com/angular/angular/issues/38882)) ([4a1c12c](https://github.com/angular/angular/commit/4a1c12c))
|
||||||
|
* **language-service:** [Ivy] getSemanticDiagnostics for external templates ([#39065](https://github.com/angular/angular/issues/39065)) ([63624a2](https://github.com/angular/angular/commit/63624a2))
|
||||||
|
* **language-service:** Add getTypeDefinitionAtPosition (go to type definition) ([#39145](https://github.com/angular/angular/issues/39145)) ([a84976f](https://github.com/angular/angular/commit/a84976f))
|
||||||
|
* **language-service:** Add module name to directive quick info ([#39121](https://github.com/angular/angular/issues/39121)) ([4604fe9](https://github.com/angular/angular/commit/4604fe9))
|
||||||
|
* **router:** add new initialNavigation options to replace legacy ([#37480](https://github.com/angular/angular/issues/37480)) ([c4becca](https://github.com/angular/angular/commit/c4becca))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **router:** * The `initialNavigation` property for the options in
|
||||||
|
`RouterModule.forRoot` no longer supports `legacy_disabled`,
|
||||||
|
`legacy_enabled`, `true`, or `false` as valid values.
|
||||||
|
`legacy_enabled` (the old default) is instead `enabledNonBlocking`
|
||||||
|
* `enabled` is deprecated as a valid value for the
|
||||||
|
`RouterModule.forRoot` `initialNavigation` option. `enabledBlocking`
|
||||||
|
has been introduced to replace it
|
||||||
|
* **router:** preserveQueryParams has been removed, use
|
||||||
|
queryParamsHandling="preserve" instead
|
||||||
|
* **router:** If you were accessing the `RouterLink` values of `queryParams`,
|
||||||
|
`fragment` or `queryParamsHandling` you might need to relax the typing to also
|
||||||
|
accept `undefined` and `null`. ([#39151](https://github.com/angular/angular/issues/39151))
|
||||||
|
* **core:** * `ViewEncapsulation.Native` has been removed. Use `ViewEncapsulation.ShadowDom` instead. Existing
|
||||||
|
usages will be updated automatically by `ng update`.
|
||||||
|
* **compiler-cli:** Expressions within ICUs are now type-checked again, fixing a regression
|
||||||
|
in Ivy. This may cause compilation failures if errors are found in
|
||||||
|
expressions that appear within an ICU. Please correct these expressions
|
||||||
|
to resolve the type-check errors.
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.1.6"></a>
|
||||||
|
## 10.1.6 (2020-10-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** incorrectly encapsulating [@import](https://github.com/import) containing colons and semicolons ([#38716](https://github.com/angular/angular/issues/38716)) ([52a0c6b](https://github.com/angular/angular/commit/52a0c6b)), closes [#38587](https://github.com/angular/angular/issues/38587)
|
||||||
|
* **compiler-cli:** support namespaced query types in directives ([#38959](https://github.com/angular/angular/issues/38959)) ([#39272](https://github.com/angular/angular/issues/39272)) ([f752ab9](https://github.com/angular/angular/commit/f752ab9))
|
||||||
|
* **elements:** detect matchesSelector prototype without IIFE ([#37799](https://github.com/angular/angular/issues/37799)) ([952fd86](https://github.com/angular/angular/commit/952fd86)), closes [#24551](https://github.com/angular/angular/issues/24551)
|
||||||
|
* **ngcc:** ensure that "inline exports" can be interpreted correctly ([#39272](https://github.com/angular/angular/issues/39272)) ([e08d021](https://github.com/angular/angular/commit/e08d021))
|
||||||
|
* **ngcc:** handle aliases in UMD export declarations ([#38959](https://github.com/angular/angular/issues/38959)) ([#39272](https://github.com/angular/angular/issues/39272)) ([9963c5d](https://github.com/angular/angular/commit/9963c5d)), closes [#38947](https://github.com/angular/angular/issues/38947)
|
||||||
|
* **ngcc:** map `exports` to the current module in UMD files ([#38959](https://github.com/angular/angular/issues/38959)) ([#39272](https://github.com/angular/angular/issues/39272)) ([13c4a7b](https://github.com/angular/angular/commit/13c4a7b))
|
||||||
|
* **ngcc:** support inline export declarations in UMD files ([#38959](https://github.com/angular/angular/issues/38959)) ([#39272](https://github.com/angular/angular/issues/39272)) ([9c875b3](https://github.com/angular/angular/commit/9c875b3)), closes [#38947](https://github.com/angular/angular/issues/38947)
|
||||||
|
|
||||||
|
|
||||||
|
### build
|
||||||
|
|
||||||
|
* upgrade angular build, integration/bazel and [@angular](https://github.com/angular)/bazel package to rule_nodejs 2.2.0 ([#39182](https://github.com/angular/angular/issues/39182)) ([7628c36](https://github.com/angular/angular/commit/7628c36))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **ngcc:** do not rescan program source files when referenced from multiple root files ([#39254](https://github.com/angular/angular/issues/39254)) ([5221df8](https://github.com/angular/angular/commit/5221df8)), closes [#39240](https://github.com/angular/angular/issues/39240)
|
||||||
|
|
||||||
|
|
||||||
|
<a name="11.0.0-next.5"></a>
|
||||||
|
# 11.0.0-next.5 (2020-10-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** add boolean to valid json for testing ([#37893](https://github.com/angular/angular/issues/37893)) ([3c474ec](https://github.com/angular/angular/commit/3c474ec)), closes [#20690](https://github.com/angular/angular/issues/20690)
|
||||||
|
* **core:** use single quotes for relative link resolution migration to align with style guide ([#39070](https://github.com/angular/angular/issues/39070)) ([8894706](https://github.com/angular/angular/commit/8894706))
|
||||||
|
* **forms:** improve types of directive constructor arguments ([#38944](https://github.com/angular/angular/issues/38944)) ([246de9a](https://github.com/angular/angular/commit/246de9a))
|
||||||
|
* **forms:** include null in .parent of abstract control ([#32671](https://github.com/angular/angular/issues/32671)) ([f4f1bcc](https://github.com/angular/angular/commit/f4f1bcc)), closes [#16999](https://github.com/angular/angular/issues/16999)
|
||||||
|
* **language-service:** [Ivy] hybrid visitor should not locate let keyword ([#39061](https://github.com/angular/angular/issues/39061)) ([70e13dc](https://github.com/angular/angular/commit/70e13dc))
|
||||||
|
* **router:** properly assign ExtraOptions to Router in RouterTestingModule ([#39096](https://github.com/angular/angular/issues/39096)) ([d8c0534](https://github.com/angular/angular/commit/d8c0534)), closes [#23347](https://github.com/angular/angular/issues/23347)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compiler-cli:** support getting resource dependencies for a source file ([#38048](https://github.com/angular/angular/issues/38048)) ([5dbf357](https://github.com/angular/angular/commit/5dbf357))
|
||||||
|
* **forms:** add migration for AbstractControl.parent accesses ([#39009](https://github.com/angular/angular/issues/39009)) ([aeec223](https://github.com/angular/angular/commit/aeec223)), closes [#32671](https://github.com/angular/angular/issues/32671)
|
||||||
|
* **language-service:** Add getDefinitionAndBoundSpan (go to definition) ([#39101](https://github.com/angular/angular/issues/39101)) ([3975dd9](https://github.com/angular/angular/commit/3975dd9))
|
||||||
|
* **language-service:** add quick info for inline templates in ivy ([#39060](https://github.com/angular/angular/issues/39060)) ([904adb7](https://github.com/angular/angular/commit/904adb7))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **forms:** Directives in the `@angular/forms` package used to have `any[]` as a type of `validators` and
|
||||||
|
`asyncValidators` arguments in constructors. Now these arguments are properly typed, so if your
|
||||||
|
code relies on directive constructor types it may require some updates to improve type safety.
|
||||||
|
* **forms:** Type of AbstractFormControl.parent now includes null
|
||||||
|
|
||||||
|
`null` is now included in the types of .parent. If you don't already have a check for this case,
|
||||||
|
the TypeScript compiler might compain. A v11 migration exists which adds the not-null assertion
|
||||||
|
operator where necessary.
|
||||||
|
|
||||||
|
In an unlikely case your code was testing the parnet against undefined with sitrct equality,
|
||||||
|
you'll need to change this to `=== null` instead, since the parent is not explicily initialized
|
||||||
|
with `null` instead of being left `undefined`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.1.5"></a>
|
||||||
|
## 10.1.5 (2020-10-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **router:** update getRouteGuards to check if the context outlet is activated ([#39049](https://github.com/angular/angular/issues/39049)) ([771f731](https://github.com/angular/angular/commit/771f731)), closes [#39030](https://github.com/angular/angular/issues/39030)
|
||||||
|
* **compiler:** Recover on malformed keyed reads and keyed writes ([#39004](https://github.com/angular/angular/issues/39004)) ([f50313f](https://github.com/angular/angular/commit/f50313f)), closes [#38596](https://github.com/angular/angular/issues/38596)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="11.0.0-next.4"></a>
|
||||||
|
# 11.0.0-next.4 (2020-09-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** correct and simplify typing of `KeyValuePipe` ([#37447](https://github.com/angular/angular/issues/37447)) ([4dfe0fa](https://github.com/angular/angular/commit/4dfe0fa))
|
||||||
|
* **common:** correct and simplify typing of AsyncPipe ([#37447](https://github.com/angular/angular/issues/37447)) ([5f815c0](https://github.com/angular/angular/commit/5f815c0))
|
||||||
|
* **common:** correct and simplify typing of I18nPluralPipe ([#37447](https://github.com/angular/angular/issues/37447)) ([3b919ef](https://github.com/angular/angular/commit/3b919ef))
|
||||||
|
* **common:** correct typing and implementation of `SlicePipe` ([#37447](https://github.com/angular/angular/issues/37447)) ([4744c22](https://github.com/angular/angular/commit/4744c22))
|
||||||
|
* **common:** let case conversion pipes accept type unions with `null` ([#36259](https://github.com/angular/angular/issues/36259)) ([#37447](https://github.com/angular/angular/issues/37447)) ([c7d5555](https://github.com/angular/angular/commit/c7d5555))
|
||||||
|
* **compiler-cli:** perform DOM schema checks even in basic mode in g3 ([#38943](https://github.com/angular/angular/issues/38943)) ([40975e0](https://github.com/angular/angular/commit/40975e0))
|
||||||
|
* **language-service:** hybrid visitor returns parent node of BoundAttribute ([#38995](https://github.com/angular/angular/issues/38995)) ([323be39](https://github.com/angular/angular/commit/323be39))
|
||||||
|
* **packaging:** remove polyfills needed to run tests on IE9 and IE 10 ([#38931](https://github.com/angular/angular/issues/38931)) ([4ca1c73](https://github.com/angular/angular/commit/4ca1c73))
|
||||||
|
* **platform-webworker:** remove platform-webworker and platform-webworker-dynamic ([#38846](https://github.com/angular/angular/issues/38846)) ([93c3d8f](https://github.com/angular/angular/commit/93c3d8f))
|
||||||
|
* **router:** make relativeLinkResolution corrected by default ([#25609](https://github.com/angular/angular/issues/25609)) ([837889f](https://github.com/angular/angular/commit/837889f)), closes [#22394](https://github.com/angular/angular/issues/22394)
|
||||||
|
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* **router:** Adjust type of parameter in navigateByUrl and createUrlTree to be more accurate ([#38227](https://github.com/angular/angular/issues/38227)) ([e4f4d18](https://github.com/angular/angular/commit/e4f4d18)), closes [#18798](https://github.com/angular/angular/issues/18798)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **common:** stricter types for DatePipe ([#37447](https://github.com/angular/angular/issues/37447)) ([daf8b7f](https://github.com/angular/angular/commit/daf8b7f))
|
||||||
|
* **common:** stricter types for number pipes ([#37447](https://github.com/angular/angular/issues/37447)) ([7b2aac9](https://github.com/angular/angular/commit/7b2aac9))
|
||||||
|
* **compiler:** Add keySpan to Variable Node ([#38965](https://github.com/angular/angular/issues/38965)) ([239968d](https://github.com/angular/angular/commit/239968d))
|
||||||
|
* **router:** Add `relativeLinkResolution` migration to update default value ([#38698](https://github.com/angular/angular/issues/38698)) ([15ea811](https://github.com/angular/angular/commit/15ea811))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **packaging:** In v10, IE 9, 10, and IE mobile support was deprecated. In v11, Angular framework removes IE 9,
|
||||||
|
10, and IE mobile support completely.
|
||||||
|
Supporting outdated browsers like these increases bundle size, code complexity, and test load,
|
||||||
|
and also requires time and effort that could be spent on improvements to the framework.
|
||||||
|
For example, fixing issues can be more difficult, as a straightforward fix for modern browsers
|
||||||
|
could break old ones that have quirks due to not receiving updates from vendors.
|
||||||
|
* **platform-webworker:** @angular/platform-webworker and @angular/platform-webworker-dynamic
|
||||||
|
have been removed as they were deprecated in v8
|
||||||
|
* **common:** The `slice` pipe now returns `null` for the `undefined` input value,
|
||||||
|
which is consistent with the behavior of most pipes. If you rely on
|
||||||
|
`undefined` being the result in that case, you now need to check for it
|
||||||
|
explicitly.
|
||||||
|
* **common:** The typing of the `keyvalue` pipe has been fixed to report that for
|
||||||
|
input objects that have `number` keys, the result will contain the
|
||||||
|
string representation of the keys. This was already the case and the
|
||||||
|
code has simply been updated to reflect this. Please update the
|
||||||
|
consumers of the pipe output if they were relying on the incorrect
|
||||||
|
types. Note that this does not affect use cases where the input values
|
||||||
|
are `Map`s, so if you need to preserve `number`s, this is an effective
|
||||||
|
way.
|
||||||
|
* **common:** The signatures of the number pipes now explicitly state which types are
|
||||||
|
accepted. This should only cause issues in corner cases, as any other
|
||||||
|
values would result in runtime exceptions.
|
||||||
|
* **common:** The signature of the `date` pipe now explicitly states which types are
|
||||||
|
accepted. This should only cause issues in corner cases, as any other
|
||||||
|
values would result in runtime exceptions.
|
||||||
|
* **common:** The async pipe no longer claims to return `undefined` for an input that
|
||||||
|
was typed as `undefined`. Note that the code actually returned `null` on
|
||||||
|
`undefined` inputs. In the unlikely case you were relying on this,
|
||||||
|
please fix the typing of the consumers of the pipe output.
|
||||||
|
* **common:** The case conversion pipes no longer let falsy values through. They now
|
||||||
|
map both `null` and `undefined` to `null` and raise an exception on
|
||||||
|
invalid input (`0`, `false`, `NaN`) just like most "common pipes". If
|
||||||
|
your code required falsy values to pass through, you need to handle them
|
||||||
|
explicitly.
|
||||||
|
* **router:** While the new parameter types allow a variable of type
|
||||||
|
`NavigationExtras` to be passed in, they will not allow object literals,
|
||||||
|
as they may only specify known properties. They will also not accept
|
||||||
|
types that do not have properties in common with the ones in the `Pick`.
|
||||||
|
To fix this error, only specify properties from the `NavigationExtras` which are
|
||||||
|
actually used in the respective function calls or use a type assertion
|
||||||
|
on the object or variable: `as NavigationExtras`.
|
||||||
|
* **router:** This commit changes the default value of
|
||||||
|
`relativeLinkResolution` from `'legacy'` to `'default'`. If your
|
||||||
|
application previously used the default by not specifying a value in the
|
||||||
|
`ExtraOptions` and uses relative links when navigating from children of
|
||||||
|
empty path routes, you will need to update your `RouterModule` to
|
||||||
|
specifically specify `'legacy'` for `relativeLinkResolution`.
|
||||||
|
See https://angular.io/api/router/ExtraOptions#relativeLinkResolution
|
||||||
|
for more details.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.1.4"></a>
|
||||||
|
## 10.1.4 (2020-09-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler-cli:** enable [@types](https://github.com/types) discovery in incremental rebuilds ([#39011](https://github.com/angular/angular/issues/39011)) ([6e99427](https://github.com/angular/angular/commit/6e99427)), closes [#38979](https://github.com/angular/angular/issues/38979)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="11.0.0-next.3"></a>
|
||||||
|
# 11.0.0-next.3 (2020-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** add `params` and `reportProgress` options to `HttpClient.put()` overload ([#37873](https://github.com/angular/angular/issues/37873)) ([dd8d8c8](https://github.com/angular/angular/commit/dd8d8c8)), closes [#23600](https://github.com/angular/angular/issues/23600)
|
||||||
|
* **compiler-cli:** generate `let` statements in ES2015+ mode ([#38775](https://github.com/angular/angular/issues/38775)) ([123bff7](https://github.com/angular/angular/commit/123bff7))
|
||||||
|
* **core:** ensure TestBed is not instantiated before override provider ([#38717](https://github.com/angular/angular/issues/38717)) ([c8f056b](https://github.com/angular/angular/commit/c8f056b))
|
||||||
|
* **forms:** type NG_VALUE_ACCESSOR injection token as array ([#29723](https://github.com/angular/angular/issues/29723)) ([2b1b718](https://github.com/angular/angular/commit/2b1b718)), closes [#29351](https://github.com/angular/angular/issues/29351)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **common:** Add ISO week-numbering year formats support to formatDate ([#38828](https://github.com/angular/angular/issues/38828)) ([984ed39](https://github.com/angular/angular/commit/984ed39))
|
||||||
|
* **compiler:** Parse and recover on incomplete opening HTML tags ([#38681](https://github.com/angular/angular/issues/38681)) ([6ae3b68](https://github.com/angular/angular/commit/6ae3b68)), closes [#38596](https://github.com/angular/angular/issues/38596)
|
||||||
|
* **router:** add migration to update calls to navigateByUrl and createUrlTree with invalid parameters ([#38825](https://github.com/angular/angular/issues/38825)) ([7849fdd](https://github.com/angular/angular/commit/7849fdd)), closes [#38227](https://github.com/angular/angular/issues/38227)
|
||||||
|
* **service-worker:** add the option to prefer network for navigation requests ([#38565](https://github.com/angular/angular/issues/38565)) ([a206852](https://github.com/angular/angular/commit/a206852)), closes [#38194](https://github.com/angular/angular/issues/38194)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **core:** If you call `TestBed.overrideProvider` after TestBed initialization, provider overrides are not applied. This
|
||||||
|
behavior is consistent with other override methods (such as `TestBed.overrideDirective`, etc) but they
|
||||||
|
throw an error to indicate that, when the check was missing in the `TestBed.overrideProvider` function.
|
||||||
|
Now calling `TestBed.overrideProvider` after TestBed initialization also triggers an
|
||||||
|
error, thus there is a chance that some tests (where `TestBed.overrideProvider` is
|
||||||
|
called after TestBed initialization) will start to fail and require updates to move `TestBed.overrideProvider` calls
|
||||||
|
before TestBed initialization is completed.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.1.3"></a>
|
||||||
|
## 10.1.3 (2020-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **http:** Fix error message when we call jsonp without importing HttpClientJsonpModule ([#38756](https://github.com/angular/angular/issues/38756)) ([3902ec0](https://github.com/angular/angular/commit/3902ec0))
|
||||||
|
* **ngcc:** fix compilation of `ChangeDetectorRef` in pipe constructors ([#38892](https://github.com/angular/angular/issues/38892)) ([093c3a1](https://github.com/angular/angular/commit/093c3a1)), closes [#38666](https://github.com/angular/angular/issues/38666) [#38883](https://github.com/angular/angular/issues/38883)
|
||||||
|
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* feat(router): better warning message when a router outlet has not been instantiated ([#38920](https://github.com/angular/angular/issues/38920)) ([04d0aa6](https://github.com/angular/angular/commit/04d0aa6))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="11.0.0-next.2"></a>
|
<a name="11.0.0-next.2"></a>
|
||||||
# 11.0.0-next.2 (2020-09-16)
|
# 11.0.0-next.2 (2020-09-16)
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
As contributors and maintainers of the Angular project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.
|
As contributors and maintainers of the Angular project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.
|
||||||
|
|
||||||
Communication through any of Angular's channels (GitHub, Gitter, IRC, mailing lists, Google+, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
Communication through any of Angular's channels (GitHub, Discord, Gitter, IRC, mailing lists, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||||
|
|
||||||
We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Angular project to do the same.
|
We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Angular project to do the same.
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ Stack Overflow is a much better place to ask questions since:
|
|||||||
|
|
||||||
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
|
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
|
||||||
|
|
||||||
If you would like to chat about the question in real-time, you can reach out via [our gitter channel][gitter].
|
If you would like to chat about the question in real-time, you can reach out via [our Discord server][discord].
|
||||||
|
|
||||||
|
|
||||||
## <a name="issue"></a> Found a Bug?
|
## <a name="issue"></a> Found a Bug?
|
||||||
@ -107,7 +107,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
|||||||
Adherence to these conventions is necessary because release notes are automatically generated from these messages.
|
Adherence to these conventions is necessary because release notes are automatically generated from these messages.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git commit -a
|
git commit --all
|
||||||
```
|
```
|
||||||
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
|
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
|
||||||
|
|
||||||
@ -119,20 +119,55 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
|||||||
|
|
||||||
11. In GitHub, send a pull request to `angular:master`.
|
11. In GitHub, send a pull request to `angular:master`.
|
||||||
|
|
||||||
If we ask for changes via code reviews then:
|
|
||||||
|
|
||||||
* Make the required updates.
|
#### Addressing review feedback
|
||||||
* Re-run the Angular test suites to ensure tests are still passing.
|
|
||||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
|
||||||
|
|
||||||
```shell
|
If we ask for changes via code reviews then:
|
||||||
git rebase master -i
|
|
||||||
git push -f
|
1. Make the required updates to the code.
|
||||||
```
|
|
||||||
|
2. Re-run the Angular test suites to ensure tests are still passing.
|
||||||
|
|
||||||
|
3. Create a fixup commit and push to your GitHub repository (this will update your Pull Request):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git commit --all --fixup HEAD
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
For more info on working with fixup commits see [here](docs/FIXUP_COMMITS.md).
|
||||||
|
|
||||||
That's it! Thank you for your contribution!
|
That's it! Thank you for your contribution!
|
||||||
|
|
||||||
|
|
||||||
|
##### Updating the commit message
|
||||||
|
|
||||||
|
A reviewer might often suggest changes to a commit message (for example, to add more context for a change or adhere to our [commit message guidelines](#commit)).
|
||||||
|
In order to update the commit message of the last commit on your branch:
|
||||||
|
|
||||||
|
1. Check out your branch:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git checkout my-fix-branch
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Amend the last commit and modify the commit message:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git commit --amend
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Push to your GitHub repository:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git push --force-with-lease
|
||||||
|
```
|
||||||
|
|
||||||
|
> NOTE:<br />
|
||||||
|
> If you need to update the commit message of an earlier commit, you can use `git rebase` in interactive mode.
|
||||||
|
> See the [git docs](https://git-scm.com/docs/git-rebase#_interactive_mode) for more details.
|
||||||
|
|
||||||
|
|
||||||
#### After your pull request is merged
|
#### After your pull request is merged
|
||||||
|
|
||||||
After your pull request is merged, you can safely delete your branch and pull the changes from the main (upstream) repository:
|
After your pull request is merged, you can safely delete your branch and pull the changes from the main (upstream) repository:
|
||||||
@ -209,9 +244,9 @@ Any line of the commit message cannot be longer than 100 characters.
|
|||||||
│ │
|
│ │
|
||||||
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
||||||
│ elements|forms|http|language-service|localize|platform-browser|
|
│ elements|forms|http|language-service|localize|platform-browser|
|
||||||
│ platform-browser-dynamic|platform-server|platform-webworker|
|
│ platform-browser-dynamic|platform-server|router|service-worker|
|
||||||
│ platform-webworker-dynamic|router|service-worker|upgrade|zone.js|
|
│ upgrade|zone.js|packaging|changelog|dev-infra|docs-infra|migrations|
|
||||||
│ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve
|
│ ngcc|ve
|
||||||
│
|
│
|
||||||
└─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
|
└─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
|
||||||
```
|
```
|
||||||
@ -253,8 +288,6 @@ The following is the list of supported scopes:
|
|||||||
* `platform-browser`
|
* `platform-browser`
|
||||||
* `platform-browser-dynamic`
|
* `platform-browser-dynamic`
|
||||||
* `platform-server`
|
* `platform-server`
|
||||||
* `platform-webworker`
|
|
||||||
* `platform-webworker-dynamic`
|
|
||||||
* `router`
|
* `router`
|
||||||
* `service-worker`
|
* `service-worker`
|
||||||
* `upgrade`
|
* `upgrade`
|
||||||
@ -349,7 +382,7 @@ The following documents can help you sort out issues with GitHub accounts and mu
|
|||||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||||
[dev-doc]: https://github.com/angular/angular/blob/master/docs/DEVELOPER.md
|
[dev-doc]: https://github.com/angular/angular/blob/master/docs/DEVELOPER.md
|
||||||
[github]: https://github.com/angular/angular
|
[github]: https://github.com/angular/angular
|
||||||
[gitter]: https://gitter.im/angular/angular
|
[discord]: https://discord.gg/angular
|
||||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||||
[js-style-guide]: https://google.github.io/styleguide/jsguide.html
|
[js-style-guide]: https://google.github.io/styleguide/jsguide.html
|
||||||
[jsfiddle]: http://jsfiddle.net
|
[jsfiddle]: http://jsfiddle.net
|
||||||
|
145
README.md
145
README.md
@ -1,26 +1,151 @@
|
|||||||
[](https://circleci.com/gh/angular/workflows/angular/tree/master)
|
<h1 align="center">Angular - One framework. Mobile & desktop.</h1>
|
||||||
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
[](https://www.npmjs.com/@angular/core)
|
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="aio/src/assets/images/logos/angular/angular.png" alt="angular-logo" width="120px" height="120px"/>
|
||||||
|
<br>
|
||||||
|
<i>Angular is a development platform for building mobile and desktop web applications
|
||||||
|
<br> using Typescript/JavaScript and other languages.</i>
|
||||||
|
<br>
|
||||||
|
</p>
|
||||||
|
|
||||||
# Angular
|
<p align="center">
|
||||||
|
<a href="https://www.angular.io"><strong>www.angular.io</strong></a>
|
||||||
|
<br>
|
||||||
|
</p>
|
||||||
|
|
||||||
Angular is a development platform for building mobile and desktop web applications using TypeScript/JavaScript and other languages.
|
<p align="center">
|
||||||
|
<a href="CONTRIBUTING.md">Contributing Guidelines</a>
|
||||||
|
·
|
||||||
|
<a href="https://github.com/angular/angular/issues">Submit an Issue</a>
|
||||||
|
·
|
||||||
|
<a href="https://blog.angular.io/">Blog</a>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://circleci.com/gh/angular/workflows/angular/tree/master">
|
||||||
|
<img src="https://img.shields.io/circleci/build/github/angular/angular/master.svg?logo=circleci&logoColor=fff&label=CircleCI" alt="CI status" />
|
||||||
|
</a>
|
||||||
|
<a href="https://www.npmjs.com/@angular/core">
|
||||||
|
<img src="https://img.shields.io/npm/v/@angular/core.svg?logo=npm&logoColor=fff&label=NPM+package&color=limegreen" alt="Angular on npm" />
|
||||||
|
</a>
|
||||||
|
<a href="https://discord.gg/angular">
|
||||||
|
<img src="https://img.shields.io/discord/463752820026376202.svg?logo=discord&logoColor=fff&label=Discord&color=7389d8" alt="Discord conversation" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Get started with Angular, learn the fundamentals and explore advanced topics on our documentation website.
|
||||||
|
|
||||||
|
- [Getting Started][quickstart]
|
||||||
|
- [Architecture][architecture]
|
||||||
|
- [Components and Templates][componentstemplates]
|
||||||
|
- [Forms][forms]
|
||||||
|
- [API][api]
|
||||||
|
|
||||||
|
### Advanced
|
||||||
|
|
||||||
|
- [Angular Elements][angularelements]
|
||||||
|
- [Server Side Rendering][ssr]
|
||||||
|
- [Schematics][schematics]
|
||||||
|
- [Lazy Loading][lazyloading]
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Install [Node.js] which includes [Node Package Manager][npm]
|
||||||
|
|
||||||
|
### Setting Up a Project
|
||||||
|
|
||||||
|
Intall the Angular CLI globally:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install -g @angular/cli
|
||||||
|
```
|
||||||
|
|
||||||
|
Create workspace:
|
||||||
|
|
||||||
|
```
|
||||||
|
ng new [PROJECT NAME]
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the application:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd [PROJECT NAME]
|
||||||
|
ng serve
|
||||||
|
```
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
[Get started in 5 minutes][quickstart].
|
[Get started in 5 minutes][quickstart].
|
||||||
|
|
||||||
|
## Ecosystem
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<img src="/docs/images/angular-ecosystem-logos.png" alt="angular ecosystem logos" width="500px" height="auto">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
- [Angular Command Line (CLI)][cli]
|
||||||
|
- [Angular Material][angularmaterial]
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
[Learn about the latest improvements][changelog].
|
[Learn about the latest improvements][changelog].
|
||||||
|
|
||||||
## Want to help?
|
## Upgrading
|
||||||
|
|
||||||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
Check out our [upgrade guide](https://update.angular.io/) to find out the best way to upgrade your project.
|
||||||
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
|
||||||
|
|
||||||
[contributing]: https://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
## Contributing
|
||||||
|
|
||||||
|
### Contributing Guidelines
|
||||||
|
|
||||||
|
Read through our [contributing guidelines][contributing] to learn about our submission process, coding rules and more.
|
||||||
|
|
||||||
|
### Want to Help?
|
||||||
|
|
||||||
|
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
||||||
|
|
||||||
|
### Code of Conduct
|
||||||
|
|
||||||
|
Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct][codeofconduct].
|
||||||
|
|
||||||
|
## Community
|
||||||
|
|
||||||
|
Join the conversation and help the community.
|
||||||
|
|
||||||
|
- [Twitter][twitter]
|
||||||
|
- [Gitter][gitter]
|
||||||
|
- Find a Local [Meetup][meetup]
|
||||||
|
|
||||||
|
[](https://www.github.com/angular/angular)
|
||||||
|
|
||||||
|
**Love Angular? Give our repo a star :star: :arrow_up:.**
|
||||||
|
|
||||||
|
[contributing]: CONTRIBUTING.md
|
||||||
[quickstart]: https://angular.io/start
|
[quickstart]: https://angular.io/start
|
||||||
[changelog]: https://github.com/angular/angular/blob/master/CHANGELOG.md
|
[changelog]: CHANGELOG.md
|
||||||
[ng]: https://angular.io
|
[ng]: https://angular.io
|
||||||
|
[documentation]: https://angular.io/docs
|
||||||
|
[angularmaterial]: https://material.angular.io/
|
||||||
|
[cli]: https://cli.angular.io/
|
||||||
|
[architecture]: https://angular.io/guide/architecture
|
||||||
|
[componentstemplates]: https://angular.io/guide/displaying-data
|
||||||
|
[forms]: https://angular.io/guide/forms-overview
|
||||||
|
[api]: https://angular.io/api
|
||||||
|
[angularelements]: https://angular.io/guide/elements
|
||||||
|
[ssr]: https://angular.io/guide/universal
|
||||||
|
[schematics]: https://angular.io/guide/schematics
|
||||||
|
[lazyloading]: https://angular.io/guide/lazy-loading-ngmodules
|
||||||
|
[node.js]: https://nodejs.org/
|
||||||
|
[npm]: https://www.npmjs.com/get-npm
|
||||||
|
[codeofconduct]: CODE_OF_CONDUCT.md
|
||||||
|
[twitter]: https://www.twitter.com/angular
|
||||||
|
[gitter]: https://gitter.im/angular/angular
|
||||||
|
[meetup]: https://www.meetup.com/find/?keywords=angular"
|
||||||
|
24
WORKSPACE
24
WORKSPACE
@ -8,8 +8,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
|||||||
# Fetch rules_nodejs so we can install our npm dependencies
|
# Fetch rules_nodejs so we can install our npm dependencies
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "build_bazel_rules_nodejs",
|
name = "build_bazel_rules_nodejs",
|
||||||
sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77",
|
sha256 = "4952ef879704ab4ad6729a29007e7094aef213ea79e9f2e94cbe1c9a753e63ef",
|
||||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"],
|
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.2.0/rules_nodejs-2.2.0.tar.gz"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check the rules_nodejs version and download npm dependencies
|
# Check the rules_nodejs version and download npm dependencies
|
||||||
@ -17,7 +17,7 @@ http_archive(
|
|||||||
# assert on that.
|
# assert on that.
|
||||||
load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install")
|
load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install")
|
||||||
|
|
||||||
check_rules_nodejs_version(minimum_version_string = "1.7.0")
|
check_rules_nodejs_version(minimum_version_string = "2.2.0")
|
||||||
|
|
||||||
# Setup the Node.js toolchain
|
# Setup the Node.js toolchain
|
||||||
node_repositories(
|
node_repositories(
|
||||||
@ -39,23 +39,18 @@ yarn_install(
|
|||||||
yarn_lock = "//:yarn.lock",
|
yarn_lock = "//:yarn.lock",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Install all bazel dependencies of the @npm npm packages
|
|
||||||
load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies")
|
|
||||||
|
|
||||||
install_bazel_dependencies()
|
|
||||||
|
|
||||||
# Load angular dependencies
|
# Load angular dependencies
|
||||||
load("//packages/bazel:package.bzl", "rules_angular_dev_dependencies")
|
load("//packages/bazel:package.bzl", "rules_angular_dev_dependencies")
|
||||||
|
|
||||||
rules_angular_dev_dependencies()
|
rules_angular_dev_dependencies()
|
||||||
|
|
||||||
# Load protractor dependencies
|
# Load protractor dependencies
|
||||||
load("@npm_bazel_protractor//:package.bzl", "npm_bazel_protractor_dependencies")
|
load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies")
|
||||||
|
|
||||||
npm_bazel_protractor_dependencies()
|
npm_bazel_protractor_dependencies()
|
||||||
|
|
||||||
# Load karma dependencies
|
# Load karma dependencies
|
||||||
load("@npm_bazel_karma//:package.bzl", "npm_bazel_karma_dependencies")
|
load("@npm//@bazel/karma:package.bzl", "npm_bazel_karma_dependencies")
|
||||||
|
|
||||||
npm_bazel_karma_dependencies()
|
npm_bazel_karma_dependencies()
|
||||||
|
|
||||||
@ -68,11 +63,6 @@ load("//dev-infra/browsers:browser_repositories.bzl", "browser_repositories")
|
|||||||
|
|
||||||
browser_repositories()
|
browser_repositories()
|
||||||
|
|
||||||
# Setup the rules_typescript tooolchain
|
|
||||||
load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace")
|
|
||||||
|
|
||||||
ts_setup_workspace()
|
|
||||||
|
|
||||||
# Setup the rules_sass toolchain
|
# Setup the rules_sass toolchain
|
||||||
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
|
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
|
||||||
|
|
||||||
@ -91,8 +81,8 @@ rbe_autoconfig(
|
|||||||
# Need to specify a base container digest in order to ensure that we can use the checked-in
|
# Need to specify a base container digest in order to ensure that we can use the checked-in
|
||||||
# platform configurations for the "ubuntu16_04" image. Otherwise the autoconfig rule would
|
# platform configurations for the "ubuntu16_04" image. Otherwise the autoconfig rule would
|
||||||
# need to pull the image and run it in order determine the toolchain configuration. See:
|
# need to pull the image and run it in order determine the toolchain configuration. See:
|
||||||
# https://github.com/bazelbuild/bazel-toolchains/blob/3.2.0/configs/ubuntu16_04_clang/versions.bzl
|
# https://github.com/bazelbuild/bazel-toolchains/blob/3.5.1/configs/ubuntu16_04_clang/versions.bzl
|
||||||
base_container_digest = "sha256:5e750dd878df9fcf4e185c6f52b9826090f6e532b097f286913a428290622332",
|
base_container_digest = "sha256:f6568d8168b14aafd1b707019927a63c2d37113a03bcee188218f99bd0327ea1",
|
||||||
# Note that if you change the `digest`, you might also need to update the
|
# Note that if you change the `digest`, you might also need to update the
|
||||||
# `base_container_digest` to make sure marketplace.gcr.io/google/rbe-ubuntu16-04-webtest:<digest>
|
# `base_container_digest` to make sure marketplace.gcr.io/google/rbe-ubuntu16-04-webtest:<digest>
|
||||||
# and marketplace.gcr.io/google/rbe-ubuntu16-04:<base_container_digest> have
|
# and marketplace.gcr.io/google/rbe-ubuntu16-04:<base_container_digest> have
|
||||||
|
@ -6,6 +6,7 @@ Everything in this folder is part of the documentation project. This includes
|
|||||||
* the dgeni configuration for converting source files to rendered files that can be viewed in the web site.
|
* the dgeni configuration for converting source files to rendered files that can be viewed in the web site.
|
||||||
* the tooling for setting up examples for development; and generating live-example and zip files from the examples.
|
* the tooling for setting up examples for development; and generating live-example and zip files from the examples.
|
||||||
|
|
||||||
|
<a name="developer-tasks"></a>
|
||||||
## Developer tasks
|
## Developer tasks
|
||||||
|
|
||||||
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
|
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
|
||||||
@ -35,8 +36,9 @@ Here are the most important tasks you might need to use:
|
|||||||
|
|
||||||
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally.
|
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally.
|
||||||
* `yarn boilerplate:add:viewengine` - same as `boilerplate:add` but also turns on `ViewEngine` (pre-Ivy) mode.
|
* `yarn boilerplate:add:viewengine` - same as `boilerplate:add` but also turns on `ViewEngine` (pre-Ivy) mode.
|
||||||
|
|
||||||
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
|
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
|
||||||
|
* `yarn create-example` - create a new example directory containing initial source files.
|
||||||
|
|
||||||
* `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
|
* `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
|
||||||
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
|
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ you don't need to specify values for those.
|
|||||||
The domain name of the server.
|
The domain name of the server.
|
||||||
|
|
||||||
- `AIO_GITHUB_ORGANIZATION`:
|
- `AIO_GITHUB_ORGANIZATION`:
|
||||||
The GitHub organization whose teams are whitelisted for accepting build artifacts.
|
The GitHub organization whose teams are trusted for accepting build artifacts.
|
||||||
See also `AIO_GITHUB_TEAM_SLUGS`.
|
See also `AIO_GITHUB_TEAM_SLUGS`.
|
||||||
|
|
||||||
- `AIO_GITHUB_REPO`:
|
- `AIO_GITHUB_REPO`:
|
||||||
|
@ -98,7 +98,7 @@ This section describes how each of the aforementioned sub-tasks is accomplished:
|
|||||||
Such a label can only have been added by a maintainer (with the necessary rights) and
|
Such a label can only have been added by a maintainer (with the necessary rights) and
|
||||||
designates that they have manually verified the PR contents.
|
designates that they have manually verified the PR contents.
|
||||||
2. We can verify (again using the GitHub API) the author's membership in one of the
|
2. We can verify (again using the GitHub API) the author's membership in one of the
|
||||||
whitelisted/trusted GitHub teams. For this operation, we need a Personal Access Token with the
|
trusted GitHub teams. For this operation, we need a Personal Access Token with the
|
||||||
`read:org` scope issued by a user that can "see" the specified GitHub organization.
|
`read:org` scope issued by a user that can "see" the specified GitHub organization.
|
||||||
Here too, we use the token by @mary-poppins.
|
Here too, we use the token by @mary-poppins.
|
||||||
|
|
||||||
|
4
aio/content/examples/.gitignore
vendored
4
aio/content/examples/.gitignore
vendored
@ -17,6 +17,7 @@
|
|||||||
**/e2e/tsconfig.e2e.json
|
**/e2e/tsconfig.e2e.json
|
||||||
**/src/karma.conf.js
|
**/src/karma.conf.js
|
||||||
**/.angular-cli.json
|
**/.angular-cli.json
|
||||||
|
**/.browserslistrc
|
||||||
**/.editorconfig
|
**/.editorconfig
|
||||||
**/.gitignore
|
**/.gitignore
|
||||||
**/angular.json
|
**/angular.json
|
||||||
@ -30,8 +31,9 @@
|
|||||||
**/tslint.json
|
**/tslint.json
|
||||||
**/karma-test-shim.js
|
**/karma-test-shim.js
|
||||||
**/browser-test-shim.js
|
**/browser-test-shim.js
|
||||||
**/browserslist
|
|
||||||
**/node_modules
|
**/node_modules
|
||||||
|
**/yarn.lock
|
||||||
|
**/package-lock.json
|
||||||
|
|
||||||
# built files
|
# built files
|
||||||
*.map
|
*.map
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
<h2>Attribute binding</h2>
|
<h2>Attribute binding</h2>
|
||||||
<!-- #docregion attrib-binding-colspan -->
|
<!-- #docregion attrib-binding-colspan -->
|
||||||
<table border=1>
|
<table border=1>
|
||||||
|
<!-- #docregion colspan -->
|
||||||
<!-- expression calculates colspan=2 -->
|
<!-- expression calculates colspan=2 -->
|
||||||
<tr><td [attr.colspan]="1 + 1">One-Two</td></tr>
|
<tr><td [attr.colspan]="1 + 1">One-Two</td></tr>
|
||||||
|
<!-- #enddocregion colspan -->
|
||||||
|
|
||||||
<!-- ERROR: There is no `colspan` property to set!
|
<!-- ERROR: There is no `colspan` property to set!
|
||||||
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
|
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
|
||||||
@ -32,31 +34,31 @@
|
|||||||
<!-- #docregion basic-specificity -->
|
<!-- #docregion basic-specificity -->
|
||||||
<h3>Basic specificity</h3>
|
<h3>Basic specificity</h3>
|
||||||
|
|
||||||
<!-- The `class.special` binding will override any value for the `special` class in `classExpr`. -->
|
<!-- The `class.special` binding overrides any value for the `special` class in `classExpression`. -->
|
||||||
<div [class.special]="isSpecial" [class]="classExpr">Some text.</div>
|
<div [class.special]="isSpecial" [class]="classExpression">Some text.</div>
|
||||||
|
|
||||||
<!-- The `style.color` binding will override any value for the `color` property in `styleExpr`. -->
|
<!-- The `style.color` binding overrides any value for the `color` property in `styleExpression`. -->
|
||||||
<div [style.color]="color" [style]="styleExpr">Some text.</div>
|
<div [style.color]="color" [style]="styleExpression">Some text.</div>
|
||||||
<!-- #enddocregion basic-specificity -->
|
<!-- #enddocregion basic-specificity -->
|
||||||
|
|
||||||
<!-- #docregion source-specificity -->
|
<!-- #docregion source-specificity -->
|
||||||
<h3>Source specificity</h3>
|
<h3>Source specificity</h3>
|
||||||
|
|
||||||
<!-- The `class.special` template binding will override any host binding to the `special` class set by `dirWithClassBinding` or `comp-with-host-binding`.-->
|
<!-- The `class.special` template binding overrides any host binding to the `special` class set by `dirWithClassBinding` or `comp-with-host-binding`.-->
|
||||||
<comp-with-host-binding [class.special]="isSpecial" dirWithClassBinding>Some text.</comp-with-host-binding>
|
<comp-with-host-binding [class.special]="isSpecial" dirWithClassBinding>Some text.</comp-with-host-binding>
|
||||||
|
|
||||||
<!-- The `style.color` template binding will override any host binding to the `color` property set by `dirWithStyleBinding` or `comp-with-host-binding`. -->
|
<!-- The `style.color` template binding overrides any host binding to the `color` property set by `dirWithStyleBinding` or `comp-with-host-binding`. -->
|
||||||
<comp-with-host-binding [style.color]="color" dirWithStyleBinding>Some text.</comp-with-host-binding>
|
<comp-with-host-binding [style.color]="color" dirWithStyleBinding>Some text.</comp-with-host-binding>
|
||||||
<!-- #enddocregion source-specificity -->
|
<!-- #enddocregion source-specificity -->
|
||||||
|
|
||||||
<!-- #docregion dynamic-priority -->
|
<!-- #docregion dynamic-priority -->
|
||||||
<h3>Dynamic vs static</h3>
|
<h3>Dynamic vs static</h3>
|
||||||
|
|
||||||
<!-- If `classExpr` has a value for the `special` class, this value will override the `class="special"` below -->
|
<!-- If `classExpression` has a value for the `special` class, this value overrides the `class="special"` below -->
|
||||||
<div class="special" [class]="classExpr">Some text.</div>
|
<div class="special" [class]="classExpression">Some text.</div>
|
||||||
|
|
||||||
<!-- If `styleExpr` has a value for the `color` property, this value will override the `style="color: blue"` below -->
|
<!-- If `styleExpression` has a value for the `color` property, this value overrides the `style="color: blue"` below -->
|
||||||
<div style="color: blue" [style]="styleExpr">Some text.</div>
|
<div style="color: blue" [style]="styleExpression">Some text.</div>
|
||||||
|
|
||||||
<!-- #enddocregion dynamic-priority -->
|
<!-- #enddocregion dynamic-priority -->
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ export class AppComponent {
|
|||||||
actionName = 'Go for it';
|
actionName = 'Go for it';
|
||||||
isSpecial = true;
|
isSpecial = true;
|
||||||
canSave = true;
|
canSave = true;
|
||||||
classExpr = 'special clearance';
|
classExpression = 'special clearance';
|
||||||
styleExpr = 'color: red';
|
styleExpression = 'color: red';
|
||||||
color = 'blue';
|
color = 'blue';
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,9 @@ export class CompWithHostBindingComponent {
|
|||||||
@HostBinding('style.color')
|
@HostBinding('style.color')
|
||||||
color = 'green';
|
color = 'green';
|
||||||
|
|
||||||
|
// #docregion hostbinding
|
||||||
@HostBinding('style.width')
|
@HostBinding('style.width')
|
||||||
width = '200px';
|
width = '200px';
|
||||||
|
// #enddocregion hostbinding
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,11 +21,13 @@ import { ItemDirective } from './item.directive';
|
|||||||
ItemDirective
|
ItemDirective
|
||||||
],
|
],
|
||||||
// #enddocregion declarations
|
// #enddocregion declarations
|
||||||
|
// #docregion imports
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
HttpClientModule
|
HttpClientModule
|
||||||
],
|
],
|
||||||
|
// #enddocregion imports
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
import { browser, element, by } from 'protractor';
|
import { browser, by, element } from 'protractor';
|
||||||
|
|
||||||
describe('Component Communication Cookbook Tests', () => {
|
describe('Component Communication Cookbook Tests', () => {
|
||||||
|
|
||||||
// Note: '?e2e' which app can read to know it is running in protractor
|
beforeEach(() => browser.get(browser.baseUrl));
|
||||||
// e.g. `if (!/e2e/.test(location.search)) { ...`
|
|
||||||
beforeAll(() => {
|
|
||||||
browser.get('?e2e');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Parent-to-child communication', () => {
|
describe('Parent-to-child communication', () => {
|
||||||
// #docregion parent-to-child
|
// #docregion parent-to-child
|
||||||
@ -15,7 +11,7 @@ describe('Component Communication Cookbook Tests', () => {
|
|||||||
const masterName = 'Master';
|
const masterName = 'Master';
|
||||||
|
|
||||||
it('should pass properties to children properly', () => {
|
it('should pass properties to children properly', () => {
|
||||||
const parent = element.all(by.tagName('app-hero-parent')).get(0);
|
const parent = element(by.tagName('app-hero-parent'));
|
||||||
const heroes = parent.all(by.tagName('app-hero-child'));
|
const heroes = parent.all(by.tagName('app-hero-child'));
|
||||||
|
|
||||||
for (let i = 0; i < heroNames.length; i++) {
|
for (let i = 0; i < heroNames.length; i++) {
|
||||||
@ -35,7 +31,7 @@ describe('Component Communication Cookbook Tests', () => {
|
|||||||
it('should display trimmed, non-empty names', () => {
|
it('should display trimmed, non-empty names', () => {
|
||||||
const nonEmptyNameIndex = 0;
|
const nonEmptyNameIndex = 0;
|
||||||
const nonEmptyName = '"Dr IQ"';
|
const nonEmptyName = '"Dr IQ"';
|
||||||
const parent = element.all(by.tagName('app-name-parent')).get(0);
|
const parent = element(by.tagName('app-name-parent'));
|
||||||
const hero = parent.all(by.tagName('app-name-child')).get(nonEmptyNameIndex);
|
const hero = parent.all(by.tagName('app-name-child')).get(nonEmptyNameIndex);
|
||||||
|
|
||||||
const displayName = hero.element(by.tagName('h3')).getText();
|
const displayName = hero.element(by.tagName('h3')).getText();
|
||||||
@ -45,7 +41,7 @@ describe('Component Communication Cookbook Tests', () => {
|
|||||||
it('should replace empty name with default name', () => {
|
it('should replace empty name with default name', () => {
|
||||||
const emptyNameIndex = 1;
|
const emptyNameIndex = 1;
|
||||||
const defaultName = '"<no name set>"';
|
const defaultName = '"<no name set>"';
|
||||||
const parent = element.all(by.tagName('app-name-parent')).get(0);
|
const parent = element(by.tagName('app-name-parent'));
|
||||||
const hero = parent.all(by.tagName('app-name-child')).get(emptyNameIndex);
|
const hero = parent.all(by.tagName('app-name-child')).get(emptyNameIndex);
|
||||||
|
|
||||||
const displayName = hero.element(by.tagName('h3')).getText();
|
const displayName = hero.element(by.tagName('h3')).getText();
|
||||||
@ -70,38 +66,36 @@ describe('Component Communication Cookbook Tests', () => {
|
|||||||
expect(actual.logs.get(0).getText()).toBe(initialLog);
|
expect(actual.logs.get(0).getText()).toBe(initialLog);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set expected values after clicking \'Minor\' twice', () => {
|
it('should set expected values after clicking \'Minor\' twice', async () => {
|
||||||
const repoTag = element(by.tagName('app-version-parent'));
|
const repoTag = element(by.tagName('app-version-parent'));
|
||||||
const newMinorButton = repoTag.all(by.tagName('button')).get(0);
|
const newMinorButton = repoTag.all(by.tagName('button')).get(0);
|
||||||
|
|
||||||
newMinorButton.click().then(() => {
|
await newMinorButton.click();
|
||||||
newMinorButton.click().then(() => {
|
await newMinorButton.click();
|
||||||
const actual = getActual();
|
|
||||||
|
|
||||||
const labelAfter2Minor = 'Version 1.25';
|
const actual = getActual();
|
||||||
const logAfter2Minor = 'minor changed from 24 to 25';
|
|
||||||
|
|
||||||
expect(actual.label).toBe(labelAfter2Minor);
|
const labelAfter2Minor = 'Version 1.25';
|
||||||
expect(actual.count).toBe(3);
|
const logAfter2Minor = 'minor changed from 24 to 25';
|
||||||
expect(actual.logs.get(2).getText()).toBe(logAfter2Minor);
|
|
||||||
});
|
expect(actual.label).toBe(labelAfter2Minor);
|
||||||
});
|
expect(actual.count).toBe(3);
|
||||||
|
expect(actual.logs.get(2).getText()).toBe(logAfter2Minor);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set expected values after clicking \'Major\' once', () => {
|
it('should set expected values after clicking \'Major\' once', async () => {
|
||||||
const repoTag = element(by.tagName('app-version-parent'));
|
const repoTag = element(by.tagName('app-version-parent'));
|
||||||
const newMajorButton = repoTag.all(by.tagName('button')).get(1);
|
const newMajorButton = repoTag.all(by.tagName('button')).get(1);
|
||||||
|
|
||||||
newMajorButton.click().then(() => {
|
await newMajorButton.click();
|
||||||
const actual = getActual();
|
const actual = getActual();
|
||||||
|
|
||||||
const labelAfterMajor = 'Version 2.0';
|
const labelAfterMajor = 'Version 2.0';
|
||||||
const logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
|
const logAfterMajor = 'major changed from 1 to 2, minor changed from 23 to 0';
|
||||||
|
|
||||||
expect(actual.label).toBe(labelAfterMajor);
|
expect(actual.label).toBe(labelAfterMajor);
|
||||||
expect(actual.count).toBe(4);
|
expect(actual.count).toBe(2);
|
||||||
expect(actual.logs.get(3).getText()).toBe(logAfterMajor);
|
expect(actual.logs.get(1).getText()).toBe(logAfterMajor);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function getActual() {
|
function getActual() {
|
||||||
@ -118,110 +112,125 @@ describe('Component Communication Cookbook Tests', () => {
|
|||||||
}
|
}
|
||||||
// ...
|
// ...
|
||||||
// #enddocregion parent-to-child-onchanges
|
// #enddocregion parent-to-child-onchanges
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Child-to-parent communication', () => {
|
describe('Child-to-parent communication', () => {
|
||||||
// #docregion child-to-parent
|
// #docregion child-to-parent
|
||||||
// ...
|
// ...
|
||||||
it('should not emit the event initially', () => {
|
it('should not emit the event initially', () => {
|
||||||
const voteLabel = element(by.tagName('app-vote-taker'))
|
const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
|
||||||
.element(by.tagName('h3')).getText();
|
expect(voteLabel.getText()).toBe('Agree: 0, Disagree: 0');
|
||||||
expect(voteLabel).toBe('Agree: 0, Disagree: 0');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should process Agree vote', () => {
|
it('should process Agree vote', async () => {
|
||||||
|
const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
|
||||||
const agreeButton1 = element.all(by.tagName('app-voter')).get(0)
|
const agreeButton1 = element.all(by.tagName('app-voter')).get(0)
|
||||||
.all(by.tagName('button')).get(0);
|
.all(by.tagName('button')).get(0);
|
||||||
agreeButton1.click().then(() => {
|
|
||||||
const voteLabel = element(by.tagName('app-vote-taker'))
|
await agreeButton1.click();
|
||||||
.element(by.tagName('h3')).getText();
|
|
||||||
expect(voteLabel).toBe('Agree: 1, Disagree: 0');
|
expect(voteLabel.getText()).toBe('Agree: 1, Disagree: 0');
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should process Disagree vote', () => {
|
it('should process Disagree vote', async () => {
|
||||||
|
const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
|
||||||
const agreeButton1 = element.all(by.tagName('app-voter')).get(1)
|
const agreeButton1 = element.all(by.tagName('app-voter')).get(1)
|
||||||
.all(by.tagName('button')).get(1);
|
.all(by.tagName('button')).get(1);
|
||||||
agreeButton1.click().then(() => {
|
|
||||||
const voteLabel = element(by.tagName('app-vote-taker'))
|
await agreeButton1.click();
|
||||||
.element(by.tagName('h3')).getText();
|
|
||||||
expect(voteLabel).toBe('Agree: 1, Disagree: 1');
|
expect(voteLabel.getText()).toBe('Agree: 0, Disagree: 1');
|
||||||
});
|
|
||||||
});
|
});
|
||||||
// ...
|
// ...
|
||||||
// #enddocregion child-to-parent
|
// #enddocregion child-to-parent
|
||||||
});
|
});
|
||||||
|
|
||||||
// Can't run timer tests in protractor because
|
describe('Parent calls child via local var', () => {
|
||||||
// interaction w/ zones causes all tests to freeze & timeout.
|
countDownTimerTests('app-countdown-parent-lv');
|
||||||
xdescribe('Parent calls child via local var', () => {
|
|
||||||
countDownTimerTests('countdown-parent-lv');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
xdescribe('Parent calls ViewChild', () => {
|
describe('Parent calls ViewChild', () => {
|
||||||
countDownTimerTests('countdown-parent-vc');
|
countDownTimerTests('app-countdown-parent-vc');
|
||||||
});
|
});
|
||||||
|
|
||||||
function countDownTimerTests(parentTag: string) {
|
function countDownTimerTests(parentTag: string) {
|
||||||
// #docregion countdown-timer-tests
|
// #docregion countdown-timer-tests
|
||||||
// ...
|
// ...
|
||||||
it('timer and parent seconds should match', () => {
|
// The tests trigger periodic asynchronous operations (via `setInterval()`), which will prevent
|
||||||
|
// the app from stabilizing. See https://angular.io/api/core/ApplicationRef#is-stable-examples
|
||||||
|
// for more details.
|
||||||
|
// To allow the tests to complete, we will disable automatically waiting for the Angular app to
|
||||||
|
// stabilize.
|
||||||
|
beforeEach(() => browser.waitForAngularEnabled(false));
|
||||||
|
afterEach(() => browser.waitForAngularEnabled(true));
|
||||||
|
|
||||||
|
it('timer and parent seconds should match', async () => {
|
||||||
const parent = element(by.tagName(parentTag));
|
const parent = element(by.tagName(parentTag));
|
||||||
const message = parent.element(by.tagName('app-countdown-timer')).getText();
|
const startButton = parent.element(by.buttonText('Start'));
|
||||||
browser.sleep(10); // give `seconds` a chance to catchup with `message`
|
const seconds = parent.element(by.className('seconds'));
|
||||||
const seconds = parent.element(by.className('seconds')).getText();
|
const timer = parent.element(by.tagName('app-countdown-timer'));
|
||||||
expect(message).toContain(seconds);
|
|
||||||
|
await startButton.click();
|
||||||
|
|
||||||
|
// Wait for `<app-countdown-timer>` to be populated with any text.
|
||||||
|
await browser.wait(() => timer.getText(), 2000);
|
||||||
|
|
||||||
|
expect(await timer.getText()).toContain(await seconds.getText());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should stop the countdown', () => {
|
it('should stop the countdown', async () => {
|
||||||
const parent = element(by.tagName(parentTag));
|
const parent = element(by.tagName(parentTag));
|
||||||
const stopButton = parent.all(by.tagName('button')).get(1);
|
const startButton = parent.element(by.buttonText('Start'));
|
||||||
|
const stopButton = parent.element(by.buttonText('Stop'));
|
||||||
|
const timer = parent.element(by.tagName('app-countdown-timer'));
|
||||||
|
|
||||||
stopButton.click().then(() => {
|
await startButton.click();
|
||||||
const message = parent.element(by.tagName('app-countdown-timer')).getText();
|
expect(await timer.getText()).not.toContain('Holding');
|
||||||
expect(message).toContain('Holding');
|
|
||||||
});
|
await stopButton.click();
|
||||||
|
expect(await timer.getText()).toContain('Holding');
|
||||||
});
|
});
|
||||||
// ...
|
// ...
|
||||||
// #enddocregion countdown-timer-tests
|
// #enddocregion countdown-timer-tests
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
describe('Parent and children communicate via a service', () => {
|
describe('Parent and children communicate via a service', () => {
|
||||||
// #docregion bidirectional-service
|
// #docregion bidirectional-service
|
||||||
// ...
|
// ...
|
||||||
it('should announce a mission', () => {
|
it('should announce a mission', async () => {
|
||||||
const missionControl = element(by.tagName('app-mission-control'));
|
const missionControl = element(by.tagName('app-mission-control'));
|
||||||
const announceButton = missionControl.all(by.tagName('button')).get(0);
|
const announceButton = missionControl.all(by.tagName('button')).get(0);
|
||||||
announceButton.click().then(() => {
|
const history = missionControl.all(by.tagName('li'));
|
||||||
const history = missionControl.all(by.tagName('li'));
|
|
||||||
expect(history.count()).toBe(1);
|
await announceButton.click();
|
||||||
expect(history.get(0).getText()).toMatch(/Mission.* announced/);
|
|
||||||
});
|
expect(history.count()).toBe(1);
|
||||||
|
expect(history.get(0).getText()).toMatch(/Mission.* announced/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm the mission by Lovell', () => {
|
it('should confirm the mission by Lovell', async () => {
|
||||||
testConfirmMission(1, 2, 'Lovell');
|
await testConfirmMission(1, 'Lovell');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm the mission by Haise', () => {
|
it('should confirm the mission by Haise', async () => {
|
||||||
testConfirmMission(3, 3, 'Haise');
|
await testConfirmMission(3, 'Haise');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm the mission by Swigert', () => {
|
it('should confirm the mission by Swigert', async () => {
|
||||||
testConfirmMission(2, 4, 'Swigert');
|
await testConfirmMission(2, 'Swigert');
|
||||||
});
|
});
|
||||||
|
|
||||||
function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
|
async function testConfirmMission(buttonIndex: number, astronaut: string) {
|
||||||
const confirmedLog = ' confirmed the mission';
|
|
||||||
const missionControl = element(by.tagName('app-mission-control'));
|
const missionControl = element(by.tagName('app-mission-control'));
|
||||||
|
const announceButton = missionControl.all(by.tagName('button')).get(0);
|
||||||
const confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
|
const confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
|
||||||
confirmButton.click().then(() => {
|
const history = missionControl.all(by.tagName('li'));
|
||||||
const history = missionControl.all(by.tagName('li'));
|
|
||||||
expect(history.count()).toBe(expectedLogCount);
|
await announceButton.click();
|
||||||
expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + confirmedLog);
|
await confirmButton.click();
|
||||||
});
|
|
||||||
|
expect(history.count()).toBe(2);
|
||||||
|
expect(history.get(1).getText()).toBe(`${astronaut} confirmed the mission`);
|
||||||
}
|
}
|
||||||
// ...
|
// ...
|
||||||
// #enddocregion bidirectional-service
|
// #enddocregion bidirectional-service
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"tests": [
|
|
||||||
{
|
|
||||||
"cmd": "yarn",
|
|
||||||
"args": [
|
|
||||||
"e2e",
|
|
||||||
"--protractor-config=e2e/protractor-puppeteer.conf.js",
|
|
||||||
"--no-webdriver-update",
|
|
||||||
"--port={PORT}"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
@ -30,22 +30,21 @@
|
|||||||
<app-vote-taker></app-vote-taker>
|
<app-vote-taker></app-vote-taker>
|
||||||
</div>
|
</div>
|
||||||
<a href="#top" class="to-top">Back to Top</a>
|
<a href="#top" class="to-top">Back to Top</a>
|
||||||
<hr>
|
|
||||||
|
|
||||||
|
<hr>
|
||||||
<div id="parent-to-child-local-var">
|
<div id="parent-to-child-local-var">
|
||||||
<app-countdown-parent-lv></app-countdown-parent-lv>
|
<app-countdown-parent-lv></app-countdown-parent-lv>
|
||||||
</div>
|
</div>
|
||||||
<a href="#top" class="to-top">Back to Top</a>
|
<a href="#top" class="to-top">Back to Top</a>
|
||||||
<hr>
|
|
||||||
|
|
||||||
|
<hr>
|
||||||
<div id="parent-to-view-child">
|
<div id="parent-to-view-child">
|
||||||
<app-countdown-parent-vc></app-countdown-parent-vc>
|
<app-countdown-parent-vc></app-countdown-parent-vc>
|
||||||
</div>
|
</div>
|
||||||
<a href="#top" class="to-top">Back to Top</a>
|
<a href="#top" class="to-top">Back to Top</a>
|
||||||
<hr>
|
|
||||||
|
|
||||||
|
<hr>
|
||||||
<div id="bidirectional-service">
|
<div id="bidirectional-service">
|
||||||
<app-mission-control></app-mission-control>
|
<app-mission-control></app-mission-control>
|
||||||
</div>
|
</div>
|
||||||
<a href="#top" class="to-top">Back to Top</a>
|
<a href="#top" class="to-top">Back to Top</a>
|
||||||
<hr>
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
@ -15,10 +15,17 @@ import { VersionParentComponent } from './version-parent.component';
|
|||||||
import { VoterComponent } from './voter.component';
|
import { VoterComponent } from './voter.component';
|
||||||
import { VoteTakerComponent } from './votetaker.component';
|
import { VoteTakerComponent } from './votetaker.component';
|
||||||
|
|
||||||
const directives: any[] = [
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
AstronautComponent,
|
AstronautComponent,
|
||||||
|
CountdownLocalVarParentComponent,
|
||||||
CountdownTimerComponent,
|
CountdownTimerComponent,
|
||||||
|
CountdownViewChildParentComponent,
|
||||||
HeroChildComponent,
|
HeroChildComponent,
|
||||||
HeroParentComponent,
|
HeroParentComponent,
|
||||||
MissionControlComponent,
|
MissionControlComponent,
|
||||||
@ -27,28 +34,8 @@ const directives: any[] = [
|
|||||||
VersionChildComponent,
|
VersionChildComponent,
|
||||||
VersionParentComponent,
|
VersionParentComponent,
|
||||||
VoterComponent,
|
VoterComponent,
|
||||||
VoteTakerComponent
|
VoteTakerComponent,
|
||||||
];
|
|
||||||
|
|
||||||
const schemas: any[] = [];
|
|
||||||
|
|
||||||
// Include Countdown examples
|
|
||||||
// unless in e2e tests which they break.
|
|
||||||
if (!/e2e/.test(location.search)) {
|
|
||||||
console.log('adding countdown timer examples');
|
|
||||||
directives.push(CountdownLocalVarParentComponent);
|
|
||||||
directives.push(CountdownViewChildParentComponent);
|
|
||||||
} else {
|
|
||||||
// In e2e test use CUSTOM_ELEMENTS_SCHEMA to suppress unknown element errors
|
|
||||||
schemas.push(CUSTOM_ELEMENTS_SCHEMA);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
BrowserModule
|
|
||||||
],
|
],
|
||||||
declarations: directives,
|
|
||||||
bootstrap: [ AppComponent ],
|
bootstrap: [ AppComponent ],
|
||||||
schemas
|
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-countdown-timer',
|
selector: 'app-countdown-timer',
|
||||||
template: '<p>{{message}}</p>'
|
template: '<p>{{message}}</p>'
|
||||||
})
|
})
|
||||||
export class CountdownTimerComponent implements OnInit, OnDestroy {
|
export class CountdownTimerComponent implements OnDestroy {
|
||||||
|
|
||||||
intervalId = 0;
|
intervalId = 0;
|
||||||
message = '';
|
message = '';
|
||||||
seconds = 11;
|
seconds = 11;
|
||||||
|
|
||||||
clearTimer() { clearInterval(this.intervalId); }
|
|
||||||
|
|
||||||
ngOnInit() { this.start(); }
|
|
||||||
ngOnDestroy() { this.clearTimer(); }
|
ngOnDestroy() { this.clearTimer(); }
|
||||||
|
|
||||||
start() { this.countDown(); }
|
start() { this.countDown(); }
|
||||||
@ -22,6 +19,8 @@ export class CountdownTimerComponent implements OnInit, OnDestroy {
|
|||||||
this.message = `Holding at T-${this.seconds} seconds`;
|
this.message = `Holding at T-${this.seconds} seconds`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private clearTimer() { clearInterval(this.intervalId); }
|
||||||
|
|
||||||
private countDown() {
|
private countDown() {
|
||||||
this.clearTimer();
|
this.clearTimer();
|
||||||
this.intervalId = window.setInterval(() => {
|
this.intervalId = window.setInterval(() => {
|
||||||
|
@ -13,8 +13,8 @@ import { Component, ViewEncapsulation } from '@angular/core';
|
|||||||
export class QuestSummaryComponent { }
|
export class QuestSummaryComponent { }
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
/*
|
/*
|
||||||
// #docregion encapsulation.native
|
// #docregion encapsulation.shadow
|
||||||
// warning: few browsers support shadow DOM encapsulation at this time
|
// warning: few browsers support shadow DOM encapsulation at this time
|
||||||
encapsulation: ViewEncapsulation.Native
|
encapsulation: ViewEncapsulation.ShadowDom
|
||||||
// #enddocregion encapsulation.native
|
// #enddocregion encapsulation.shadow
|
||||||
*/
|
*/
|
||||||
|
@ -24,7 +24,7 @@ export class UploaderService {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
upload(file: File) {
|
upload(file: File) {
|
||||||
if (!file) { return; }
|
if (!file) { return of<string>(); }
|
||||||
|
|
||||||
// COULD HAVE WRITTEN:
|
// COULD HAVE WRITTEN:
|
||||||
// return this.http.post('/upload/file', file, {
|
// return this.http.post('/upload/file', file, {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// TODO: Add unit tests for this file.
|
||||||
// tslint:disable: no-output-native
|
// tslint:disable: no-output-native
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core';
|
import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core';
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
"tests": [
|
"tests": [
|
||||||
{
|
{
|
||||||
"cmd": "yarn",
|
"cmd": "yarn",
|
||||||
"args": [ "tsc", "--project", "./tsconfig.app.json" ]
|
"args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": ["jasmine", "out-tsc/**/*.spec.js"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
55
aio/content/examples/observables/src/creating.spec.ts
Normal file
55
aio/content/examples/observables/src/creating.spec.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { docRegionFromEvent, docRegionSubscriber } from './creating';
|
||||||
|
|
||||||
|
describe('observables', () => {
|
||||||
|
it('should create an observable using the constructor', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
docRegionSubscriber(console);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(4);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
[1],
|
||||||
|
[2],
|
||||||
|
[3],
|
||||||
|
['Finished sequence'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should listen to input changes', () => {
|
||||||
|
let triggerInputChange;
|
||||||
|
const input = {
|
||||||
|
value: 'Test',
|
||||||
|
addEventListener: jasmine
|
||||||
|
.createSpy('addEvent')
|
||||||
|
.and.callFake((eventName: string, cb: (e) => void) => {
|
||||||
|
if (eventName === 'keydown') {
|
||||||
|
triggerInputChange = cb;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
removeEventListener: jasmine.createSpy('removeEventListener'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const document = { getElementById: () => input };
|
||||||
|
docRegionFromEvent(document);
|
||||||
|
triggerInputChange({keyCode: 65});
|
||||||
|
expect(input.value).toBe('Test');
|
||||||
|
|
||||||
|
triggerInputChange({keyCode: 27});
|
||||||
|
expect(input.value).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call removeEventListener when unsubscribing', (doneFn: DoneFn) => {
|
||||||
|
const input = {
|
||||||
|
addEventListener: jasmine.createSpy('addEvent'),
|
||||||
|
removeEventListener: jasmine
|
||||||
|
.createSpy('removeEvent')
|
||||||
|
.and.callFake((eventName: string, cb: (e) => void) => {
|
||||||
|
if (eventName === 'keydown') {
|
||||||
|
doneFn();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const document = { getElementById: () => input };
|
||||||
|
const subscription = docRegionFromEvent(document);
|
||||||
|
subscription.unsubscribe();
|
||||||
|
});
|
||||||
|
});
|
@ -1,38 +1,39 @@
|
|||||||
|
// #docplaster
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
// #docregion subscriber
|
export function docRegionSubscriber(console) {
|
||||||
|
// #docregion subscriber
|
||||||
|
// This function runs when subscribe() is called
|
||||||
|
function sequenceSubscriber(observer) {
|
||||||
|
// synchronously deliver 1, 2, and 3, then complete
|
||||||
|
observer.next(1);
|
||||||
|
observer.next(2);
|
||||||
|
observer.next(3);
|
||||||
|
observer.complete();
|
||||||
|
|
||||||
// This function runs when subscribe() is called
|
// unsubscribe function doesn't need to do anything in this
|
||||||
function sequenceSubscriber(observer) {
|
// because values are delivered synchronously
|
||||||
// synchronously deliver 1, 2, and 3, then complete
|
return {unsubscribe() {}};
|
||||||
observer.next(1);
|
}
|
||||||
observer.next(2);
|
|
||||||
observer.next(3);
|
|
||||||
observer.complete();
|
|
||||||
|
|
||||||
// unsubscribe function doesn't need to do anything in this
|
// Create a new Observable that will deliver the above sequence
|
||||||
// because values are delivered synchronously
|
const sequence = new Observable(sequenceSubscriber);
|
||||||
return {unsubscribe() {}};
|
|
||||||
|
// execute the Observable and print the result of each notification
|
||||||
|
sequence.subscribe({
|
||||||
|
next(num) { console.log(num); },
|
||||||
|
complete() { console.log('Finished sequence'); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Logs:
|
||||||
|
// 1
|
||||||
|
// 2
|
||||||
|
// 3
|
||||||
|
// Finished sequence
|
||||||
|
// #enddocregion subscriber
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new Observable that will deliver the above sequence
|
|
||||||
const sequence = new Observable(sequenceSubscriber);
|
|
||||||
|
|
||||||
// execute the Observable and print the result of each notification
|
|
||||||
sequence.subscribe({
|
|
||||||
next(num) { console.log(num); },
|
|
||||||
complete() { console.log('Finished sequence'); }
|
|
||||||
});
|
|
||||||
|
|
||||||
// Logs:
|
|
||||||
// 1
|
|
||||||
// 2
|
|
||||||
// 3
|
|
||||||
// Finished sequence
|
|
||||||
|
|
||||||
// #enddocregion subscriber
|
|
||||||
|
|
||||||
// #docregion fromevent
|
// #docregion fromevent
|
||||||
|
|
||||||
function fromEvent(target, eventName) {
|
function fromEvent(target, eventName) {
|
||||||
@ -51,16 +52,18 @@ function fromEvent(target, eventName) {
|
|||||||
|
|
||||||
// #enddocregion fromevent
|
// #enddocregion fromevent
|
||||||
|
|
||||||
// #docregion fromevent_use
|
export function docRegionFromEvent(document) {
|
||||||
|
// #docregion fromevent_use
|
||||||
|
|
||||||
const ESC_KEY = 27;
|
const ESC_KEY = 27;
|
||||||
const nameInput = document.getElementById('name') as HTMLInputElement;
|
const nameInput = document.getElementById('name') as HTMLInputElement;
|
||||||
|
|
||||||
const subscription = fromEvent(nameInput, 'keydown')
|
const subscription = fromEvent(nameInput, 'keydown').subscribe((e: KeyboardEvent) => {
|
||||||
.subscribe((e: KeyboardEvent) => {
|
|
||||||
if (e.keyCode === ESC_KEY) {
|
if (e.keyCode === ESC_KEY) {
|
||||||
nameInput.value = '';
|
nameInput.value = '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// #enddocregion fromevent_use
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
// #enddocregion fromevent_use
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
// TODO: Add unit tests for this file.
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
// Create an Observable that will start listening to geolocation updates
|
// Create an Observable that will start listening to geolocation updates
|
||||||
|
48
aio/content/examples/observables/src/multicasting.spec.ts
Normal file
48
aio/content/examples/observables/src/multicasting.spec.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { docRegionDelaySequence, docRegionMulticastSequence } from './multicasting';
|
||||||
|
|
||||||
|
describe('multicasting', () => {
|
||||||
|
let console;
|
||||||
|
beforeEach(() => {
|
||||||
|
jasmine.clock().install();
|
||||||
|
console = {log: jasmine.createSpy('log')};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jasmine.clock().uninstall();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an observable and emit in sequence', () => {
|
||||||
|
docRegionDelaySequence(console);
|
||||||
|
jasmine.clock().tick(10000);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(12);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
[1],
|
||||||
|
['1st subscribe: 1'],
|
||||||
|
['2nd subscribe: 1'],
|
||||||
|
[2],
|
||||||
|
['1st subscribe: 2'],
|
||||||
|
['2nd subscribe: 2'],
|
||||||
|
[3],
|
||||||
|
['Finished sequence'],
|
||||||
|
['1st subscribe: 3'],
|
||||||
|
['1st sequence finished.'],
|
||||||
|
['2nd subscribe: 3'],
|
||||||
|
['2nd sequence finished.']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an observable and multicast the emissions', () => {
|
||||||
|
docRegionMulticastSequence(console);
|
||||||
|
jasmine.clock().tick(10000);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(7);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
['1st subscribe: 1'],
|
||||||
|
['1st subscribe: 2'],
|
||||||
|
['2nd subscribe: 2'],
|
||||||
|
['1st subscribe: 3'],
|
||||||
|
['2nd subscribe: 3'],
|
||||||
|
['1st sequence finished.'],
|
||||||
|
['2nd sequence finished.']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,155 +1,160 @@
|
|||||||
|
// #docplaster
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
// #docregion delay_sequence
|
export function docRegionDelaySequence(console) {
|
||||||
|
// #docregion delay_sequence
|
||||||
|
function sequenceSubscriber(observer) {
|
||||||
|
const seq = [1, 2, 3];
|
||||||
|
let timeoutId;
|
||||||
|
|
||||||
function sequenceSubscriber(observer) {
|
// Will run through an array of numbers, emitting one value
|
||||||
const seq = [1, 2, 3];
|
// per second until it gets to the end of the array.
|
||||||
let timeoutId;
|
function doInSequence(arr, idx) {
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
observer.next(arr[idx]);
|
||||||
|
if (idx === arr.length - 1) {
|
||||||
|
observer.complete();
|
||||||
|
} else {
|
||||||
|
doInSequence(arr, ++idx);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
// Will run through an array of numbers, emitting one value
|
doInSequence(seq, 0);
|
||||||
|
|
||||||
|
// Unsubscribe should clear the timeout to stop execution
|
||||||
|
return {
|
||||||
|
unsubscribe() {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new Observable that will deliver the above sequence
|
||||||
|
const sequence = new Observable(sequenceSubscriber);
|
||||||
|
|
||||||
|
sequence.subscribe({
|
||||||
|
next(num) { console.log(num); },
|
||||||
|
complete() { console.log('Finished sequence'); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Logs:
|
||||||
|
// (at 1 second): 1
|
||||||
|
// (at 2 seconds): 2
|
||||||
|
// (at 3 seconds): 3
|
||||||
|
// (at 3 seconds): Finished sequence
|
||||||
|
|
||||||
|
// #enddocregion delay_sequence
|
||||||
|
|
||||||
|
// #docregion subscribe_twice
|
||||||
|
|
||||||
|
// Subscribe starts the clock, and will emit after 1 second
|
||||||
|
sequence.subscribe({
|
||||||
|
next(num) { console.log('1st subscribe: ' + num); },
|
||||||
|
complete() { console.log('1st sequence finished.'); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// After 1/2 second, subscribe again.
|
||||||
|
setTimeout(() => {
|
||||||
|
sequence.subscribe({
|
||||||
|
next(num) { console.log('2nd subscribe: ' + num); },
|
||||||
|
complete() { console.log('2nd sequence finished.'); }
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Logs:
|
||||||
|
// (at 1 second): 1st subscribe: 1
|
||||||
|
// (at 1.5 seconds): 2nd subscribe: 1
|
||||||
|
// (at 2 seconds): 1st subscribe: 2
|
||||||
|
// (at 2.5 seconds): 2nd subscribe: 2
|
||||||
|
// (at 3 seconds): 1st subscribe: 3
|
||||||
|
// (at 3 seconds): 1st sequence finished
|
||||||
|
// (at 3.5 seconds): 2nd subscribe: 3
|
||||||
|
// (at 3.5 seconds): 2nd sequence finished
|
||||||
|
|
||||||
|
// #enddocregion subscribe_twice
|
||||||
|
}
|
||||||
|
|
||||||
|
export function docRegionMulticastSequence(console) {
|
||||||
|
// #docregion multicast_sequence
|
||||||
|
function multicastSequenceSubscriber() {
|
||||||
|
const seq = [1, 2, 3];
|
||||||
|
// Keep track of each observer (one for every active subscription)
|
||||||
|
const observers = [];
|
||||||
|
// Still a single timeoutId because there will only ever be one
|
||||||
|
// set of values being generated, multicasted to each subscriber
|
||||||
|
let timeoutId;
|
||||||
|
|
||||||
|
// Return the subscriber function (runs when subscribe()
|
||||||
|
// function is invoked)
|
||||||
|
return observer => {
|
||||||
|
observers.push(observer);
|
||||||
|
// When this is the first subscription, start the sequence
|
||||||
|
if (observers.length === 1) {
|
||||||
|
timeoutId = doSequence({
|
||||||
|
next(val) {
|
||||||
|
// Iterate through observers and notify all subscriptions
|
||||||
|
observers.forEach(obs => obs.next(val));
|
||||||
|
},
|
||||||
|
complete() {
|
||||||
|
// Notify all complete callbacks
|
||||||
|
observers.slice(0).forEach(obs => obs.complete());
|
||||||
|
}
|
||||||
|
}, seq, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
unsubscribe() {
|
||||||
|
// Remove from the observers array so it's no longer notified
|
||||||
|
observers.splice(observers.indexOf(observer), 1);
|
||||||
|
// If there's no more listeners, do cleanup
|
||||||
|
if (observers.length === 0) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run through an array of numbers, emitting one value
|
||||||
// per second until it gets to the end of the array.
|
// per second until it gets to the end of the array.
|
||||||
function doInSequence(arr, idx) {
|
function doSequence(observer, arr, idx) {
|
||||||
timeoutId = setTimeout(() => {
|
return setTimeout(() => {
|
||||||
observer.next(arr[idx]);
|
observer.next(arr[idx]);
|
||||||
if (idx === arr.length - 1) {
|
if (idx === arr.length - 1) {
|
||||||
observer.complete();
|
observer.complete();
|
||||||
} else {
|
} else {
|
||||||
doInSequence(arr, ++idx);
|
doSequence(observer, arr, ++idx);
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
doInSequence(seq, 0);
|
// Create a new Observable that will deliver the above sequence
|
||||||
|
const multicastSequence = new Observable(multicastSequenceSubscriber());
|
||||||
|
|
||||||
// Unsubscribe should clear the timeout to stop execution
|
// Subscribe starts the clock, and begins to emit after 1 second
|
||||||
return {unsubscribe() {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new Observable that will deliver the above sequence
|
|
||||||
const sequence = new Observable(sequenceSubscriber);
|
|
||||||
|
|
||||||
sequence.subscribe({
|
|
||||||
next(num) { console.log(num); },
|
|
||||||
complete() { console.log('Finished sequence'); }
|
|
||||||
});
|
|
||||||
|
|
||||||
// Logs:
|
|
||||||
// (at 1 second): 1
|
|
||||||
// (at 2 seconds): 2
|
|
||||||
// (at 3 seconds): 3
|
|
||||||
// (at 3 seconds): Finished sequence
|
|
||||||
|
|
||||||
// #enddocregion delay_sequence
|
|
||||||
|
|
||||||
// #docregion subscribe_twice
|
|
||||||
|
|
||||||
// Subscribe starts the clock, and will emit after 1 second
|
|
||||||
sequence.subscribe({
|
|
||||||
next(num) { console.log('1st subscribe: ' + num); },
|
|
||||||
complete() { console.log('1st sequence finished.'); }
|
|
||||||
});
|
|
||||||
|
|
||||||
// After 1/2 second, subscribe again.
|
|
||||||
setTimeout(() => {
|
|
||||||
sequence.subscribe({
|
|
||||||
next(num) { console.log('2nd subscribe: ' + num); },
|
|
||||||
complete() { console.log('2nd sequence finished.'); }
|
|
||||||
});
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
// Logs:
|
|
||||||
// (at 1 second): 1st subscribe: 1
|
|
||||||
// (at 1.5 seconds): 2nd subscribe: 1
|
|
||||||
// (at 2 seconds): 1st subscribe: 2
|
|
||||||
// (at 2.5 seconds): 2nd subscribe: 2
|
|
||||||
// (at 3 seconds): 1st subscribe: 3
|
|
||||||
// (at 3 seconds): 1st sequence finished
|
|
||||||
// (at 3.5 seconds): 2nd subscribe: 3
|
|
||||||
// (at 3.5 seconds): 2nd sequence finished
|
|
||||||
|
|
||||||
// #enddocregion subscribe_twice
|
|
||||||
|
|
||||||
// #docregion multicast_sequence
|
|
||||||
|
|
||||||
function multicastSequenceSubscriber() {
|
|
||||||
const seq = [1, 2, 3];
|
|
||||||
// Keep track of each observer (one for every active subscription)
|
|
||||||
const observers = [];
|
|
||||||
// Still a single timeoutId because there will only ever be one
|
|
||||||
// set of values being generated, multicasted to each subscriber
|
|
||||||
let timeoutId;
|
|
||||||
|
|
||||||
// Return the subscriber function (runs when subscribe()
|
|
||||||
// function is invoked)
|
|
||||||
return (observer) => {
|
|
||||||
observers.push(observer);
|
|
||||||
// When this is the first subscription, start the sequence
|
|
||||||
if (observers.length === 1) {
|
|
||||||
timeoutId = doSequence({
|
|
||||||
next(val) {
|
|
||||||
// Iterate through observers and notify all subscriptions
|
|
||||||
observers.forEach(obs => obs.next(val));
|
|
||||||
},
|
|
||||||
complete() {
|
|
||||||
// Notify all complete callbacks
|
|
||||||
observers.slice(0).forEach(obs => obs.complete());
|
|
||||||
}
|
|
||||||
}, seq, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
unsubscribe() {
|
|
||||||
// Remove from the observers array so it's no longer notified
|
|
||||||
observers.splice(observers.indexOf(observer), 1);
|
|
||||||
// If there's no more listeners, do cleanup
|
|
||||||
if (observers.length === 0) {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run through an array of numbers, emitting one value
|
|
||||||
// per second until it gets to the end of the array.
|
|
||||||
function doSequence(observer, arr, idx) {
|
|
||||||
return setTimeout(() => {
|
|
||||||
observer.next(arr[idx]);
|
|
||||||
if (idx === arr.length - 1) {
|
|
||||||
observer.complete();
|
|
||||||
} else {
|
|
||||||
doSequence(observer, arr, ++idx);
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new Observable that will deliver the above sequence
|
|
||||||
const multicastSequence = new Observable(multicastSequenceSubscriber());
|
|
||||||
|
|
||||||
// Subscribe starts the clock, and begins to emit after 1 second
|
|
||||||
multicastSequence.subscribe({
|
|
||||||
next(num) { console.log('1st subscribe: ' + num); },
|
|
||||||
complete() { console.log('1st sequence finished.'); }
|
|
||||||
});
|
|
||||||
|
|
||||||
// After 1 1/2 seconds, subscribe again (should "miss" the first value).
|
|
||||||
setTimeout(() => {
|
|
||||||
multicastSequence.subscribe({
|
multicastSequence.subscribe({
|
||||||
next(num) { console.log('2nd subscribe: ' + num); },
|
next(num) { console.log('1st subscribe: ' + num); },
|
||||||
complete() { console.log('2nd sequence finished.'); }
|
complete() { console.log('1st sequence finished.'); }
|
||||||
});
|
});
|
||||||
}, 1500);
|
|
||||||
|
|
||||||
// Logs:
|
// After 1 1/2 seconds, subscribe again (should "miss" the first value).
|
||||||
// (at 1 second): 1st subscribe: 1
|
setTimeout(() => {
|
||||||
// (at 2 seconds): 1st subscribe: 2
|
multicastSequence.subscribe({
|
||||||
// (at 2 seconds): 2nd subscribe: 2
|
next(num) { console.log('2nd subscribe: ' + num); },
|
||||||
// (at 3 seconds): 1st subscribe: 3
|
complete() { console.log('2nd sequence finished.'); }
|
||||||
// (at 3 seconds): 1st sequence finished
|
});
|
||||||
// (at 3 seconds): 2nd subscribe: 3
|
}, 1500);
|
||||||
// (at 3 seconds): 2nd sequence finished
|
|
||||||
|
|
||||||
// #enddocregion multicast_sequence
|
// Logs:
|
||||||
|
// (at 1 second): 1st subscribe: 1
|
||||||
|
// (at 2 seconds): 1st subscribe: 2
|
||||||
|
// (at 2 seconds): 2nd subscribe: 2
|
||||||
|
// (at 3 seconds): 1st subscribe: 3
|
||||||
|
// (at 3 seconds): 1st sequence finished
|
||||||
|
// (at 3 seconds): 2nd subscribe: 3
|
||||||
|
// (at 3 seconds): 2nd sequence finished
|
||||||
|
|
||||||
|
// #enddocregion multicast_sequence
|
||||||
|
}
|
||||||
|
19
aio/content/examples/observables/src/subscribing.spec.ts
Normal file
19
aio/content/examples/observables/src/subscribing.spec.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { docRegionObserver } from './subscribing';
|
||||||
|
|
||||||
|
describe('subscribing', () => {
|
||||||
|
it('should subscribe and emit', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
docRegionObserver(console);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(8);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
['Observer got a next value: 1'],
|
||||||
|
['Observer got a next value: 2'],
|
||||||
|
['Observer got a next value: 3'],
|
||||||
|
['Observer got a complete notification'],
|
||||||
|
['Observer got a next value: 1'],
|
||||||
|
['Observer got a next value: 2'],
|
||||||
|
['Observer got a next value: 3'],
|
||||||
|
['Observer got a complete notification'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,32 +1,35 @@
|
|||||||
|
// #docplaster
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
import { Observable, of } from 'rxjs';
|
export function docRegionObserver(console) {
|
||||||
|
// #docregion observer
|
||||||
|
|
||||||
// #docregion observer
|
// Create simple observable that emits three values
|
||||||
|
const myObservable = of(1, 2, 3);
|
||||||
|
|
||||||
// Create simple observable that emits three values
|
// Create observer object
|
||||||
const myObservable = of(1, 2, 3);
|
const myObserver = {
|
||||||
|
next: x => console.log('Observer got a next value: ' + x),
|
||||||
|
error: err => console.error('Observer got an error: ' + err),
|
||||||
|
complete: () => console.log('Observer got a complete notification'),
|
||||||
|
};
|
||||||
|
|
||||||
// Create observer object
|
// Execute with the observer object
|
||||||
const myObserver = {
|
myObservable.subscribe(myObserver);
|
||||||
next: x => console.log('Observer got a next value: ' + x),
|
|
||||||
error: err => console.error('Observer got an error: ' + err),
|
|
||||||
complete: () => console.log('Observer got a complete notification'),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Execute with the observer object
|
// Logs:
|
||||||
myObservable.subscribe(myObserver);
|
// Observer got a next value: 1
|
||||||
// Logs:
|
// Observer got a next value: 2
|
||||||
// Observer got a next value: 1
|
// Observer got a next value: 3
|
||||||
// Observer got a next value: 2
|
// Observer got a complete notification
|
||||||
// Observer got a next value: 3
|
|
||||||
// Observer got a complete notification
|
|
||||||
|
|
||||||
// #enddocregion observer
|
// #enddocregion observer
|
||||||
|
|
||||||
// #docregion sub_fn
|
// #docregion sub_fn
|
||||||
myObservable.subscribe(
|
myObservable.subscribe(
|
||||||
x => console.log('Observer got a next value: ' + x),
|
x => console.log('Observer got a next value: ' + x),
|
||||||
err => console.error('Observer got an error: ' + err),
|
err => console.error('Observer got an error: ' + err),
|
||||||
() => console.log('Observer got a complete notification')
|
() => console.log('Observer got a complete notification')
|
||||||
);
|
);
|
||||||
// #enddocregion sub_fn
|
// #enddocregion sub_fn
|
||||||
|
}
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
"tests": [
|
"tests": [
|
||||||
{
|
{
|
||||||
"cmd": "yarn",
|
"cmd": "yarn",
|
||||||
"args": [ "tsc", "--project", "./tsconfig.app.json" ]
|
"args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": ["jasmine", "out-tsc/**/*.spec.js"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
import { interval } from 'rxjs';
|
||||||
|
import { tap } from 'rxjs/operators';
|
||||||
|
import { backoff } from './backoff';
|
||||||
|
|
||||||
|
describe('backoff()', () => {
|
||||||
|
beforeEach(() => jasmine.clock().install());
|
||||||
|
afterEach(() => jasmine.clock().uninstall());
|
||||||
|
|
||||||
|
it('should retry in case of error', () => {
|
||||||
|
const mockConsole = {log: jasmine.createSpy('log')};
|
||||||
|
const source = interval(10).pipe(
|
||||||
|
tap(i => {
|
||||||
|
if (i > 0) {
|
||||||
|
throw new Error('Test error');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
backoff(3, 100),
|
||||||
|
);
|
||||||
|
source.subscribe({
|
||||||
|
next: v => mockConsole.log(`Emitted: ${v}`),
|
||||||
|
error: e => mockConsole.log(`Errored: ${e.message || e}`),
|
||||||
|
complete: () => mockConsole.log('Completed'),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial try:
|
||||||
|
// Errors on second emission and schedules retrying (with delay).
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
mockConsole.log.calls.reset();
|
||||||
|
|
||||||
|
// First re-attempt after 100ms:
|
||||||
|
// Errors again on second emission and schedules retrying (with larger delay).
|
||||||
|
jasmine.clock().tick(100);
|
||||||
|
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
mockConsole.log.calls.reset();
|
||||||
|
|
||||||
|
// Second re-attempt after 400ms:
|
||||||
|
// Errors again on second emission and schedules retrying (with even larger delay).
|
||||||
|
jasmine.clock().tick(400);
|
||||||
|
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
mockConsole.log.calls.reset();
|
||||||
|
|
||||||
|
// Third re-attempt after 900ms:
|
||||||
|
// Errors again on second emission and gives up (no retrying).
|
||||||
|
jasmine.clock().tick(900);
|
||||||
|
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
mockConsole.log.calls.reset();
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Errored: Test error']]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,23 +1,32 @@
|
|||||||
|
// #docplaster
|
||||||
import { pipe, range, timer, zip } from 'rxjs';
|
// #docregion
|
||||||
|
import { of, pipe, range, throwError, timer, zip } from 'rxjs';
|
||||||
import { ajax } from 'rxjs/ajax';
|
import { ajax } from 'rxjs/ajax';
|
||||||
import { retryWhen, map, mergeMap } from 'rxjs/operators';
|
import { map, mergeMap, retryWhen } from 'rxjs/operators';
|
||||||
|
|
||||||
function backoff(maxTries, ms) {
|
export function backoff(maxTries, delay) {
|
||||||
return pipe(
|
return pipe(
|
||||||
retryWhen(attempts => zip(range(1, maxTries), attempts)
|
retryWhen(attempts =>
|
||||||
.pipe(
|
zip(range(1, maxTries + 1), attempts).pipe(
|
||||||
map(([i]) => i * i),
|
mergeMap(([i, err]) => (i > maxTries) ? throwError(err) : of(i)),
|
||||||
mergeMap(i => timer(i * ms))
|
map(i => i * i),
|
||||||
)
|
mergeMap(v => timer(v * delay)),
|
||||||
)
|
),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
/*
|
||||||
|
This function declaration is necessary to ensure that it does not get called
|
||||||
|
when running the unit tests. It will not get rendered into the docs.
|
||||||
|
The indentation needs to start in the leftmost level position as well because of how
|
||||||
|
the docplaster combines the different regions together.
|
||||||
|
*/
|
||||||
|
function docRegionAjaxCall() {
|
||||||
|
// #docregion
|
||||||
ajax('/api/endpoint')
|
ajax('/api/endpoint')
|
||||||
.pipe(backoff(3, 250))
|
.pipe(backoff(3, 250))
|
||||||
.subscribe(data => handleData(data));
|
.subscribe(function handleData(data) { /* ... */ });
|
||||||
|
// #enddocregion
|
||||||
function handleData(data) {
|
|
||||||
// ...
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
import { of } from 'rxjs';
|
||||||
|
import { docRegionTypeahead } from './typeahead';
|
||||||
|
|
||||||
|
describe('typeahead', () => {
|
||||||
|
let document;
|
||||||
|
let ajax;
|
||||||
|
let triggertInputChange;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jasmine.clock().install();
|
||||||
|
const input = {
|
||||||
|
addEventListener: jasmine
|
||||||
|
.createSpy('addEvent')
|
||||||
|
.and.callFake((eventName: string, cb: (e) => void) => {
|
||||||
|
if (eventName === 'input') {
|
||||||
|
triggertInputChange = cb;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
removeEventListener: jasmine.createSpy('removeEvent'),
|
||||||
|
};
|
||||||
|
|
||||||
|
document = { getElementById: (id: string) => input };
|
||||||
|
ajax = jasmine.createSpy('ajax').and.callFake((url: string) => of('foo bar'));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jasmine.clock().uninstall();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should make an ajax call to the corrent endpoint', () => {
|
||||||
|
docRegionTypeahead(document, ajax);
|
||||||
|
triggertInputChange({ target: { value: 'foo' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).toHaveBeenCalledWith('/api/endpoint?search=foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not make an ajax call, when the input length < 3', () => {
|
||||||
|
docRegionTypeahead(document, ajax);
|
||||||
|
triggertInputChange({ target: { value: '' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).not.toHaveBeenCalled();
|
||||||
|
triggertInputChange({ target: { value: 'fo' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not make an ajax call for intermediate values when debouncing', () => {
|
||||||
|
docRegionTypeahead(document, ajax);
|
||||||
|
triggertInputChange({ target: { value: 'foo' } });
|
||||||
|
jasmine.clock().tick(9);
|
||||||
|
triggertInputChange({ target: { value: 'bar' } });
|
||||||
|
jasmine.clock().tick(9);
|
||||||
|
triggertInputChange({ target: { value: 'baz' } });
|
||||||
|
jasmine.clock().tick(9);
|
||||||
|
triggertInputChange({ target: { value: 'qux' } });
|
||||||
|
expect(ajax).not.toHaveBeenCalled();
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(ajax).toHaveBeenCalledTimes(1);
|
||||||
|
expect(ajax).toHaveBeenCalledWith('/api/endpoint?search=qux');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not make an ajax call, when the input value has not changed', () => {
|
||||||
|
docRegionTypeahead(document, ajax);
|
||||||
|
triggertInputChange({ target: { value: 'foo' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).toHaveBeenCalled();
|
||||||
|
ajax.calls.reset();
|
||||||
|
triggertInputChange({ target: { value: 'foo' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -1,18 +1,32 @@
|
|||||||
import { fromEvent } from 'rxjs';
|
/*
|
||||||
import { ajax } from 'rxjs/ajax';
|
Because of how the code is merged together using the doc regions,
|
||||||
import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
// #docplaster
|
||||||
|
// #docregion
|
||||||
|
import { fromEvent } from 'rxjs';
|
||||||
|
import { ajax } from 'rxjs/ajax';
|
||||||
|
import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
/* tslint:disable:no-shadowed-variable */
|
||||||
|
/* tslint:disable:align */
|
||||||
|
export function docRegionTypeahead(document, ajax) {
|
||||||
|
// #docregion
|
||||||
|
const searchBox = document.getElementById('search-box');
|
||||||
|
|
||||||
const searchBox = document.getElementById('search-box');
|
const typeahead = fromEvent(searchBox, 'input').pipe(
|
||||||
|
map((e: KeyboardEvent) => (e.target as HTMLInputElement).value),
|
||||||
|
filter(text => text.length > 2),
|
||||||
|
debounceTime(10),
|
||||||
|
distinctUntilChanged(),
|
||||||
|
switchMap(searchTerm => ajax(`/api/endpoint?search=${searchTerm}`))
|
||||||
|
);
|
||||||
|
|
||||||
const typeahead = fromEvent(searchBox, 'input').pipe(
|
typeahead.subscribe(data => {
|
||||||
map((e: KeyboardEvent) => (e.target as HTMLInputElement).value),
|
// Handle the data from the API
|
||||||
filter(text => text.length > 2),
|
});
|
||||||
debounceTime(10),
|
|
||||||
distinctUntilChanged(),
|
|
||||||
switchMap(() => ajax('/api/endpoint'))
|
|
||||||
);
|
|
||||||
|
|
||||||
typeahead.subscribe(data => {
|
// #enddocregion
|
||||||
// Handle the data from the API
|
return typeahead;
|
||||||
});
|
}
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
"tests": [
|
"tests": [
|
||||||
{
|
{
|
||||||
"cmd": "yarn",
|
"cmd": "yarn",
|
||||||
"args": [ "tsc", "--project", "./tsconfig.app.json" ]
|
"args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": ["jasmine", "out-tsc/**/*.spec.js"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
46
aio/content/examples/rx-library/src/error-handling.spec.ts
Normal file
46
aio/content/examples/rx-library/src/error-handling.spec.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { Subject, throwError } from 'rxjs';
|
||||||
|
import { docRegionDefault } from './error-handling';
|
||||||
|
|
||||||
|
describe('error-handling', () => {
|
||||||
|
let mockConsole;
|
||||||
|
let ajaxSubject;
|
||||||
|
let ajax;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockConsole = {log: jasmine.createSpy('log')};
|
||||||
|
ajaxSubject = new Subject();
|
||||||
|
ajax = jasmine
|
||||||
|
.createSpy('ajax')
|
||||||
|
.and.callFake((url: string) => ajaxSubject);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => ajaxSubject.unsubscribe());
|
||||||
|
|
||||||
|
it('should return the response object', () => {
|
||||||
|
docRegionDefault(mockConsole, ajax);
|
||||||
|
|
||||||
|
ajaxSubject.next({response: {foo: 'bar'}});
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||||
|
['data: ', {foo: 'bar'}]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty array when using an object without a `response` property', () => {
|
||||||
|
docRegionDefault(mockConsole, ajax);
|
||||||
|
|
||||||
|
ajaxSubject.next({foo: 'bar'});
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||||
|
['data: ', []]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty array when the ajax observable errors', () => {
|
||||||
|
ajax.and.returnValue(throwError('Test Error'));
|
||||||
|
|
||||||
|
docRegionDefault(mockConsole, ajax);
|
||||||
|
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||||
|
['data: ', []]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,25 +1,36 @@
|
|||||||
|
// #docplaster
|
||||||
import { of } from 'rxjs';
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:no-shadowed-variable */
|
||||||
|
/* tslint:disable:align */
|
||||||
// #docregion
|
// #docregion
|
||||||
|
import { of } from 'rxjs';
|
||||||
import { ajax } from 'rxjs/ajax';
|
import { ajax } from 'rxjs/ajax';
|
||||||
import { map, catchError } from 'rxjs/operators';
|
import { map, catchError } from 'rxjs/operators';
|
||||||
// Return "response" from the API. If an error happens,
|
|
||||||
// return an empty array.
|
|
||||||
const apiData = ajax('/api/data').pipe(
|
|
||||||
map(res => {
|
|
||||||
if (!res.response) {
|
|
||||||
throw new Error('Value expected!');
|
|
||||||
}
|
|
||||||
return res.response;
|
|
||||||
}),
|
|
||||||
catchError(err => of([]))
|
|
||||||
);
|
|
||||||
|
|
||||||
apiData.subscribe({
|
|
||||||
next(x) { console.log('data: ', x); },
|
|
||||||
error(err) { console.log('errors already caught... will not run'); }
|
|
||||||
});
|
|
||||||
|
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
|
export function docRegionDefault(console, ajax) {
|
||||||
|
// #docregion
|
||||||
|
// Return "response" from the API. If an error happens,
|
||||||
|
// return an empty array.
|
||||||
|
const apiData = ajax('/api/data').pipe(
|
||||||
|
map((res: any) => {
|
||||||
|
if (!res.response) {
|
||||||
|
throw new Error('Value expected!');
|
||||||
|
}
|
||||||
|
return res.response;
|
||||||
|
}),
|
||||||
|
catchError(err => of([]))
|
||||||
|
);
|
||||||
|
|
||||||
|
apiData.subscribe({
|
||||||
|
next(x) { console.log('data: ', x); },
|
||||||
|
error(err) { console.log('errors already caught... will not run'); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
return apiData;
|
||||||
|
}
|
||||||
|
14
aio/content/examples/rx-library/src/operators.1.spec.ts
Normal file
14
aio/content/examples/rx-library/src/operators.1.spec.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { docRegionDefault } from './operators.1';
|
||||||
|
|
||||||
|
describe('squareOdd - operators.1.ts', () => {
|
||||||
|
it('should return square odds', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
docRegionDefault(console);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(3);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
[1],
|
||||||
|
[9],
|
||||||
|
[25],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,23 +1,30 @@
|
|||||||
import { of, pipe } from 'rxjs';
|
// #docplaster
|
||||||
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:align */
|
||||||
// #docregion
|
// #docregion
|
||||||
|
import { of, pipe } from 'rxjs';
|
||||||
import { filter, map } from 'rxjs/operators';
|
import { filter, map } from 'rxjs/operators';
|
||||||
|
|
||||||
const nums = of(1, 2, 3, 4, 5);
|
|
||||||
|
|
||||||
// Create a function that accepts an Observable.
|
|
||||||
const squareOddVals = pipe(
|
|
||||||
filter((n: number) => n % 2 !== 0),
|
|
||||||
map(n => n * n)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create an Observable that will run the filter and map functions
|
|
||||||
const squareOdd = squareOddVals(nums);
|
|
||||||
|
|
||||||
// Subscribe to run the combined functions
|
|
||||||
squareOdd.subscribe(x => console.log(x));
|
|
||||||
|
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
|
export function docRegionDefault(console) {
|
||||||
|
// #docregion
|
||||||
|
const nums = of(1, 2, 3, 4, 5);
|
||||||
|
|
||||||
|
// Create a function that accepts an Observable.
|
||||||
|
const squareOddVals = pipe(
|
||||||
|
filter((n: number) => n % 2 !== 0),
|
||||||
|
map(n => n * n)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create an Observable that will run the filter and map functions
|
||||||
|
const squareOdd = squareOddVals(nums);
|
||||||
|
|
||||||
|
// Subscribe to run the combined functions
|
||||||
|
squareOdd.subscribe(x => console.log(x));
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
}
|
||||||
|
14
aio/content/examples/rx-library/src/operators.2.spec.ts
Normal file
14
aio/content/examples/rx-library/src/operators.2.spec.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { docRegionDefault } from './operators.2';
|
||||||
|
|
||||||
|
describe('squareOdd - operators.2.ts', () => {
|
||||||
|
it('should return square odds', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
docRegionDefault(console);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(3);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
[1],
|
||||||
|
[9],
|
||||||
|
[25],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,16 +1,25 @@
|
|||||||
import { Observable, of } from 'rxjs';
|
// #docplaster
|
||||||
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:align */
|
||||||
// #docregion
|
// #docregion
|
||||||
|
import { of } from 'rxjs';
|
||||||
import { filter, map } from 'rxjs/operators';
|
import { filter, map } from 'rxjs/operators';
|
||||||
|
|
||||||
const squareOdd = of(1, 2, 3, 4, 5)
|
|
||||||
.pipe(
|
|
||||||
filter(n => n % 2 !== 0),
|
|
||||||
map(n => n * n)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Subscribe to get values
|
|
||||||
squareOdd.subscribe(x => console.log(x));
|
|
||||||
|
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
|
export function docRegionDefault(console) {
|
||||||
|
// #docregion
|
||||||
|
const squareOdd = of(1, 2, 3, 4, 5)
|
||||||
|
.pipe(
|
||||||
|
filter(n => n % 2 !== 0),
|
||||||
|
map(n => n * n)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Subscribe to get values
|
||||||
|
squareOdd.subscribe(x => console.log(x));
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
}
|
||||||
|
14
aio/content/examples/rx-library/src/operators.spec.ts
Normal file
14
aio/content/examples/rx-library/src/operators.spec.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { docRegionDefault } from './operators';
|
||||||
|
|
||||||
|
describe('squaredNums - operators.ts', () => {
|
||||||
|
it('should return square odds', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
docRegionDefault(console);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(3);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
[1],
|
||||||
|
[4],
|
||||||
|
[9],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,20 +1,28 @@
|
|||||||
|
// #docplaster
|
||||||
import { Observable, of } from 'rxjs';
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:align */
|
||||||
// #docregion
|
// #docregion
|
||||||
|
import { of } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
const nums = of(1, 2, 3);
|
|
||||||
|
|
||||||
const squareValues = map((val: number) => val * val);
|
|
||||||
const squaredNums = squareValues(nums);
|
|
||||||
|
|
||||||
squaredNums.subscribe(x => console.log(x));
|
|
||||||
|
|
||||||
// Logs
|
|
||||||
// 1
|
|
||||||
// 4
|
|
||||||
// 9
|
|
||||||
|
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
|
export function docRegionDefault(console) {
|
||||||
|
// #docregion
|
||||||
|
const nums = of(1, 2, 3);
|
||||||
|
|
||||||
|
const squareValues = map((val: number) => val * val);
|
||||||
|
const squaredNums = squareValues(nums);
|
||||||
|
|
||||||
|
squaredNums.subscribe(x => console.log(x));
|
||||||
|
|
||||||
|
// Logs
|
||||||
|
// 1
|
||||||
|
// 4
|
||||||
|
// 9
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
}
|
||||||
|
69
aio/content/examples/rx-library/src/retry-on-error.spec.ts
Normal file
69
aio/content/examples/rx-library/src/retry-on-error.spec.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { of, throwError } from 'rxjs';
|
||||||
|
import { mergeMap, tap } from 'rxjs/operators';
|
||||||
|
import { docRegionDefault } from './retry-on-error';
|
||||||
|
|
||||||
|
describe('retry-on-error', () => {
|
||||||
|
let mockConsole;
|
||||||
|
beforeEach(() => mockConsole = { log: jasmine.createSpy('log') });
|
||||||
|
|
||||||
|
it('should return the response object', () => {
|
||||||
|
const ajax = () => of({ response: { foo: 'bar' } });
|
||||||
|
|
||||||
|
docRegionDefault(mockConsole, ajax);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||||
|
['data: ', { foo: 'bar' }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty array after 3 retries + 1 initial request', () => {
|
||||||
|
const ajax = () => {
|
||||||
|
return of({ noresponse: true }).pipe(tap(() => mockConsole.log('Subscribed to AJAX')));
|
||||||
|
};
|
||||||
|
|
||||||
|
docRegionDefault(mockConsole, ajax);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||||
|
['Subscribed to AJAX'],
|
||||||
|
['Error occurred.'],
|
||||||
|
['Subscribed to AJAX'],
|
||||||
|
['Error occurred.'],
|
||||||
|
['Subscribed to AJAX'],
|
||||||
|
['Error occurred.'],
|
||||||
|
['Subscribed to AJAX'],
|
||||||
|
['Error occurred.'],
|
||||||
|
['data: ', []],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the response if the request succeeds upon retrying', () => {
|
||||||
|
// Fail on the first two requests, but succeed from the 3rd onwards.
|
||||||
|
let failCount = 2;
|
||||||
|
const ajax = () => of(null).pipe(
|
||||||
|
tap(() => mockConsole.log('Subscribed to AJAX')),
|
||||||
|
// Fail on the first 2 requests, but succeed from the 3rd onwards.
|
||||||
|
mergeMap(() => {
|
||||||
|
if (failCount > 0) {
|
||||||
|
failCount--;
|
||||||
|
return throwError('Test error');
|
||||||
|
}
|
||||||
|
return of({ response: { foo: 'bar' } });
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
docRegionDefault(mockConsole, ajax);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||||
|
['Subscribed to AJAX'], // Initial request | 1st attempt overall
|
||||||
|
['Subscribed to AJAX'], // 1st retry attempt | 2nd attempt overall
|
||||||
|
['Subscribed to AJAX'], // 2nd retry attempt | 3rd attempt overall
|
||||||
|
['data: ', { foo: 'bar' }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty array when the ajax observable throws an error', () => {
|
||||||
|
const ajax = () => throwError('Test Error');
|
||||||
|
|
||||||
|
docRegionDefault(mockConsole, ajax);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||||
|
['data: ', []],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,26 +1,35 @@
|
|||||||
|
// #docplaster
|
||||||
import { Observable, of } from 'rxjs';
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:no-shadowed-variable */
|
||||||
|
/* tslint:disable:align */
|
||||||
// #docregion
|
// #docregion
|
||||||
|
import { of } from 'rxjs';
|
||||||
import { ajax } from 'rxjs/ajax';
|
import { ajax } from 'rxjs/ajax';
|
||||||
import { map, retry, catchError } from 'rxjs/operators';
|
import { map, retry, catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
const apiData = ajax('/api/data').pipe(
|
|
||||||
retry(3), // Retry up to 3 times before failing
|
|
||||||
map(res => {
|
|
||||||
if (!res.response) {
|
|
||||||
throw new Error('Value expected!');
|
|
||||||
}
|
|
||||||
return res.response;
|
|
||||||
}),
|
|
||||||
catchError(err => of([]))
|
|
||||||
);
|
|
||||||
|
|
||||||
apiData.subscribe({
|
|
||||||
next(x) { console.log('data: ', x); },
|
|
||||||
error(err) { console.log('errors already caught... will not run'); }
|
|
||||||
});
|
|
||||||
|
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
|
export function docRegionDefault(console, ajax) {
|
||||||
|
// #docregion
|
||||||
|
const apiData = ajax('/api/data').pipe(
|
||||||
|
map((res: any) => {
|
||||||
|
if (!res.response) {
|
||||||
|
console.log('Error occurred.');
|
||||||
|
throw new Error('Value expected!');
|
||||||
|
}
|
||||||
|
return res.response;
|
||||||
|
}),
|
||||||
|
retry(3), // Retry up to 3 times before failing
|
||||||
|
catchError(err => of([]))
|
||||||
|
);
|
||||||
|
|
||||||
|
apiData.subscribe({
|
||||||
|
next(x) { console.log('data: ', x); },
|
||||||
|
error(err) { console.log('errors already caught... will not run'); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
import { of } from 'rxjs';
|
||||||
|
import { docRegionPromise } from './simple-creation.1';
|
||||||
|
|
||||||
|
describe('simple-creation.1', () => {
|
||||||
|
it('should create a promise from an observable and return an empty object', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
const fetch = () => of({foo: 42});
|
||||||
|
docRegionPromise(console, fetch);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
[{foo: 42}],
|
||||||
|
['Completed'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
24
aio/content/examples/rx-library/src/simple-creation.1.ts
Normal file
24
aio/content/examples/rx-library/src/simple-creation.1.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// #docplaster
|
||||||
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:align */
|
||||||
|
// #docregion promise
|
||||||
|
import { from } from 'rxjs';
|
||||||
|
|
||||||
|
// #enddocregion promise
|
||||||
|
|
||||||
|
export function docRegionPromise(console, fetch) {
|
||||||
|
// #docregion promise
|
||||||
|
// Create an Observable out of a promise
|
||||||
|
const data = from(fetch('/api/endpoint'));
|
||||||
|
// Subscribe to begin listening for async result
|
||||||
|
data.subscribe({
|
||||||
|
next(response) { console.log(response); },
|
||||||
|
error(err) { console.error('Error: ' + err); },
|
||||||
|
complete() { console.log('Completed'); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// #enddocregion promise
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import { docRegionInterval } from './simple-creation.2';
|
||||||
|
|
||||||
|
describe('simple-creation.2', () => {
|
||||||
|
beforeEach(() => jasmine.clock().install());
|
||||||
|
afterEach(() => jasmine.clock().uninstall());
|
||||||
|
|
||||||
|
it('should create an Observable that will publish a value on an interval', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
const subscription = docRegionInterval(console);
|
||||||
|
jasmine.clock().tick(1000);
|
||||||
|
expect(console.log).toHaveBeenCalledWith('It\'s been 1 seconds since subscribing!');
|
||||||
|
console.log.calls.reset();
|
||||||
|
|
||||||
|
jasmine.clock().tick(999);
|
||||||
|
expect(console.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
jasmine.clock().tick(1);
|
||||||
|
expect(console.log).toHaveBeenCalledWith('It\'s been 2 seconds since subscribing!');
|
||||||
|
subscription.unsubscribe();
|
||||||
|
});
|
||||||
|
});
|
22
aio/content/examples/rx-library/src/simple-creation.2.ts
Normal file
22
aio/content/examples/rx-library/src/simple-creation.2.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// #docplaster
|
||||||
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:align */
|
||||||
|
// #docregion interval
|
||||||
|
import { interval } from 'rxjs';
|
||||||
|
|
||||||
|
// #enddocregion interval
|
||||||
|
|
||||||
|
export function docRegionInterval(console) {
|
||||||
|
// #docregion interval
|
||||||
|
// Create an Observable that will publish a value on an interval
|
||||||
|
const secondsCounter = interval(1000);
|
||||||
|
// Subscribe to begin publishing values
|
||||||
|
const subscription = secondsCounter.subscribe(n =>
|
||||||
|
console.log(`It's been ${n + 1} seconds since subscribing!`));
|
||||||
|
|
||||||
|
// #enddocregion interval
|
||||||
|
return subscription;
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
import { docRegionEvent } from './simple-creation.3';
|
||||||
|
|
||||||
|
describe('simple-creation.3', () => {
|
||||||
|
let triggerMousemove;
|
||||||
|
let mockConsole;
|
||||||
|
let input;
|
||||||
|
let mockDocument;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockConsole = {log: jasmine.createSpy('log')};
|
||||||
|
input = {
|
||||||
|
addEventListener: jasmine
|
||||||
|
.createSpy('addEventListener')
|
||||||
|
.and.callFake((eventName, cb) => {
|
||||||
|
if (eventName === 'mousemove') {
|
||||||
|
triggerMousemove = cb;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
removeEventListener: jasmine.createSpy('removeEventListener'),
|
||||||
|
};
|
||||||
|
mockDocument = { getElementById: () => input };
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log coords when subscribing', () => {
|
||||||
|
docRegionEvent(mockConsole, mockDocument);
|
||||||
|
|
||||||
|
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
triggerMousemove({ clientX: 50, clientY: 50 });
|
||||||
|
triggerMousemove({ clientX: 30, clientY: 50 });
|
||||||
|
triggerMousemove({ clientX: 50, clientY: 30 });
|
||||||
|
expect(mockConsole.log).toHaveBeenCalledTimes(3);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||||
|
['Coords: 50 X 50'],
|
||||||
|
['Coords: 30 X 50'],
|
||||||
|
['Coords: 50 X 30']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call unsubscribe when clientX and clientY are below < 40 ', () => {
|
||||||
|
docRegionEvent(mockConsole, mockDocument);
|
||||||
|
|
||||||
|
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Ensure that we have unsubscribed.
|
||||||
|
triggerMousemove({ clientX: 30, clientY: 30 });
|
||||||
|
expect(input.removeEventListener).toHaveBeenCalledWith('mousemove', triggerMousemove, undefined);
|
||||||
|
mockConsole.log.calls.reset();
|
||||||
|
|
||||||
|
triggerMousemove({ clientX: 50, clientY: 50 });
|
||||||
|
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
32
aio/content/examples/rx-library/src/simple-creation.3.ts
Normal file
32
aio/content/examples/rx-library/src/simple-creation.3.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// #docplaster
|
||||||
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:align */
|
||||||
|
// #docregion event
|
||||||
|
import { fromEvent } from 'rxjs';
|
||||||
|
|
||||||
|
// #enddocregion event
|
||||||
|
|
||||||
|
export function docRegionEvent(console, document) {
|
||||||
|
// #docregion event
|
||||||
|
const el = document.getElementById('my-element');
|
||||||
|
|
||||||
|
// Create an Observable that will publish mouse movements
|
||||||
|
const mouseMoves = fromEvent(el, 'mousemove');
|
||||||
|
|
||||||
|
// Subscribe to start listening for mouse-move events
|
||||||
|
const subscription = mouseMoves.subscribe((evt: MouseEvent) => {
|
||||||
|
// Log coords of mouse movements
|
||||||
|
console.log(`Coords: ${evt.clientX} X ${evt.clientY}`);
|
||||||
|
|
||||||
|
// When the mouse is over the upper-left of the screen,
|
||||||
|
// unsubscribe to stop listening for mouse movements
|
||||||
|
if (evt.clientX < 40 && evt.clientY < 40) {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// #enddocregion event
|
||||||
|
}
|
14
aio/content/examples/rx-library/src/simple-creation.spec.ts
Normal file
14
aio/content/examples/rx-library/src/simple-creation.spec.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { of } from 'rxjs';
|
||||||
|
import { docRegionAjax } from './simple-creation';
|
||||||
|
|
||||||
|
describe('ajax', () => {
|
||||||
|
it('should make a request and console log the status and response', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
const ajax = jasmine.createSpy('ajax').and.callFake((url: string) => {
|
||||||
|
return of({status: 200, response: 'foo bar'});
|
||||||
|
});
|
||||||
|
|
||||||
|
docRegionAjax(console, ajax);
|
||||||
|
expect(console.log).toHaveBeenCalledWith(200, 'foo bar');
|
||||||
|
});
|
||||||
|
});
|
@ -1,65 +1,19 @@
|
|||||||
|
// #docplaster
|
||||||
// #docregion promise
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
import { from } from 'rxjs';
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
// Create an Observable out of a promise
|
/* tslint:disable:no-shadowed-variable */
|
||||||
const data = from(fetch('/api/endpoint'));
|
/* tslint:disable:align */
|
||||||
// Subscribe to begin listening for async result
|
|
||||||
data.subscribe({
|
|
||||||
next(response) { console.log(response); },
|
|
||||||
error(err) { console.error('Error: ' + err); },
|
|
||||||
complete() { console.log('Completed'); }
|
|
||||||
});
|
|
||||||
|
|
||||||
// #enddocregion promise
|
|
||||||
|
|
||||||
// #docregion interval
|
|
||||||
|
|
||||||
import { interval } from 'rxjs';
|
|
||||||
|
|
||||||
// Create an Observable that will publish a value on an interval
|
|
||||||
const secondsCounter = interval(1000);
|
|
||||||
// Subscribe to begin publishing values
|
|
||||||
secondsCounter.subscribe(n =>
|
|
||||||
console.log(`It's been ${n} seconds since subscribing!`));
|
|
||||||
|
|
||||||
// #enddocregion interval
|
|
||||||
|
|
||||||
|
|
||||||
// #docregion event
|
|
||||||
|
|
||||||
import { fromEvent } from 'rxjs';
|
|
||||||
|
|
||||||
const el = document.getElementById('my-element');
|
|
||||||
|
|
||||||
// Create an Observable that will publish mouse movements
|
|
||||||
const mouseMoves = fromEvent(el, 'mousemove');
|
|
||||||
|
|
||||||
// Subscribe to start listening for mouse-move events
|
|
||||||
const subscription = mouseMoves.subscribe((evt: MouseEvent) => {
|
|
||||||
// Log coords of mouse movements
|
|
||||||
console.log(`Coords: ${evt.clientX} X ${evt.clientY}`);
|
|
||||||
|
|
||||||
// When the mouse is over the upper-left of the screen,
|
|
||||||
// unsubscribe to stop listening for mouse movements
|
|
||||||
if (evt.clientX < 40 && evt.clientY < 40) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// #enddocregion event
|
|
||||||
|
|
||||||
|
|
||||||
// #docregion ajax
|
// #docregion ajax
|
||||||
|
import { ajax } from 'rxjs/ajax';
|
||||||
import { ajax } from 'rxjs/ajax';
|
|
||||||
|
|
||||||
// Create an Observable that will create an AJAX request
|
// Create an Observable that will create an AJAX request
|
||||||
const apiData = ajax('/api/data');
|
|
||||||
// Subscribe to create the request
|
|
||||||
apiData.subscribe(res => console.log(res.status, res.response));
|
|
||||||
|
|
||||||
// #enddocregion ajax
|
// #enddocregion ajax
|
||||||
|
export function docRegionAjax(console, ajax) {
|
||||||
|
// #docregion ajax
|
||||||
|
const apiData = ajax('/api/data');
|
||||||
|
// Subscribe to create the request
|
||||||
|
apiData.subscribe(res => console.log(res.status, res.response));
|
||||||
|
// #enddocregion ajax
|
||||||
|
}
|
||||||
|
@ -28,7 +28,7 @@ import {
|
|||||||
ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync
|
ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync
|
||||||
} from '@angular/core/testing';
|
} from '@angular/core/testing';
|
||||||
|
|
||||||
import { addMatchers, newEvent, click } from '../../testing';
|
import { addMatchers, click } from '../../testing';
|
||||||
|
|
||||||
export class NotProvided extends ValueService { /* example below */ }
|
export class NotProvided extends ValueService { /* example below */ }
|
||||||
beforeEach(addMatchers);
|
beforeEach(addMatchers);
|
||||||
@ -274,9 +274,11 @@ describe('demo (with TestBed):', () => {
|
|||||||
expect(comp.name).toBe(expectedOrigName,
|
expect(comp.name).toBe(expectedOrigName,
|
||||||
`comp.name should still be ${expectedOrigName} after value change, before binding happens`);
|
`comp.name should still be ${expectedOrigName} after value change, before binding happens`);
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular learns of input value change.
|
// Dispatch a DOM event so that Angular learns of input value change.
|
||||||
// then wait while ngModel pushes input.box value to comp.name
|
// then wait while ngModel pushes input.box value to comp.name
|
||||||
input.dispatchEvent(newEvent('input'));
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
|
input.dispatchEvent(new Event('input'));
|
||||||
return fixture.whenStable();
|
return fixture.whenStable();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -312,9 +314,11 @@ describe('demo (with TestBed):', () => {
|
|||||||
expect(comp.name).toBe(expectedOrigName,
|
expect(comp.name).toBe(expectedOrigName,
|
||||||
`comp.name should still be ${expectedOrigName} after value change, before binding happens`);
|
`comp.name should still be ${expectedOrigName} after value change, before binding happens`);
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular learns of input value change.
|
// Dispatch a DOM event so that Angular learns of input value change.
|
||||||
// then wait a tick while ngModel pushes input.box value to comp.name
|
// then wait a tick while ngModel pushes input.box value to comp.name
|
||||||
input.dispatchEvent(newEvent('input'));
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
|
input.dispatchEvent(new Event('input'));
|
||||||
tick();
|
tick();
|
||||||
expect(comp.name).toBe(expectedNewName,
|
expect(comp.name).toBe(expectedNewName,
|
||||||
`After ngModel updates the model, comp.name should be ${expectedNewName} `);
|
`After ngModel updates the model, comp.name should be ${expectedNewName} `);
|
||||||
@ -335,10 +339,12 @@ describe('demo (with TestBed):', () => {
|
|||||||
// simulate user entering new name in input
|
// simulate user entering new name in input
|
||||||
input.value = inputText;
|
input.value = inputText;
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular learns of input value change.
|
// Dispatch a DOM event so that Angular learns of input value change.
|
||||||
// then wait a tick while ngModel pushes input.box value to comp.text
|
// then wait a tick while ngModel pushes input.box value to comp.text
|
||||||
// and Angular updates the output span
|
// and Angular updates the output span
|
||||||
input.dispatchEvent(newEvent('input'));
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
|
input.dispatchEvent(new Event('input'));
|
||||||
tick();
|
tick();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(span.textContent).toBe(expectedText, 'output span');
|
expect(span.textContent).toBe(expectedText, 'output span');
|
||||||
|
@ -3,7 +3,7 @@ import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ActivatedRoute, ActivatedRouteStub, asyncData, click, newEvent
|
ActivatedRoute, ActivatedRouteStub, asyncData, click
|
||||||
} from '../../testing';
|
} from '../../testing';
|
||||||
|
|
||||||
import { Hero } from '../model/hero';
|
import { Hero } from '../model/hero';
|
||||||
@ -99,7 +99,10 @@ function overrideSetup() {
|
|||||||
const newName = 'New Name';
|
const newName = 'New Name';
|
||||||
|
|
||||||
page.nameInput.value = newName;
|
page.nameInput.value = newName;
|
||||||
page.nameInput.dispatchEvent(newEvent('input')); // tell Angular
|
|
||||||
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
|
page.nameInput.dispatchEvent(new Event('input')); // tell Angular
|
||||||
|
|
||||||
expect(component.hero.name).toBe(newName, 'component hero has new name');
|
expect(component.hero.name).toBe(newName, 'component hero has new name');
|
||||||
expect(hdsSpy.testHero.name).toBe(origName, 'service hero unchanged before save');
|
expect(hdsSpy.testHero.name).toBe(origName, 'service hero unchanged before save');
|
||||||
@ -197,9 +200,10 @@ function heroModuleSetup() {
|
|||||||
// simulate user entering a new name into the input box
|
// simulate user entering a new name into the input box
|
||||||
nameInput.value = 'quick BROWN fOx';
|
nameInput.value = 'quick BROWN fOx';
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular learns of input value change.
|
// Dispatch a DOM event so that Angular learns of input value change.
|
||||||
// use newEvent utility function (not provided by Angular) for better browser compatibility
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
nameInput.dispatchEvent(newEvent('input'));
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
|
nameInput.dispatchEvent(new Event('input'));
|
||||||
|
|
||||||
// Tell Angular to update the display binding through the title pipe
|
// Tell Angular to update the display binding through the title pipe
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -6,7 +6,7 @@ import { DebugElement } from '@angular/core';
|
|||||||
|
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { addMatchers, newEvent } from '../../testing';
|
import { addMatchers } from '../../testing';
|
||||||
import { HeroService } from '../model/hero.service';
|
import { HeroService } from '../model/hero.service';
|
||||||
import { getTestHeroes, TestHeroService } from '../model/testing/test-hero.service';
|
import { getTestHeroes, TestHeroService } from '../model/testing/test-hero.service';
|
||||||
|
|
||||||
@ -53,7 +53,10 @@ describe('HeroListComponent', () => {
|
|||||||
it('should select hero on click', fakeAsync(() => {
|
it('should select hero on click', fakeAsync(() => {
|
||||||
const expectedHero = HEROES[1];
|
const expectedHero = HEROES[1];
|
||||||
const li = page.heroRows[1];
|
const li = page.heroRows[1];
|
||||||
li.dispatchEvent(newEvent('click'));
|
|
||||||
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
|
li.dispatchEvent(new Event('click'));
|
||||||
tick();
|
tick();
|
||||||
// `.toEqual` because selectedHero is clone of expectedHero; see FakeHeroService
|
// `.toEqual` because selectedHero is clone of expectedHero; see FakeHeroService
|
||||||
expect(comp.selectedHero).toEqual(expectedHero);
|
expect(comp.selectedHero).toEqual(expectedHero);
|
||||||
@ -62,7 +65,10 @@ describe('HeroListComponent', () => {
|
|||||||
it('should navigate to selected hero detail on click', fakeAsync(() => {
|
it('should navigate to selected hero detail on click', fakeAsync(() => {
|
||||||
const expectedHero = HEROES[1];
|
const expectedHero = HEROES[1];
|
||||||
const li = page.heroRows[1];
|
const li = page.heroRows[1];
|
||||||
li.dispatchEvent(newEvent('click'));
|
|
||||||
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
|
li.dispatchEvent(new Event('click'));
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
// should have navigated
|
// should have navigated
|
||||||
|
@ -3,7 +3,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { HighlightDirective } from './highlight.directive';
|
import { HighlightDirective } from './highlight.directive';
|
||||||
import { newEvent } from '../../testing';
|
|
||||||
|
|
||||||
// #docregion test-component
|
// #docregion test-component
|
||||||
@Component({
|
@Component({
|
||||||
@ -59,9 +58,12 @@ describe('HighlightDirective', () => {
|
|||||||
const input = des[2].nativeElement as HTMLInputElement;
|
const input = des[2].nativeElement as HTMLInputElement;
|
||||||
expect(input.style.backgroundColor).toBe('cyan', 'initial backgroundColor');
|
expect(input.style.backgroundColor).toBe('cyan', 'initial backgroundColor');
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular responds to the input value change.
|
|
||||||
input.value = 'green';
|
input.value = 'green';
|
||||||
input.dispatchEvent(newEvent('input'));
|
|
||||||
|
// Dispatch a DOM event so that Angular responds to the input value change.
|
||||||
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
|
input.dispatchEvent(new Event('input'));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(input.style.backgroundColor).toBe('green', 'changed backgroundColor');
|
expect(input.style.backgroundColor).toBe('green', 'changed backgroundColor');
|
||||||
|
@ -14,18 +14,6 @@ export function advance(f: ComponentFixture<any>): void {
|
|||||||
f.detectChanges();
|
f.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create custom DOM event the old fashioned way
|
|
||||||
*
|
|
||||||
* https://developer.mozilla.org/en-US/docs/Web/API/Event/initEvent
|
|
||||||
* Although officially deprecated, some browsers (phantom) don't accept the preferred "new Event(eventName)"
|
|
||||||
*/
|
|
||||||
export function newEvent(eventName: string, bubbles = false, cancelable = false) {
|
|
||||||
const evt = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
|
|
||||||
evt.initCustomEvent(eventName, bubbles, cancelable, null);
|
|
||||||
return evt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
||||||
// #docregion click-event
|
// #docregion click-event
|
||||||
/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */
|
/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "tslint:recommended",
|
"extends": "tslint:recommended",
|
||||||
|
"rulesDirectory": [
|
||||||
|
"codelyzer"
|
||||||
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"align": {
|
"align": {
|
||||||
"options": [
|
"options": [
|
||||||
@ -13,22 +16,6 @@
|
|||||||
"deprecation": {
|
"deprecation": {
|
||||||
"severity": "warning"
|
"severity": "warning"
|
||||||
},
|
},
|
||||||
"component-class-suffix": true,
|
|
||||||
"component-selector": [
|
|
||||||
true,
|
|
||||||
"element",
|
|
||||||
// TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense).
|
|
||||||
"",
|
|
||||||
"kebab-case"
|
|
||||||
],
|
|
||||||
"contextual-lifecycle": true,
|
|
||||||
"directive-class-suffix": true,
|
|
||||||
"directive-selector": [
|
|
||||||
true,
|
|
||||||
"attribute",
|
|
||||||
["app", "toh"],
|
|
||||||
"camelCase"
|
|
||||||
],
|
|
||||||
"eofline": true,
|
"eofline": true,
|
||||||
"import-blacklist": [
|
"import-blacklist": [
|
||||||
true,
|
true,
|
||||||
@ -56,6 +43,8 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
// TODO(gkalpak): Fix the code and enable this.
|
||||||
|
// "no-any": true,
|
||||||
"no-console": [
|
"no-console": [
|
||||||
true,
|
true,
|
||||||
"debug",
|
"debug",
|
||||||
@ -95,6 +84,11 @@
|
|||||||
"named": "never"
|
"named": "never"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// TODO(gkalpak): Fix the code and enable this.
|
||||||
|
// "typedef": [
|
||||||
|
// true,
|
||||||
|
// "call-signature"
|
||||||
|
// ],
|
||||||
"typedef-whitespace": {
|
"typedef-whitespace": {
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
@ -130,6 +124,9 @@
|
|||||||
"check-typecast"
|
"check-typecast"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"component-class-suffix": true,
|
||||||
|
"contextual-lifecycle": true,
|
||||||
|
"directive-class-suffix": true,
|
||||||
"no-conflicting-lifecycle": true,
|
"no-conflicting-lifecycle": true,
|
||||||
"no-host-metadata-property": true,
|
"no-host-metadata-property": true,
|
||||||
"no-input-rename": true,
|
"no-input-rename": true,
|
||||||
@ -141,9 +138,19 @@
|
|||||||
"template-banana-in-box": true,
|
"template-banana-in-box": true,
|
||||||
"template-no-negated-async": true,
|
"template-no-negated-async": true,
|
||||||
"use-lifecycle-interface": true,
|
"use-lifecycle-interface": true,
|
||||||
"use-pipe-transform-interface": true
|
"use-pipe-transform-interface": true,
|
||||||
},
|
"directive-selector": [
|
||||||
"rulesDirectory": [
|
true,
|
||||||
"codelyzer"
|
"attribute",
|
||||||
]
|
["app", "toh"],
|
||||||
|
"camelCase"
|
||||||
|
],
|
||||||
|
"component-selector": [
|
||||||
|
true,
|
||||||
|
"element",
|
||||||
|
// TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense).
|
||||||
|
"",
|
||||||
|
"kebab-case"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import * as angular from 'angular';
|
import * as angular from 'angular';
|
||||||
import 'angular-route';
|
import 'angular-route';
|
||||||
|
|
||||||
const appName = 'myApp';
|
const appModule = angular.module('myApp', [
|
||||||
|
|
||||||
angular.module(appName, [
|
|
||||||
'ngRoute'
|
'ngRoute'
|
||||||
])
|
])
|
||||||
.config(['$routeProvider', '$locationProvider',
|
.config(['$routeProvider', '$locationProvider',
|
||||||
@ -25,5 +23,5 @@ angular.module(appName, [
|
|||||||
);
|
);
|
||||||
|
|
||||||
export function bootstrap(el: HTMLElement) {
|
export function bootstrap(el: HTMLElement) {
|
||||||
return angular.bootstrap(el, [appName]);
|
return angular.bootstrap(el, [appModule.name]);
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ This example from the `HeroListComponent` template uses three of these forms.
|
|||||||
|
|
||||||
<code-example path="architecture/src/app/hero-list.component.1.html" header="src/app/hero-list.component.html (binding)" region="binding"></code-example>
|
<code-example path="architecture/src/app/hero-list.component.1.html" header="src/app/hero-list.component.html (binding)" region="binding"></code-example>
|
||||||
|
|
||||||
* The `{{hero.name}}` [*interpolation*](guide/displaying-data#interpolation)
|
* The `{{hero.name}}` [*interpolation*](guide/interpolation)
|
||||||
displays the component's `hero.name` property value within the `<li>` element.
|
displays the component's `hero.name` property value within the `<li>` element.
|
||||||
|
|
||||||
* The `[hero]` [*property binding*](guide/property-binding) passes the value of
|
* The `[hero]` [*property binding*](guide/property-binding) passes the value of
|
||||||
@ -166,8 +166,8 @@ The example template uses two built-in structural directives to add application
|
|||||||
|
|
||||||
<code-example path="architecture/src/app/hero-list.component.1.html" header="src/app/hero-list.component.html (structural)" region="structural"></code-example>
|
<code-example path="architecture/src/app/hero-list.component.1.html" header="src/app/hero-list.component.html (structural)" region="structural"></code-example>
|
||||||
|
|
||||||
* [`*ngFor`](guide/displaying-data#ngFor) is an iterative; it tells Angular to stamp out one `<li>` per hero in the `heroes` list.
|
* [`*ngFor`](guide/structural-directives#inside-ngfor) is an iterative; it tells Angular to stamp out one `<li>` per hero in the `heroes` list.
|
||||||
* [`*ngIf`](guide/displaying-data#ngIf) is a conditional; it includes the `HeroDetail` component only if a selected hero exists.
|
* [`*ngIf`](guide/structural-directives#ngif-case-study) is a conditional; it includes the `HeroDetail` component only if a selected hero exists.
|
||||||
|
|
||||||
#### Attribute directives
|
#### Attribute directives
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ about the features and tools that can help you develop and deliver Angular appli
|
|||||||
|
|
||||||
## Application architecture
|
## Application architecture
|
||||||
|
|
||||||
* The [Components and templates](guide/displaying-data) guide explains how to connect the application data in your [components](guide/glossary#component) to your page-display [templates](guide/glossary#template), to create a complete interactive application.
|
* The **Main Concepts** section located in the table of contents contains several topics that explain how to connect the application data in your [components](guide/glossary#component) to your page-display [templates](guide/glossary#template), to create a complete interactive application.
|
||||||
|
|
||||||
* The [NgModules](guide/ngmodules) guide provides in-depth information on the modular structure of an Angular application.
|
* The [NgModules](guide/ngmodules) guide provides in-depth information on the modular structure of an Angular application.
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ about the features and tools that can help you develop and deliver Angular appli
|
|||||||
|
|
||||||
## Responsive programming
|
## Responsive programming
|
||||||
|
|
||||||
The **Components and Templates** guide provides guidance and details of the [template syntax](guide/template-syntax) that you use to display your component data when and where you want it within a view, and to collect input from users that you can respond to.
|
The [template syntax](guide/template-syntax) and related topics contain details about how to display your component data when and where you want it within a view, and how to collect input from users that you can respond to.
|
||||||
|
|
||||||
Additional pages and sections describe some basic programming techniques for Angular apps.
|
Additional pages and sections describe some basic programming techniques for Angular apps.
|
||||||
|
|
||||||
@ -52,8 +52,6 @@ For some platforms and applications, you might also want to use the PWA (Progres
|
|||||||
|
|
||||||
## Support for the development cycle
|
## Support for the development cycle
|
||||||
|
|
||||||
The **Development Workflow** section describes the tools and processes you use to compile, test, and deploy Angular applications.
|
|
||||||
|
|
||||||
* [CLI Command Reference](cli): The Angular CLI is a command-line tool that you use to create projects, generate application and library code, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
|
* [CLI Command Reference](cli): The Angular CLI is a command-line tool that you use to create projects, generate application and library code, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
|
||||||
|
|
||||||
* [Compilation](guide/aot-compiler): Angular provides just-in-time (JIT) compilation for the development environment, and ahead-of-time (AOT) compilation for the production environment.
|
* [Compilation](guide/aot-compiler): Angular provides just-in-time (JIT) compilation for the development environment, and ahead-of-time (AOT) compilation for the production environment.
|
||||||
@ -68,7 +66,6 @@ The **Development Workflow** section describes the tools and processes you use t
|
|||||||
|
|
||||||
* [Accessibility](guide/accessibility): Make your app accessible to all users.
|
* [Accessibility](guide/accessibility): Make your app accessible to all users.
|
||||||
|
|
||||||
|
|
||||||
## File structure, configuration, and dependencies
|
## File structure, configuration, and dependencies
|
||||||
|
|
||||||
* [Workspace and file structure](guide/file-structure): Understand the structure of Angular workspace and project folders.
|
* [Workspace and file structure](guide/file-structure): Understand the structure of Angular workspace and project folders.
|
||||||
|
@ -95,7 +95,7 @@ or in the `@NgModule()` or `@Component()` metadata
|
|||||||
Registering the provider in the `@Injectable()` metadata also allows Angular to optimize an app
|
Registering the provider in the `@Injectable()` metadata also allows Angular to optimize an app
|
||||||
by removing the service from the compiled app if it isn't used.
|
by removing the service from the compiled app if it isn't used.
|
||||||
|
|
||||||
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator,
|
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator.
|
||||||
|
|
||||||
```
|
```
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# Attribute, class, and style bindings
|
# Attribute, class, and style bindings
|
||||||
|
|
||||||
The template syntax provides specialized one-way bindings for scenarios less well-suited to property binding.
|
Attribute binding in Angular helps you set values for attributes directly.
|
||||||
|
With attribute binding, you can improve accessibility, style your application dynamically, and manage multiple CSS classes or styles simultaneously.
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
@ -8,23 +9,36 @@ See the <live-example></live-example> for a working example containing the code
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
## Binding to an attribute
|
||||||
|
|
||||||
## Attribute binding
|
It is recommended that you set an element property with a [property binding](guide/property-binding) whenever possible.
|
||||||
|
However, sometimes you don't have an element property to bind.
|
||||||
|
In those situations, you can use attribute binding.
|
||||||
|
|
||||||
Set the value of an attribute directly with an **attribute binding**. This is the only exception to the rule that a binding sets a target property and the only binding that creates and sets an attribute.
|
For example, [ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) and
|
||||||
|
[SVG](https://developer.mozilla.org/en-US/docs/Web/SVG) are purely attributes.
|
||||||
|
Neither ARIA nor SVG correspond to element properties and don't set element properties.
|
||||||
|
In these cases, you must use attribute binding because there are no corresponding property targets.
|
||||||
|
|
||||||
Usually, setting an element property with a [property binding](guide/property-binding)
|
|
||||||
is preferable to setting the attribute with a string. However, sometimes
|
|
||||||
there is no element property to bind, so attribute binding is the solution.
|
|
||||||
|
|
||||||
Consider the [ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) and
|
## Syntax
|
||||||
[SVG](https://developer.mozilla.org/en-US/docs/Web/SVG). They are purely attributes, don't correspond to element properties, and don't set element properties. In these cases, there are no property targets to bind to.
|
|
||||||
|
|
||||||
Attribute binding syntax resembles property binding, but
|
Attribute binding syntax resembles [property binding](guide/property-binding), but instead of an element property between brackets, you precede the name of the attribute with the prefix `attr`, followed by a dot.
|
||||||
instead of an element property between brackets, start with the prefix `attr`,
|
Then, you set the attribute value with an expression that resolves to a string.
|
||||||
followed by a dot (`.`), and the name of the attribute.
|
|
||||||
You then set the attribute value, using an expression that resolves to a string,
|
<code-example language="html">
|
||||||
or remove the attribute when the expression resolves to `null`.
|
|
||||||
|
<p [attr.attribute-you-are-targeting]="expression"></p>
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
When the expression resolves to `null`, Angular removes the attribute altogether.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Binding ARIA attributes
|
||||||
|
|
||||||
One of the primary use cases for attribute binding
|
One of the primary use cases for attribute binding
|
||||||
is to set ARIA attributes, as in this example:
|
is to set ARIA attributes, as in this example:
|
||||||
@ -33,32 +47,30 @@ is to set ARIA attributes, as in this example:
|
|||||||
|
|
||||||
{@a colspan}
|
{@a colspan}
|
||||||
|
|
||||||
|
## Binding to `colspan`
|
||||||
|
|
||||||
|
Another common use case for attribute binding is with the `colspan` attribute in tables.
|
||||||
|
Binding to the `colspan` attribute helps you keep your tables programmatically dynamic.
|
||||||
|
Depending on the amount of data that your application populates a table with, the number of columns that a row spans could change.
|
||||||
|
|
||||||
|
To use attribute binding with the `<td>` attribute `colspan`:
|
||||||
|
|
||||||
|
1. Specify the `colspan` attribute by using the following syntax: `[attr.colspan]`.
|
||||||
|
1. Set `[attr.colspan]` equal to an expression.
|
||||||
|
|
||||||
|
In the following example, binds the `colspan` attribute to the expression `1 + 1`.
|
||||||
|
|
||||||
|
<code-example path="attribute-binding/src/app/app.component.html" region="colspan" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
|
This binding causes the `<tr>` to span two columns.
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
#### `colspan` and `colSpan`
|
Sometimes there are differences between the name of property and an attribute.
|
||||||
|
|
||||||
Notice the difference between the `colspan` attribute and the `colSpan` property.
|
`colspan` is an attribute of `<tr>`, while `colSpan` with a capital "S" is a property.
|
||||||
|
When using attribute binding, use `colspan` with a lowercase "s".
|
||||||
If you wrote something like this:
|
For more information on how to bind to the `colSpan` property, see the [`colspan` and `colSpan`](guide/property-binding#colspan) section of [Property Binding](guide/property-binding).
|
||||||
|
|
||||||
<code-example language="html">
|
|
||||||
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
You'd get this error:
|
|
||||||
|
|
||||||
<code-example language="bash">
|
|
||||||
Template parse errors:
|
|
||||||
Can't bind to 'colspan' since it isn't a known native property
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
As the message says, the `<td>` element does not have a `colspan` property. This is true
|
|
||||||
because `colspan` is an attribute—`colSpan`, with a capital `S`, is the
|
|
||||||
corresponding property. Interpolation and property binding can set only *properties*, not attributes.
|
|
||||||
|
|
||||||
Instead, you'd use property binding and write it like this:
|
|
||||||
|
|
||||||
<code-example path="attribute-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html"></code-example>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -66,28 +78,32 @@ Instead, you'd use property binding and write it like this:
|
|||||||
|
|
||||||
{@a class-binding}
|
{@a class-binding}
|
||||||
|
|
||||||
## Class binding
|
## Binding to the `class` attribute
|
||||||
|
|
||||||
Here's how to set the `class` attribute without a binding in plain HTML:
|
You can use class binding to add and remove CSS class names from an element's `class` attribute.
|
||||||
|
|
||||||
```html
|
### Binding to a single CSS `class`
|
||||||
<!-- standard class attribute setting -->
|
|
||||||
<div class="foo bar">Some text</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also add and remove CSS class names from an element's `class` attribute with a **class binding**.
|
To create a single class binding, use the prefix `class` followed by a dot and the name of the CSS class—for example, `[class.sale]="onSale"`.
|
||||||
|
Angular adds the class when the bound expression, `onSale` is truthy, and it removes the class when the expression is falsy—with the exception of `undefined`.
|
||||||
|
See [styling delegation](guide/style-precedence#styling-delegation) for more information.
|
||||||
|
|
||||||
To create a single class binding, start with the prefix `class` followed by a dot (`.`) and the name of the CSS class (for example, `[class.foo]="hasFoo"`).
|
### Binding to multiple CSS classes
|
||||||
Angular adds the class when the bound expression is truthy, and it removes the class when the expression is falsy (with the exception of `undefined`, see [styling delegation](#styling-delegation)).
|
|
||||||
|
|
||||||
To create a binding to multiple classes, use a generic `[class]` binding without the dot (for example, `[class]="classExpr"`).
|
To bind to multiple classes, use `[class]` set to an expression—for example, `[class]="classExpression"`.
|
||||||
The expression can be a space-delimited string of class names, or you can format it as an object with class names as the keys and truthy/falsy expressions as the values.
|
The expression can be a space-delimited string of class names, or an object with class names as the keys and truthy or falsy expressions as the values.
|
||||||
With object format, Angular will add a class only if its associated value is truthy.
|
With an object format, Angular adds a class only if its associated value is truthy.
|
||||||
|
|
||||||
It's important to note that with any object-like expression (`object`, `Array`, `Map`, `Set`, etc), the identity of the object must change for the class list to be updated.
|
<div class="alert is-important">
|
||||||
Updating the property without changing object identity will have no effect.
|
|
||||||
|
|
||||||
If there are multiple bindings to the same class name, conflicts are resolved using [styling precedence](#styling-precedence).
|
With any object-like expression—such as `object`, `Array`, `Map`, or `Set`—the identity of the object must change for Angular to update the class list.
|
||||||
|
Updating the property without changing object identity has no effect.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
If there are multiple bindings to the same class name, Angular uses [styling precedence](guide/style-precedence) to determine which binding to use.
|
||||||
|
|
||||||
|
The following table summarizes class binding syntax.
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
td, th {vertical-align: top}
|
td, th {vertical-align: top}
|
||||||
@ -118,13 +134,13 @@ If there are multiple bindings to the same class name, conflicts are resolved us
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Single class binding</td>
|
<td>Single class binding</td>
|
||||||
<td><code>[class.foo]="hasFoo"</code></td>
|
<td><code>[class.sale]="onSale"</code></td>
|
||||||
<td><code>boolean | undefined | null</code></td>
|
<td><code>boolean | undefined | null</code></td>
|
||||||
<td><code>true</code>, <code>false</code></td>
|
<td><code>true</code>, <code>false</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td rowspan=3>Multi-class binding</td>
|
<td rowspan=3>Multi-class binding</td>
|
||||||
<td rowspan=3><code>[class]="classExpr"</code></td>
|
<td rowspan=3><code>[class]="classExpression"</code></td>
|
||||||
<td><code>string</code></td>
|
<td><code>string</code></td>
|
||||||
<td><code>"my-class-1 my-class-2 my-class-3"</code></td>
|
<td><code>"my-class-1 my-class-2 my-class-3"</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -138,44 +154,44 @@ If there are multiple bindings to the same class name, conflicts are resolved us
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
The [NgClass](guide/built-in-directives/#ngclass) directive can be used as an alternative to direct `[class]` bindings.
|
|
||||||
However, using the above class binding syntax without `NgClass` is preferred because due to improvements in class binding in Angular, `NgClass` no longer provides significant value, and might eventually be removed in the future.
|
|
||||||
|
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
## Style binding
|
{@a style-binding}
|
||||||
|
|
||||||
Here's how to set the `style` attribute without a binding in plain HTML:
|
## Binding to the style attribute
|
||||||
|
|
||||||
```html
|
You can use style binding to set styles dynamically.
|
||||||
<!-- standard style attribute setting -->
|
|
||||||
<div style="color: blue">Some text</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also set styles dynamically with a **style binding**.
|
### Binding to a single style
|
||||||
|
|
||||||
To create a single style binding, start with the prefix `style` followed by a dot (`.`) and the name of the CSS style property (for example, `[style.width]="width"`).
|
To create a single style binding, use the prefix `style` followed by a dot and the name of the CSS style property—for example, `[style.width]="width"`.
|
||||||
The property will be set to the value of the bound expression, which is normally a string.
|
Angular sets the property to the value of the bound expression, which is usually a string.
|
||||||
Optionally, you can add a unit extension like `em` or `%`, which requires a number type.
|
Optionally, you can add a unit extension like `em` or `%`, which requires a number type.
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
Note that a _style property_ name can be written in either
|
You can write a style property name in either [dash-case](guide/glossary#dash-case), or
|
||||||
[dash-case](guide/glossary#dash-case), as shown above, or
|
[camelCase](guide/glossary#camelcase).
|
||||||
[camelCase](guide/glossary#camelcase), such as `fontSize`.
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
If there are multiple styles you'd like to toggle, you can bind to the `[style]` property directly without the dot (for example, `[style]="styleExpr"`).
|
### Binding to multiple styles
|
||||||
The expression attached to the `[style]` binding is most often a string list of styles like `"width: 100px; height: 100px;"`.
|
|
||||||
|
|
||||||
You can also format the expression as an object with style names as the keys and style values as the values, like `{width: '100px', height: '100px'}`.
|
To toggle multiple styles, bind to the `[style]` attribute—for example, `[style]="styleExpression"`).
|
||||||
It's important to note that with any object-like expression (`object`, `Array`, `Map`, `Set`, etc), the identity of the object must change for the class list to be updated.
|
The expression is often a string list of styles such as `"width: 100px; height: 100px;"`.
|
||||||
Updating the property without changing object identity will have no effect.
|
|
||||||
|
|
||||||
If there are multiple bindings to the same style property, conflicts are resolved using [styling precedence rules](#styling-precedence).
|
You can also format the expression as an object with style names as the keys and style values as the values, such as `{width: '100px', height: '100px'}`.
|
||||||
|
|
||||||
|
<div class="alert is-important">
|
||||||
|
|
||||||
|
With any object-like expression—such as `object`, `Array`, `Map`, or `Set`—the identity of the object must change for Angular to update the class list.
|
||||||
|
Updating the property without changing object identity has no effect.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
If there are multiple bindings to the same style attribute, Angular uses [styling precedence](guide/style-precedence) to determine which binding to use.
|
||||||
|
|
||||||
|
The following table summarizes style binding syntax.
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
td, th {vertical-align: top}
|
td, th {vertical-align: top}
|
||||||
@ -219,7 +235,7 @@ If there are multiple bindings to the same style property, conflicts are resolve
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td rowspan=3>Multi-style binding</td>
|
<td rowspan=3>Multi-style binding</td>
|
||||||
<td rowspan=3><code>[style]="styleExpr"</code></td>
|
<td rowspan=3><code>[style]="styleExpression"</code></td>
|
||||||
<td><code>string</code></td>
|
<td><code>string</code></td>
|
||||||
<td><code>"width: 100px; height: 100px"</code></td>
|
<td><code>"width: 100px; height: 100px"</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -232,72 +248,3 @@ If there are multiple bindings to the same style property, conflicts are resolve
|
|||||||
<td><code>['width', '100px']</code></td>
|
<td><code>['width', '100px']</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
The [NgStyle](guide/built-in-directives/#ngstyle) directive can be used as an alternative to direct `[style]` bindings.
|
|
||||||
However, using the above style binding syntax without `NgStyle` is preferred because due to improvements in style binding in Angular, `NgStyle` no longer provides significant value, and might eventually be removed in the future.
|
|
||||||
|
|
||||||
|
|
||||||
<hr/>
|
|
||||||
|
|
||||||
{@a styling-precedence}
|
|
||||||
|
|
||||||
## Styling Precedence
|
|
||||||
|
|
||||||
A single HTML element can have its CSS class list and style values bound to multiple sources (for example, host bindings from multiple directives).
|
|
||||||
|
|
||||||
When there are multiple bindings to the same class name or style property, Angular uses a set of precedence rules to resolve conflicts and determine which classes or styles are ultimately applied to the element.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
<h4>Styling precedence (highest to lowest)</h4>
|
|
||||||
|
|
||||||
1. Template bindings
|
|
||||||
1. Property binding (for example, `<div [class.foo]="hasFoo">` or `<div [style.color]="color">`)
|
|
||||||
1. Map binding (for example, `<div [class]="classExpr">` or `<div [style]="styleExpr">`)
|
|
||||||
1. Static value (for example, `<div class="foo">` or `<div style="color: blue">`)
|
|
||||||
1. Directive host bindings
|
|
||||||
1. Property binding (for example, `host: {'[class.foo]': 'hasFoo'}` or `host: {'[style.color]': 'color'}`)
|
|
||||||
1. Map binding (for example, `host: {'[class]': 'classExpr'}` or `host: {'[style]': 'styleExpr'}`)
|
|
||||||
1. Static value (for example, `host: {'class': 'foo'}` or `host: {'style': 'color: blue'}`)
|
|
||||||
1. Component host bindings
|
|
||||||
1. Property binding (for example, `host: {'[class.foo]': 'hasFoo'}` or `host: {'[style.color]': 'color'}`)
|
|
||||||
1. Map binding (for example, `host: {'[class]': 'classExpr'}` or `host: {'[style]': 'styleExpr'}`)
|
|
||||||
1. Static value (for example, `host: {'class': 'foo'}` or `host: {'style': 'color: blue'}`)
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
The more specific a class or style binding is, the higher its precedence.
|
|
||||||
|
|
||||||
A binding to a specific class (for example, `[class.foo]`) will take precedence over a generic `[class]` binding, and a binding to a specific style (for example, `[style.bar]`) will take precedence over a generic `[style]` binding.
|
|
||||||
|
|
||||||
<code-example path="attribute-binding/src/app/app.component.html" region="basic-specificity" header="src/app/app.component.html"></code-example>
|
|
||||||
|
|
||||||
Specificity rules also apply when it comes to bindings that originate from different sources.
|
|
||||||
It's possible for an element to have bindings in the template where it's declared, from host bindings on matched directives, and from host bindings on matched components.
|
|
||||||
|
|
||||||
Template bindings are the most specific because they apply to the element directly and exclusively, so they have the highest precedence.
|
|
||||||
|
|
||||||
Directive host bindings are considered less specific because directives can be used in multiple locations, so they have a lower precedence than template bindings.
|
|
||||||
|
|
||||||
Directives often augment component behavior, so host bindings from components have the lowest precedence.
|
|
||||||
|
|
||||||
<code-example path="attribute-binding/src/app/app.component.html" region="source-specificity" header="src/app/app.component.html"></code-example>
|
|
||||||
|
|
||||||
In addition, bindings take precedence over static attributes.
|
|
||||||
|
|
||||||
In the following case, `class` and `[class]` have similar specificity, but the `[class]` binding will take precedence because it is dynamic.
|
|
||||||
|
|
||||||
<code-example path="attribute-binding/src/app/app.component.html" region="dynamic-priority" header="src/app/app.component.html"></code-example>
|
|
||||||
|
|
||||||
{@a styling-delegation}
|
|
||||||
### Delegating to styles with lower precedence
|
|
||||||
|
|
||||||
It is possible for higher precedence styles to "delegate" to lower precedence styles using `undefined` values.
|
|
||||||
Whereas setting a style property to `null` ensures the style is removed, setting it to `undefined` will cause Angular to fall back to the next-highest precedence binding to that style.
|
|
||||||
|
|
||||||
For example, consider the following template:
|
|
||||||
|
|
||||||
<code-example path="attribute-binding/src/app/app.component.html" region="style-delegation" header="src/app/app.component.html"></code-example>
|
|
||||||
|
|
||||||
Imagine that the `dirWithHostBinding` directive and the `comp-with-host-binding` component both have a `[style.width]` host binding.
|
|
||||||
In that case, if `dirWithHostBinding` sets its binding to `undefined`, the `width` property will fall back to the value of the `comp-with-host-binding` host binding.
|
|
||||||
However, if `dirWithHostBinding` sets its binding to `null`, the `width` property will be removed entirely.
|
|
||||||
|
@ -18,8 +18,6 @@ When you use the [Angular CLI](cli) command `ng new` to generate an app, the def
|
|||||||
/* JavaScript imports */
|
/* JavaScript imports */
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
@ -29,9 +27,7 @@ import { AppComponent } from './app.component';
|
|||||||
AppComponent
|
AppComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule
|
||||||
FormsModule,
|
|
||||||
HttpClientModule
|
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
@ -120,9 +116,6 @@ Now you could use your `ItemDirective` in a component. This example uses `AppMod
|
|||||||
|
|
||||||
Remember, components, directives, and pipes belong to one module only. You only need to declare them once in your app because you share them by importing the necessary modules. This saves you time and helps keep your app lean.
|
Remember, components, directives, and pipes belong to one module only. You only need to declare them once in your app because you share them by importing the necessary modules. This saves you time and helps keep your app lean.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a imports}
|
{@a imports}
|
||||||
|
|
||||||
## The `imports` array
|
## The `imports` array
|
||||||
@ -130,6 +123,12 @@ Remember, components, directives, and pipes belong to one module only. You only
|
|||||||
The module's `imports` array appears exclusively in the `@NgModule` metadata object.
|
The module's `imports` array appears exclusively in the `@NgModule` metadata object.
|
||||||
It tells Angular about other NgModules that this particular module needs to function properly.
|
It tells Angular about other NgModules that this particular module needs to function properly.
|
||||||
|
|
||||||
|
<code-example
|
||||||
|
path="bootstrapping/src/app/app.module.ts"
|
||||||
|
region="imports"
|
||||||
|
header="src/app/app.module.ts (excerpt)">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
This list of modules are those that export components, directives, or pipes
|
This list of modules are those that export components, directives, or pipes
|
||||||
that component templates in this module reference. In this case, the component is
|
that component templates in this module reference. In this case, the component is
|
||||||
`AppComponent`, which references components, directives, or pipes in `BrowserModule`,
|
`AppComponent`, which references components, directives, or pipes in `BrowserModule`,
|
||||||
@ -138,6 +137,8 @@ A component template can reference another component, directive,
|
|||||||
or pipe when the referenced class is declared in this module or
|
or pipe when the referenced class is declared in this module or
|
||||||
the class was imported from another module.
|
the class was imported from another module.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a bootstrap-array}
|
{@a bootstrap-array}
|
||||||
|
|
||||||
## The `providers` array
|
## The `providers` array
|
||||||
|
@ -53,18 +53,7 @@ Angular supports most recent browsers. This includes the following specific vers
|
|||||||
IE
|
IE
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div> 11, 10*, 9* ("compatibility view" mode not supported) </div>
|
<div>11</div>
|
||||||
<div>*deprecated in v10, see the {@link guide/deprecations#ie-9-10-and-mobile deprecations guide}.</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
IE Mobile*
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
11
|
|
||||||
<div>*deprecated in v10, see the {@link guide/deprecations#ie-9-10-and-mobile deprecations guide}.</div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -185,7 +174,7 @@ These are the polyfills required to run an Angular application on each supported
|
|||||||
|
|
||||||
<td>
|
<td>
|
||||||
Chrome, Firefox, Edge, <br>
|
Chrome, Firefox, Edge, <br>
|
||||||
Safari, Android, IE 10+
|
Safari, Android, IE 11
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
@ -196,20 +185,6 @@ These are the polyfills required to run an Angular application on each supported
|
|||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr style="vertical-align: top">
|
|
||||||
|
|
||||||
<td>
|
|
||||||
IE 9
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
ES2015<br>[classList](guide/browser-support#classlist)
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
@ -272,30 +247,7 @@ Some features of Angular may require additional polyfills.
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
IE 10, IE 11
|
IE 11
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr style="vertical-align: top">
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
[Http](guide/http) when sending and receiving binary data
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
[Typed Array](guide/browser-support#typedarray)<br>
|
|
||||||
|
|
||||||
[Blob](guide/browser-support#blob)<br>
|
|
||||||
|
|
||||||
[FormData](guide/browser-support#formdata)
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
IE 9
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
@ -437,60 +389,6 @@ The following polyfills are used to test the framework itself. They are a good s
|
|||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
<a id='typedarray' href="https://github.com/inexorabletash/polyfill/blob/master/typedarray.js">Typed Array</a>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
MIT
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
4KB
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
<a id='blob' href="https://github.com/eligrey/Blob.js">Blob</a>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
MIT
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
1.3KB
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
<a id='formdata' href="https://github.com/francois2metz/html5-formdata">FormData</a>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
MIT
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
0.4KB
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ which explains the following:
|
|||||||
* Using [`<ng-container>`](guide/structural-directives#ngcontainer "<ng-container>")
|
* Using [`<ng-container>`](guide/structural-directives#ngcontainer "<ng-container>")
|
||||||
to group elements when there is no suitable host element for the directive.
|
to group elements when there is no suitable host element for the directive.
|
||||||
* How to write your own structural directive.
|
* How to write your own structural directive.
|
||||||
* That you can only apply [one structural directive](guide/structural-directives#one-per-element "one per host element") to an element.
|
* Why you [can only apply one structural directive](guide/structural-directives#one-per-element "one per host element") to an element.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -281,7 +281,7 @@ You define a block of HTML that defines how a single item should be displayed
|
|||||||
and then you tell Angular to use that block as a template for rendering each item in the list.
|
and then you tell Angular to use that block as a template for rendering each item in the list.
|
||||||
The text assigned to `*ngFor` is the instruction that guides the repeater process.
|
The text assigned to `*ngFor` is the instruction that guides the repeater process.
|
||||||
|
|
||||||
The following example shows `NgFor` applied to a simple `<div>`. (Don't forget the asterisk (`*`) in front of `ngFor`.)
|
The following example shows `NgFor` applied to a simple `<div>`.
|
||||||
|
|
||||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1" header="src/app/app.component.html"></code-example>
|
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ The `/deep/` combinator also has the aliases `>>>`, and `::ng-deep`.
|
|||||||
|
|
||||||
Use `/deep/`, `>>>` and `::ng-deep` only with *emulated* view encapsulation.
|
Use `/deep/`, `>>>` and `::ng-deep` only with *emulated* view encapsulation.
|
||||||
Emulated is the default and most commonly used view encapsulation. For more information, see the
|
Emulated is the default and most commonly used view encapsulation. For more information, see the
|
||||||
[Controlling view encapsulation](guide/component-styles#view-encapsulation) section.
|
[View Encapsulation](guide/view-encapsulation) section.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -267,89 +267,3 @@ as explained in the [CLI wiki](https://github.com/angular/angular-cli/wiki/stori
|
|||||||
Style strings added to the `@Component.styles` array _must be written in CSS_ because the CLI cannot apply a preprocessor to inline styles.
|
Style strings added to the `@Component.styles` array _must be written in CSS_ because the CLI cannot apply a preprocessor to inline styles.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{@a view-encapsulation}
|
|
||||||
|
|
||||||
## View encapsulation
|
|
||||||
|
|
||||||
As discussed earlier, component CSS styles are encapsulated into the component's view and don't
|
|
||||||
affect the rest of the application.
|
|
||||||
|
|
||||||
To control how this encapsulation happens on a *per
|
|
||||||
component* basis, you can set the *view encapsulation mode* in the component metadata.
|
|
||||||
Choose from the following modes:
|
|
||||||
|
|
||||||
* `ShadowDom` view encapsulation uses the browser's native shadow DOM implementation (see
|
|
||||||
[Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM)
|
|
||||||
on the [MDN](https://developer.mozilla.org) site)
|
|
||||||
to attach a shadow DOM to the component's host element, and then puts the component
|
|
||||||
view inside that shadow DOM. The component's styles are included within the shadow DOM.
|
|
||||||
|
|
||||||
* `Native` view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - [learn about the changes](https://hayato.io/2016/shadowdomv1/).
|
|
||||||
|
|
||||||
* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing
|
|
||||||
(and renaming) the CSS code to effectively scope the CSS to the component's view.
|
|
||||||
For details, see [Inspecting generated CSS](guide/component-styles#inspect-generated-css) below.
|
|
||||||
|
|
||||||
* `None` means that Angular does no view encapsulation.
|
|
||||||
Angular adds the CSS to the global styles.
|
|
||||||
The scoping rules, isolations, and protections discussed earlier don't apply.
|
|
||||||
This is essentially the same as pasting the component's styles into the HTML.
|
|
||||||
|
|
||||||
To set the components encapsulation mode, use the `encapsulation` property in the component metadata:
|
|
||||||
|
|
||||||
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" header="src/app/quest-summary.component.ts"></code-example>
|
|
||||||
|
|
||||||
`ShadowDom` view encapsulation only works on browsers that have native support
|
|
||||||
for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the
|
|
||||||
[Can I use](http://caniuse.com) site). The support is still limited,
|
|
||||||
which is why `Emulated` view encapsulation is the default mode and recommended
|
|
||||||
in most cases.
|
|
||||||
|
|
||||||
{@a inspect-generated-css}
|
|
||||||
|
|
||||||
## Inspecting generated CSS
|
|
||||||
|
|
||||||
When using emulated view encapsulation, Angular preprocesses
|
|
||||||
all component styles so that they approximate the standard shadow CSS scoping rules.
|
|
||||||
|
|
||||||
In the DOM of a running Angular application with emulated view
|
|
||||||
encapsulation enabled, each DOM element has some extra attributes
|
|
||||||
attached to it:
|
|
||||||
|
|
||||||
<code-example format="">
|
|
||||||
<hero-details _nghost-pmm-5>
|
|
||||||
<h2 _ngcontent-pmm-5>Mister Fantastic</h2>
|
|
||||||
<hero-team _ngcontent-pmm-5 _nghost-pmm-6>
|
|
||||||
<h3 _ngcontent-pmm-6>Team</h3>
|
|
||||||
</hero-team>
|
|
||||||
</hero-detail>
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
There are two kinds of generated attributes:
|
|
||||||
|
|
||||||
* An element that would be a shadow DOM host in native encapsulation has a
|
|
||||||
generated `_nghost` attribute. This is typically the case for component host elements.
|
|
||||||
* An element within a component's view has a `_ngcontent` attribute
|
|
||||||
that identifies to which host's emulated shadow DOM this element belongs.
|
|
||||||
|
|
||||||
The exact values of these attributes aren't important. They are automatically
|
|
||||||
generated and you never refer to them in application code. But they are targeted
|
|
||||||
by the generated component styles, which are in the `<head>` section of the DOM:
|
|
||||||
|
|
||||||
<code-example format="">
|
|
||||||
[_nghost-pmm-5] {
|
|
||||||
display: block;
|
|
||||||
border: 1px solid black;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3[_ngcontent-pmm-6] {
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #777;
|
|
||||||
}
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
These styles are post-processed so that each selector is augmented
|
|
||||||
with `_nghost` or `_ngcontent` attribute selectors.
|
|
||||||
These extra selectors enable the scoping rules described in this page.
|
|
||||||
|
@ -156,7 +156,7 @@ The library must be rebuilt on every change.
|
|||||||
When linking a library, make sure that the build step runs in watch mode, and that the library's `package.json` configuration points at the correct entry points.
|
When linking a library, make sure that the build step runs in watch mode, and that the library's `package.json` configuration points at the correct entry points.
|
||||||
For example, `main` should point at a JavaScript file, not a TypeScript file.
|
For example, `main` should point at a JavaScript file, not a TypeScript file.
|
||||||
|
|
||||||
## Use TypeScript path mapping for peer dependencies
|
### Use TypeScript path mapping for peer dependencies
|
||||||
|
|
||||||
Angular libraries should list all `@angular/*` dependencies as peer dependencies.
|
Angular libraries should list all `@angular/*` dependencies as peer dependencies.
|
||||||
This ensures that when modules ask for Angular, they all get the exact same module.
|
This ensures that when modules ask for Angular, they all get the exact same module.
|
||||||
|
@ -97,7 +97,7 @@ For the simplest deployment, create a production build and copy the output direc
|
|||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
2. Copy _everything_ within the output folder (`dist/` by default) to a folder on the server.
|
2. Copy _everything_ within the output folder (`dist/project-name/` by default) to a folder on the server.
|
||||||
|
|
||||||
3. Configure the server to redirect requests for missing files to `index.html`.
|
3. Configure the server to redirect requests for missing files to `index.html`.
|
||||||
Learn more about server-side redirects [below](#fallback).
|
Learn more about server-side redirects [below](#fallback).
|
||||||
@ -211,11 +211,11 @@ modified to serve `index.html`:
|
|||||||
# .
|
# .
|
||||||
# -- server.rb
|
# -- server.rb
|
||||||
# -- public
|
# -- public
|
||||||
# |-- dist
|
# |-- project-name
|
||||||
# |-- index.html
|
# |-- index.html
|
||||||
|
|
||||||
get '/' do
|
get '/' do
|
||||||
folderDir = settings.public_folder + '/dist' # ng build output folder
|
folderDir = settings.public_folder + '/project-name' # ng build output folder
|
||||||
send_file File.join(folderDir, 'index.html')
|
send_file File.join(folderDir, 'index.html')
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
@ -383,11 +383,11 @@ Build your app for production _including the source maps_
|
|||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
List the generated bundles in the `dist/` folder.
|
List the generated bundles in the `dist/project-name/` folder.
|
||||||
|
|
||||||
<code-example language="none" class="code-shell">
|
<code-example language="none" class="code-shell">
|
||||||
|
|
||||||
ls dist/*.bundle.js
|
ls dist/project-name/*.js
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
@ -396,7 +396,7 @@ The following example displays the graph for the _main_ bundle.
|
|||||||
|
|
||||||
<code-example language="none" class="code-shell">
|
<code-example language="none" class="code-shell">
|
||||||
|
|
||||||
node_modules/.bin/source-map-explorer dist/main.*.bundle.js
|
node_modules/.bin/source-map-explorer dist/project-name/main*
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
@ -530,7 +530,7 @@ last 2 Edge major versions
|
|||||||
last 2 Safari major versions
|
last 2 Safari major versions
|
||||||
last 2 iOS major versions
|
last 2 iOS major versions
|
||||||
Firefox ESR
|
Firefox ESR
|
||||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
<code-example language="json" header="tsconfig.json">
|
<code-example language="json" header="tsconfig.json">
|
||||||
|
@ -41,14 +41,11 @@ v9 - v12
|
|||||||
| `@angular/core` | [`DefaultIterableDiffer`](#core) | <!--v7--> v11 |
|
| `@angular/core` | [`DefaultIterableDiffer`](#core) | <!--v7--> v11 |
|
||||||
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v11 |
|
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v11 |
|
||||||
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v11 |
|
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v11 |
|
||||||
| `@angular/core` | [`ViewEncapsulation.Native`](#core) | <!--v6--> v11 |
|
|
||||||
| `@angular/core` | [`WrappedValue`](#core) | <!--v10--> v12 |
|
| `@angular/core` | [`WrappedValue`](#core) | <!--v10--> v12 |
|
||||||
| `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | <!--v6--> v11 |
|
| `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | <!--v6--> v11 |
|
||||||
| `@angular/router` | [`preserveQueryParams`](#router) | <!--v7--> v11 |
|
|
||||||
| `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | <!--v8--> v11 |
|
| `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | <!--v8--> v11 |
|
||||||
| `@angular/upgrade` | [`getAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
| `@angular/upgrade` | [`getAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
||||||
| `@angular/upgrade` | [`setAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
| `@angular/upgrade` | [`setAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
||||||
| `@angular/platform-webworker` | [All entry points](api/platform-webworker) | <!--v8--> v11 |
|
|
||||||
| template syntax | [`<template`>](#template-tag) | <!--v7--> v11 |
|
| template syntax | [`<template`>](#template-tag) | <!--v7--> v11 |
|
||||||
| polyfills | [reflect-metadata](#reflect-metadata) | <!--v8--> v11 |
|
| polyfills | [reflect-metadata](#reflect-metadata) | <!--v8--> v11 |
|
||||||
| npm package format | [`esm5` and `fesm5` entry-points in @angular/* npm packages](guide/deprecations#esm5-fesm5) | <!-- v9 --> v11 |
|
| npm package format | [`esm5` and `fesm5` entry-points in @angular/* npm packages](guide/deprecations#esm5-fesm5) | <!-- v9 --> v11 |
|
||||||
@ -60,7 +57,6 @@ v9 - v12
|
|||||||
| `@angular/core/testing` | [`async`](#testing) | <!--v9--> v12 |
|
| `@angular/core/testing` | [`async`](#testing) | <!--v9--> v12 |
|
||||||
| `@angular/router` | [`ActivatedRoute` params and `queryParams` properties](#activatedroute-props) | unspecified |
|
| `@angular/router` | [`ActivatedRoute` params and `queryParams` properties](#activatedroute-props) | unspecified |
|
||||||
| template syntax | [`/deep/`, `>>>`, and `::ng-deep`](#deep-component-style-selector) | <!--v7--> unspecified |
|
| template syntax | [`/deep/`, `>>>`, and `::ng-deep`](#deep-component-style-selector) | <!--v7--> unspecified |
|
||||||
| browser support | [`IE 9 and 10, IE mobile`](#ie-9-10-and-mobile) | <!--v10--> v11 |
|
|
||||||
|
|
||||||
For information about Angular CDK and Angular Material deprecations, see the [changelog](https://github.com/angular/components/blob/master/CHANGELOG.md).
|
For information about Angular CDK and Angular Material deprecations, see the [changelog](https://github.com/angular/components/blob/master/CHANGELOG.md).
|
||||||
|
|
||||||
@ -91,7 +87,6 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i
|
|||||||
| [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. |
|
| [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. |
|
||||||
| [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | See [`ReflectiveInjector`](#reflectiveinjector) |
|
| [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | See [`ReflectiveInjector`](#reflectiveinjector) |
|
||||||
| [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none |
|
| [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none |
|
||||||
| [`ViewEncapsulation.Native`](api/core/ViewEncapsulation#Native) | [`ViewEncapsulation.ShadowDom`](api/core/ViewEncapsulation#ShadowDom) | v6 | Use the native encapsulation mechanism of the renderer. See [view.ts](https://github.com/angular/angular/blob/3e992e18ebf51d6036818f26c3d77b52d3ec48eb/packages/core/src/metadata/view.ts#L32).
|
|
||||||
| [`defineInjectable`](api/core/defineInjectable) | `ɵɵdefineInjectable` | v8 | Used only in generated code. No source code should depend on this API. |
|
| [`defineInjectable`](api/core/defineInjectable) | `ɵɵdefineInjectable` | v8 | Used only in generated code. No source code should depend on this API. |
|
||||||
| [`entryComponents`](api/core/NgModule#entryComponents) | none | v9 | See [`entryComponents`](#entryComponents) |
|
| [`entryComponents`](api/core/NgModule#entryComponents) | none | v9 | See [`entryComponents`](#entryComponents) |
|
||||||
| [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | v9 | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) |
|
| [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | v9 | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) |
|
||||||
@ -117,26 +112,6 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i
|
|||||||
| --- | ----------- | --------------------- | ----- |
|
| --- | ----------- | --------------------- | ----- |
|
||||||
| [`ngModel` with reactive forms](#ngmodel-reactive) | [`FormControlDirective`](api/forms/FormControlDirective) | v6 | none |
|
| [`ngModel` with reactive forms](#ngmodel-reactive) | [`FormControlDirective`](api/forms/FormControlDirective) | v6 | none |
|
||||||
|
|
||||||
{@a router}
|
|
||||||
### @angular/router
|
|
||||||
|
|
||||||
| API | Replacement | Deprecation announced | Notes |
|
|
||||||
| --- | ----------- | --------------------- | ----- |
|
|
||||||
| [`preserveQueryParams`](api/router/NavigationExtras#preserveQueryParams) | [`queryParamsHandling`](api/router/NavigationExtras#queryParamsHandling) | v4 | none |
|
|
||||||
|
|
||||||
{@a platform-webworker}
|
|
||||||
### @angular/platform-webworker
|
|
||||||
|
|
||||||
| API | Replacement | Deprecation announced | Notes |
|
|
||||||
| --- | ----------- | --------------------- | ----- |
|
|
||||||
| [All entry points](api/platform-webworker) | none | v8 | See [platform-webworker](#webworker-apps). |
|
|
||||||
|
|
||||||
{@a platform-webworker-dynamic}
|
|
||||||
### @angular/platform-webworker-dynamic
|
|
||||||
|
|
||||||
| API | Replacement | Deprecation announced | Notes |
|
|
||||||
| --- | ----------- | --------------------- | ----- |
|
|
||||||
| [All entry points](api/platform-webworker-dynamic) | none | v8 | See [platform-webworker](#webworker-apps). |
|
|
||||||
|
|
||||||
{@a upgrade}
|
{@a upgrade}
|
||||||
### @angular/upgrade
|
### @angular/upgrade
|
||||||
@ -392,28 +367,6 @@ These two properties have subtle differences, so switching to `textContent` unde
|
|||||||
|
|
||||||
All of the `wtf*` APIs are deprecated and will be removed in a future version.
|
All of the `wtf*` APIs are deprecated and will be removed in a future version.
|
||||||
|
|
||||||
{@a webworker-apps}
|
|
||||||
### Running Angular applications in platform-webworker
|
|
||||||
|
|
||||||
The `@angular/platform-*` packages enable Angular to be run in different contexts. For examples,
|
|
||||||
`@angular/platform-server` enables Angular to be run on the server, and `@angular/platform-browser`
|
|
||||||
enables Angular to be run in a web browser.
|
|
||||||
|
|
||||||
`@angular/platform-webworker` was introduced in Angular version 2 as an experiment in leveraging
|
|
||||||
Angular's rendering architecture to run an entire web application in a
|
|
||||||
[web worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API). We've learned a lot
|
|
||||||
from this experiment and have come to the conclusion that running the entire application in a web
|
|
||||||
worker is not the best strategy for most applications.
|
|
||||||
|
|
||||||
Going forward, we will focus our efforts related to web workers around their primary use case of
|
|
||||||
offloading CPU-intensive, non-critical work needed for initial rendering (such as in-memory search
|
|
||||||
and image processing). Learn more in the
|
|
||||||
[guide to Using Web Workers with the Angular CLI](guide/web-worker).
|
|
||||||
|
|
||||||
As of Angular version 8, all `platform-webworker` APIs are deprecated.
|
|
||||||
This includes both packages: `@angular/platform-webworker` and
|
|
||||||
`@angular/platform-webworker-dynamic`.
|
|
||||||
|
|
||||||
{@a entryComponents}
|
{@a entryComponents}
|
||||||
### `entryComponents` and `ANALYZE_FOR_ENTRY_COMPONENTS` no longer required
|
### `entryComponents` and `ANALYZE_FOR_ENTRY_COMPONENTS` no longer required
|
||||||
Previously, the `entryComponents` array in the `NgModule` definition was used to tell the compiler which components would be created and inserted dynamically. With Ivy, this isn't a requirement anymore and the `entryComponents` array can be removed from existing module declarations. The same applies to the `ANALYZE_FOR_ENTRY_COMPONENTS` injection token.
|
Previously, the `entryComponents` array in the `NgModule` definition was used to tell the compiler which components would be created and inserted dynamically. With Ivy, this isn't a requirement anymore and the `entryComponents` array can be removed from existing module declarations. The same applies to the `ANALYZE_FOR_ENTRY_COMPONENTS` injection token.
|
||||||
@ -462,20 +415,6 @@ export class MyModule {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
{@a ie-9-10-and-mobile}
|
|
||||||
### IE 9, 10, and IE mobile support
|
|
||||||
|
|
||||||
Support for IE 9 and 10 has been deprecated, as well as support for IE Mobile. These will be dropped in a future version.
|
|
||||||
Supporting outdated browsers like these increases bundle size, code complexity, and test load, and also requires time and effort that could be spent on improvements to the framework.
|
|
||||||
For example, fixing issues can be more difficult, as a straightforward fix for modern browsers could break old ones that have quirks due to not receiving updates from vendors.
|
|
||||||
|
|
||||||
The final decision was made on three key points:
|
|
||||||
* __Vendor support__: Microsoft dropped support of IE 9 and 10 on 1/12/16, meaning they no longer provide security updates or technical support. Additionally, Microsoft dropped support for Windows 10 Mobile in December 2019.
|
|
||||||
* __Usage statistics__: We looked at usage trends for IE 9 and 10 (as well as IE Mobile) from various sources and all indicated that usage percentages were extremely small (fractions of 1%).
|
|
||||||
* __Feedback from partners__: We also reached out to some of our Angular customers and none expressed concern about dropping IE 9, 10, nor IE Mobile support.
|
|
||||||
|
|
||||||
|
|
||||||
{@a wrapped-value}
|
{@a wrapped-value}
|
||||||
### `WrappedValue`
|
### `WrappedValue`
|
||||||
|
|
||||||
@ -542,15 +481,13 @@ This section contains a complete list all of the currently deprecated CLI flags.
|
|||||||
{@a removed}
|
{@a removed}
|
||||||
## Removed APIs
|
## Removed APIs
|
||||||
|
|
||||||
The following APIs have been removed starting with version 10.0.0*:
|
The following APIs have been removed starting with version 11.0.0*:
|
||||||
|
|
||||||
| Package | API | Replacement | Notes |
|
| Package | API | Replacement | Notes |
|
||||||
| ---------------- | -------------- | ----------- | ----- |
|
| ---------------- | -------------- | ----------- | ----- |
|
||||||
| `@angular/core` | Undecorated base classes that use Angular features | Add Angular decorator | See [migration guide](guide/migration-undecorated-classes) for more info |
|
| `@angular/router` | `preserveQueryParams` | [`queryParamsHandling`](api/router/UrlCreationOptions#queryParamsHandling) | |
|
||||||
| `@angular/core` | `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [migration guide](guide/migration-module-with-providers) for more info |
|
|
||||||
| `@angular/core` | Style Sanitization | no action needed | See [style sanitization API removal](#style-sanitization) for more info
|
|
||||||
|
|
||||||
*To see APIs removed in version 9, check out this guide on the [version 9 docs site](https://v9.angular.io/guide/deprecations#removed).
|
*To see APIs removed in version 10, check out this guide on the [version 10 docs site](https://v10.angular.io/guide/deprecations#removed).
|
||||||
|
|
||||||
|
|
||||||
{@a esm5-fesm5}
|
{@a esm5-fesm5}
|
||||||
@ -601,18 +538,6 @@ In practical terms, the `package.json` of all `@angular` packages has changed in
|
|||||||
|
|
||||||
For more information about the npm package format, see the [Angular Package Format spec](https://goo.gl/jB3GVv).
|
For more information about the npm package format, see the [Angular Package Format spec](https://goo.gl/jB3GVv).
|
||||||
|
|
||||||
{@a removed}
|
|
||||||
## Removed APIs
|
|
||||||
|
|
||||||
The following APIs have been removed starting with version 10.0.0*:
|
|
||||||
|
|
||||||
| Package | API | Replacement | Notes |
|
|
||||||
| ---------------- | -------------- | ----------- | ----- |
|
|
||||||
| `@angular/core` | Undecorated base classes that use Angular features | Add Angular decorator | See [migration guide](guide/migration-undecorated-classes) for more info |
|
|
||||||
| `@angular/core` | `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [migration guide](guide/migration-module-with-providers) for more info |
|
|
||||||
|
|
||||||
*To see APIs removed in version 9, check out this guide on the [version 9 docs site](https://v9.angular.io/guide/deprecations#removed).
|
|
||||||
|
|
||||||
{@a style-sanitization}
|
{@a style-sanitization}
|
||||||
### Style Sanitization for `[style]` and `[style.prop]` bindings
|
### Style Sanitization for `[style]` and `[style.prop]` bindings
|
||||||
Angular used to sanitize `[style]` and `[style.prop]` bindings to prevent malicious code from being inserted through `javascript:` expressions in CSS `url()` entries. However, most modern browsers no longer support the usage of these expressions, so sanitization was only maintained for the sake of IE 6 and 7. Given that Angular does not support either IE 6 or 7 and sanitization has a performance cost, we will no longer sanitize style bindings as of version 10 of Angular.
|
Angular used to sanitize `[style]` and `[style.prop]` bindings to prevent malicious code from being inserted through `javascript:` expressions in CSS `url()` entries. However, most modern browsers no longer support the usage of these expressions, so sanitization was only maintained for the sake of IE 6 and 7. Given that Angular does not support either IE 6 or 7 and sanitization has a performance cost, we will no longer sanitize style bindings as of version 10 of Angular.
|
||||||
|
@ -1,312 +0,0 @@
|
|||||||
# Displaying data in views
|
|
||||||
|
|
||||||
Angular [components](guide/glossary#component) form the data structure of your application.
|
|
||||||
The HTML [template](guide/glossary#template) associated with a component provides the means to display that data in the context of a web page.
|
|
||||||
Together, a component's class and template form a [view](guide/glossary#view) of your application data.
|
|
||||||
|
|
||||||
The process of combining data values with their representation on the page is called [data binding](guide/glossary#data-binding).
|
|
||||||
You display your data to a user (and collect data from the user) by *binding* controls in the HTML template to the data properties of the component class.
|
|
||||||
|
|
||||||
In addition, you can add logic to the template by including [directives](guide/glossary#directive), which tell Angular how to modify the page as it is rendered.
|
|
||||||
|
|
||||||
Angular defines a *template language* that expands HTML notation with syntax that allows you to define various kinds of data binding and logical directives.
|
|
||||||
When the page is rendered, Angular interprets the template syntax to update the HTML according to your logic and current data state.
|
|
||||||
Before you read the complete [template syntax guide](guide/template-syntax), the exercises on this page give you a quick demonstration of how template syntax works.
|
|
||||||
|
|
||||||
In this demo, you'll create a component with a list of heroes.
|
|
||||||
You'll display the list of hero names and conditionally show a message below the list.
|
|
||||||
The final UI looks like this:
|
|
||||||
|
|
||||||
<div class="lightbox">
|
|
||||||
<img src="generated/images/guide/displaying-data/final.png" alt="Final UI">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
The <live-example></live-example> demonstrates all of the syntax and code snippets described in this page.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{@a interpolation}
|
|
||||||
|
|
||||||
## Showing component properties with interpolation
|
|
||||||
The easiest way to display a component property is to bind the property name through interpolation.
|
|
||||||
With interpolation, you put the property name in the view template, enclosed in double curly braces: `{{myHero}}`.
|
|
||||||
|
|
||||||
Use the CLI command [`ng new displaying-data`](cli/new) to create a workspace and app named `displaying-data`.
|
|
||||||
|
|
||||||
Delete the <code>app.component.html</code> file. It is not needed for this example.
|
|
||||||
|
|
||||||
Then modify the <code>app.component.ts</code> file by
|
|
||||||
changing the template and the body of the component.
|
|
||||||
|
|
||||||
When you're done, it should look like this:
|
|
||||||
|
|
||||||
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts"></code-example>
|
|
||||||
|
|
||||||
You added two properties to the formerly empty component: `title` and `myHero`.
|
|
||||||
|
|
||||||
The template displays the two component properties using double curly brace
|
|
||||||
interpolation:
|
|
||||||
|
|
||||||
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
The template is a multi-line string within ECMAScript 2015 backticks (<code>\`</code>).
|
|
||||||
The backtick (<code>\`</code>)—which is *not* the same character as a single
|
|
||||||
quote (`'`)—allows you to compose a string over several lines, which makes the
|
|
||||||
HTML more readable.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Angular automatically pulls the value of the `title` and `myHero` properties from the component and
|
|
||||||
inserts those values into the browser. Angular updates the display
|
|
||||||
when these properties change.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
More precisely, the redisplay occurs after some kind of asynchronous event related to
|
|
||||||
the view, such as a keystroke, a timer completion, or a response to an HTTP request.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Notice that you don't call **new** to create an instance of the `AppComponent` class.
|
|
||||||
Angular is creating an instance for you. How?
|
|
||||||
|
|
||||||
The CSS `selector` in the `@Component` decorator specifies an element named `<app-root>`.
|
|
||||||
That element is a placeholder in the body of your `index.html` file:
|
|
||||||
|
|
||||||
<code-example path="displaying-data/src/index.html" header="src/index.html (body)" region="body"></code-example>
|
|
||||||
|
|
||||||
When you bootstrap with the `AppComponent` class (in <code>main.ts</code>), Angular looks for a `<app-root>`
|
|
||||||
in the `index.html`, finds it, instantiates an instance of `AppComponent`, and renders it
|
|
||||||
inside the `<app-root>` tag.
|
|
||||||
|
|
||||||
Now run the app. It should display the title and hero name:
|
|
||||||
|
|
||||||
<div class="lightbox">
|
|
||||||
<img src="generated/images/guide/displaying-data/title-and-hero.png" alt="Title and Hero">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
The next few sections review some of the coding choices in the app.
|
|
||||||
|
|
||||||
|
|
||||||
## Choosing the template source
|
|
||||||
|
|
||||||
The `@Component` metadata tells Angular where to find the component's template.
|
|
||||||
You can store your component's template in one of two places.
|
|
||||||
|
|
||||||
* You can define the template *inline* using the `template` property of the `@Component` decorator. An inline template is useful for a small demo or test.
|
|
||||||
* Alternatively, you can define the template in a separate HTML file and link to that file in the `templateUrl` property of the `@Component` decorator. This configuration is typical for anything more complex than a small test or demo, and is the default when you generate a new component.
|
|
||||||
|
|
||||||
In either style, the template data bindings have the same access to the component's properties.
|
|
||||||
Here the app uses inline HTML because the template is small and the demo is simpler without the additional HTML file.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
By default, the Angular CLI command [`ng generate component`](cli/generate) generates components with a template file.
|
|
||||||
You can override that by adding the "-t" (short for `inlineTemplate=true`) option:
|
|
||||||
|
|
||||||
<code-example hideCopy language="sh" class="code-shell">
|
|
||||||
ng generate component hero -t
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
## Initialization
|
|
||||||
|
|
||||||
The following example uses variable assignment to initialize the components.
|
|
||||||
|
|
||||||
<code-example path="displaying-data/src/app/app-ctor.component.1.ts" region="class"></code-example>
|
|
||||||
|
|
||||||
You could instead declare and initialize the properties using a constructor.
|
|
||||||
This app uses more terse "variable assignment" style simply for brevity.
|
|
||||||
|
|
||||||
|
|
||||||
{@a ngFor}
|
|
||||||
|
|
||||||
## Add logic to loop through data
|
|
||||||
|
|
||||||
The `*ngFor` directive (predefined by Angular) lets you loop through data. The following example uses the directive to show all of the values in an array property.
|
|
||||||
|
|
||||||
To display a list of heroes, begin by adding an array of hero names to the component and redefine `myHero` to be the first name in the array.
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
|
|
||||||
|
|
||||||
|
|
||||||
Now use the Angular `ngFor` directive in the template to display each item in the `heroes` list.
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
|
||||||
|
|
||||||
|
|
||||||
This UI uses the HTML unordered list with `<ul>` and `<li>` tags. The `*ngFor`
|
|
||||||
in the `<li>` element is the Angular "repeater" directive.
|
|
||||||
It marks that `<li>` element (and its children) as the "repeater template":
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (li)" region="li"></code-example>
|
|
||||||
|
|
||||||
<div class="alert is-important">
|
|
||||||
|
|
||||||
Don't forget the leading asterisk (\*) in `*ngFor`. It is an essential part of the syntax.
|
|
||||||
Read more about `ngFor` and `*` in the [ngFor section](guide/built-in-directives#ngfor) of the [Built-in directives](guide/built-in-directives) page.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Notice the `hero` in the `ngFor` double-quoted instruction;
|
|
||||||
it is an example of a template input variable. Read
|
|
||||||
more about template input variables in the [microsyntax](guide/built-in-directives#microsyntax) section of
|
|
||||||
the [Built-in directives](guide/built-in-directives) page.
|
|
||||||
|
|
||||||
Angular duplicates the `<li>` for each item in the list, setting the `hero` variable
|
|
||||||
to the item (the hero) in the current iteration. Angular uses that variable as the
|
|
||||||
context for the interpolation in the double curly braces.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
In this case, `ngFor` is displaying an array, but `ngFor` can
|
|
||||||
repeat items for any [iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) object.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Now the heroes appear in an unordered list.
|
|
||||||
|
|
||||||
<div class="lightbox">
|
|
||||||
<img src="generated/images/guide/displaying-data/hero-names-list.png" alt="After ngfor">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
## Creating a class for the data
|
|
||||||
|
|
||||||
The app's code defines the data directly inside the component, which isn't best practice.
|
|
||||||
In a simple demo, however, it's fine.
|
|
||||||
|
|
||||||
At the moment, the binding is to an array of strings.
|
|
||||||
In real applications, most bindings are to more specialized objects.
|
|
||||||
|
|
||||||
To convert this binding to use specialized objects, turn the array
|
|
||||||
of hero names into an array of `Hero` objects. For that you'll need a `Hero` class:
|
|
||||||
|
|
||||||
<code-example language="sh" class="code-shell">
|
|
||||||
ng generate class hero
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
This command creates the following code.
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="displaying-data/src/app/hero.ts" header="src/app/hero.ts"></code-example>
|
|
||||||
|
|
||||||
You've defined a class with a constructor and two properties: `id` and `name`.
|
|
||||||
|
|
||||||
It might not look like the class has properties, but it does.
|
|
||||||
The declaration of the constructor parameters takes advantage of a TypeScript shortcut.
|
|
||||||
|
|
||||||
Consider the first parameter:
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="displaying-data/src/app/hero.ts" header="src/app/hero.ts (id)" region="id"></code-example>
|
|
||||||
|
|
||||||
That brief syntax does a lot:
|
|
||||||
|
|
||||||
* Declares a constructor parameter and its type.
|
|
||||||
* Declares a public property of the same name.
|
|
||||||
* Initializes that property with the corresponding argument when creating an instance of the class.
|
|
||||||
|
|
||||||
|
|
||||||
### Using the Hero class
|
|
||||||
|
|
||||||
After importing the `Hero` class, the `AppComponent.heroes` property can return a _typed_ array
|
|
||||||
of `Hero` objects:
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="displaying-data/src/app/app.component.3.ts" header="src/app/app.component.ts (heroes)" region="heroes"></code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Next, update the template.
|
|
||||||
At the moment it displays the hero's `id` and `name`.
|
|
||||||
Fix that to display only the hero's `name` property.
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="displaying-data/src/app/app.component.3.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
|
||||||
|
|
||||||
|
|
||||||
The display looks the same, but the code is clearer.
|
|
||||||
|
|
||||||
{@a ngIf}
|
|
||||||
|
|
||||||
## Conditional display with NgIf
|
|
||||||
|
|
||||||
Sometimes an app needs to display a view or a portion of a view only under specific circumstances.
|
|
||||||
|
|
||||||
Let's change the example to display a message if there are more than three heroes.
|
|
||||||
|
|
||||||
The Angular `ngIf` directive inserts or removes an element based on a _truthy/falsy_ condition.
|
|
||||||
To see it in action, add the following paragraph at the bottom of the template:
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="displaying-data/src/app/app.component.ts" header="src/app/app.component.ts (message)" region="message"></code-example>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="alert is-important">
|
|
||||||
|
|
||||||
Don't forget the leading asterisk (\*) in `*ngIf`. It is an essential part of the syntax.
|
|
||||||
Read more about `ngIf` and `*` in the [ngIf section](guide/built-in-directives#ngIf) of the [Built-in directives](guide/built-in-directives) page.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
The template expression inside the double quotes,
|
|
||||||
`*ngIf="heroes.length > 3"`, looks and behaves much like TypeScript.
|
|
||||||
When the component's list of heroes has more than three items, Angular adds the paragraph
|
|
||||||
to the DOM and the message appears.
|
|
||||||
If there are three or fewer items, Angular omits the paragraph, so no message appears.
|
|
||||||
|
|
||||||
For more information, see [template expression operators](guide/interpolation#template-expressions).
|
|
||||||
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
Angular isn't showing and hiding the message. It is adding and removing the paragraph element from the DOM. That improves performance, especially in larger projects when conditionally including or excluding
|
|
||||||
big chunks of HTML with many data bindings.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Try it out. Because the array has four items, the message should appear.
|
|
||||||
Go back into <code>app.component.ts</code> and delete or comment out one of the elements from the heroes array.
|
|
||||||
The browser should refresh automatically and the message should disappear.
|
|
||||||
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
Now you know how to use:
|
|
||||||
|
|
||||||
* **Interpolation** with double curly braces to display a component property.
|
|
||||||
* **ngFor** to display an array of items.
|
|
||||||
* A TypeScript class to shape the **model data** for your component and display properties of that model.
|
|
||||||
* **ngIf** to conditionally display a chunk of HTML based on a boolean expression.
|
|
||||||
|
|
||||||
Here's the final code:
|
|
||||||
|
|
||||||
<code-tabs>
|
|
||||||
|
|
||||||
<code-pane header="src/app/app.component.ts" path="displaying-data/src/app/app.component.ts" region="final">
|
|
||||||
|
|
||||||
</code-pane>
|
|
||||||
|
|
||||||
<code-pane header="src/app/hero.ts" path="displaying-data/src/app/hero.ts">
|
|
||||||
|
|
||||||
</code-pane>
|
|
||||||
|
|
||||||
<code-pane header="src/app/app.module.ts" path="displaying-data/src/app/app.module.ts">
|
|
||||||
|
|
||||||
</code-pane>
|
|
||||||
|
|
||||||
<code-pane header="main.ts" path="displaying-data/src/main.ts">
|
|
||||||
|
|
||||||
</code-pane>
|
|
||||||
|
|
||||||
</code-tabs>
|
|
@ -4,7 +4,7 @@ An entry component is any component that Angular loads imperatively, (which mean
|
|||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
To contrast the two types of components, there are components which are included in the template, which are declarative. Additionally, there are components which you load imperatively; that is, entry components.
|
To contrast the two types of components, there are components which are included in the template, which are declarative. Additionally, there are components which you load imperatively; that is, entry components.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -75,11 +75,10 @@ All router components must be entry components. Because this would require you t
|
|||||||
|
|
||||||
|
|
||||||
## The `entryComponents` array
|
## The `entryComponents` array
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
Since 9.0.0 with Ivy, the `entryComponents` property is no longer necessary. See [deprecations guide](guide/deprecations#entryComponents).
|
Since 9.0.0 with Ivy, the `entryComponents` property is no longer necessary. See [deprecations guide](guide/deprecations#entryComponents).
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Though the `@NgModule` decorator has an `entryComponents` array, most of the time
|
Though the `@NgModule` decorator has an `entryComponents` array, most of the time
|
||||||
|
@ -95,7 +95,6 @@ Angular components, templates, and styles go here.
|
|||||||
| `app/app.component.css` | Defines the base CSS stylesheet for the root `AppComponent`. |
|
| `app/app.component.css` | Defines the base CSS stylesheet for the root `AppComponent`. |
|
||||||
| `app/app.component.spec.ts` | Defines a unit test for the root `AppComponent`. |
|
| `app/app.component.spec.ts` | Defines a unit test for the root `AppComponent`. |
|
||||||
| `app/app.module.ts` | Defines the root module, named `AppModule`, that tells Angular how to assemble the application. Initially declares only the `AppComponent`. As you add more components to the app, they must be declared here. |
|
| `app/app.module.ts` | Defines the root module, named `AppModule`, that tells Angular how to assemble the application. Initially declares only the `AppComponent`. As you add more components to the app, they must be declared here. |
|
||||||
| `app/package.json` | This file is generated only in applications created using `--strict` mode. This file is not used by package managers. It is used to tell the tools and bundlers whether the code under this directory is free of non-local [side-effects](guide/strict-mode#side-effect). |
|
|
||||||
|
|
||||||
### Application configuration files
|
### Application configuration files
|
||||||
|
|
||||||
|
@ -627,10 +627,11 @@ The [npm package manager](https://docs.npmjs.com/getting-started/what-is-npm) is
|
|||||||
|
|
||||||
Learn more about how Angular uses [Npm Packages](guide/npm-packages).
|
Learn more about how Angular uses [Npm Packages](guide/npm-packages).
|
||||||
|
|
||||||
{@ ngc}
|
{@a ngc}
|
||||||
|
|
||||||
## ngc
|
## ngc
|
||||||
`ngc` is a Typescript-to-Javascript transpiler that processes Angular decorators, metadata, and templates, and emits JavaScript code.
|
`ngc` is a Typescript-to-Javascript transpiler that processes Angular decorators, metadata, and templates, and emits JavaScript code.
|
||||||
The most recent implementation is internally refered to as `ngtsc` because it's a minimalistic wrapper around the TypeScript compiler `tsc` that adds a transform for processing Angular code.
|
The most recent implementation is internally referred to as `ngtsc` because it's a minimalistic wrapper around the TypeScript compiler `tsc` that adds a transform for processing Angular code.
|
||||||
|
|
||||||
{@a O}
|
{@a O}
|
||||||
|
|
||||||
|
@ -397,7 +397,7 @@ The following code example binds an observable of message strings
|
|||||||
## Caching HTTP requests
|
## Caching HTTP requests
|
||||||
|
|
||||||
To [communicate with backend services using HTTP](/guide/http "Communicating with backend services using HTTP"), the `HttpClient` service uses observables and offers the `HTTPClient.get()` method to fetch data from a server.
|
To [communicate with backend services using HTTP](/guide/http "Communicating with backend services using HTTP"), the `HttpClient` service uses observables and offers the `HTTPClient.get()` method to fetch data from a server.
|
||||||
The aynchronous method sends an HTTP request, and returns an observable that emits the requested data for the response.
|
The asynchronous method sends an HTTP request, and returns an observable that emits the requested data for the response.
|
||||||
|
|
||||||
As shown in the previous section, you can use the impure `AsyncPipe` to accept an observable as input and subscribe to the input automatically.
|
As shown in the previous section, you can use the impure `AsyncPipe` to accept an observable as input and subscribe to the input automatically.
|
||||||
You can also create an impure pipe to make and cache an HTTP request.
|
You can also create an impure pipe to make and cache an HTTP request.
|
||||||
|
@ -39,7 +39,34 @@ which is the attribute, spelled with a lowercase `s`.
|
|||||||
|
|
||||||
For more details, see the [MDN HTMLTableCellElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) documentation.
|
For more details, see the [MDN HTMLTableCellElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) documentation.
|
||||||
|
|
||||||
For more information about `colSpan` and `colspan`, see the [Attribute binding](guide/attribute-binding#colspan) guide.
|
{@a colspan}
|
||||||
|
|
||||||
|
#### `colspan` and `colSpan`
|
||||||
|
|
||||||
|
A common point of confusion is between the attribute, `colspan`, and the property, `colSpan`.
|
||||||
|
Notice that these two names differ by only a single letter.
|
||||||
|
|
||||||
|
If you wrote something like this:
|
||||||
|
|
||||||
|
<code-example language="html">
|
||||||
|
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
You'd get this error:
|
||||||
|
|
||||||
|
<code-example language="bash">
|
||||||
|
Template parse errors:
|
||||||
|
Can't bind to 'colspan' since it isn't a known native property
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
As the message says, the `<td>` element does not have a `colspan` property. This is true
|
||||||
|
because `colspan` is an attribute—`colSpan`, with a capital `S`, is the
|
||||||
|
corresponding property. Interpolation and property binding can set only *properties*, not attributes.
|
||||||
|
|
||||||
|
Instead, you'd use property binding and write it like this:
|
||||||
|
|
||||||
|
<code-example path="attribute-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
|
|
||||||
Another example is disabling a button when the component says that it `isUnchanged`:
|
Another example is disabling a button when the component says that it `isUnchanged`:
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ A form group tracks the status and changes for each of its controls, so if one o
|
|||||||
|
|
||||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroup" header="src/app/profile-editor/profile-editor.component.html (template form group)"></code-example>
|
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroup" header="src/app/profile-editor/profile-editor.component.html (template form group)"></code-example>
|
||||||
|
|
||||||
Note that just as a form group contains a group of controls, the *profile form* `FormGroup` is bound to the `form` element with the `FormGroup` directive, creating a communication layer between the model and the form containing the inputs. The `formControlName` input provided by the `FormControlName` directive binds each individual input to the form control defined in `FormGroup`. The form controls communicate with their respective elements. They also communicate changes to the form group instance, which provides the source of truth for the model value.
|
Note that just as a form group contains a group of controls, the *profileForm* `FormGroup` is bound to the `form` element with the `FormGroup` directive, creating a communication layer between the model and the form containing the inputs. The `formControlName` input provided by the `FormControlName` directive binds each individual input to the form control defined in `FormGroup`. The form controls communicate with their respective elements. They also communicate changes to the form group instance, which provides the source of truth for the model value.
|
||||||
|
|
||||||
**Save form data**
|
**Save form data**
|
||||||
|
|
||||||
|
@ -1656,7 +1656,7 @@ _before_ the `AppRoutingModule`:
|
|||||||
|
|
||||||
</code-tabs>
|
</code-tabs>
|
||||||
|
|
||||||
Remove the initial crisis center route from the `app-routing.module.ts` because now the `HeroesModule` and the `CrisisCenter` modules provide teh feature routes.
|
Remove the initial crisis center route from the `app-routing.module.ts` because now the `HeroesModule` and the `CrisisCenter` modules provide the feature routes.
|
||||||
|
|
||||||
The `app-routing.module.ts` file retains the top-level application routes such as the default and wildcard routes.
|
The `app-routing.module.ts` file retains the top-level application routes such as the default and wildcard routes.
|
||||||
|
|
||||||
|
@ -15,11 +15,11 @@ RxJS provides an implementation of the `Observable` type, which is needed until
|
|||||||
RxJS offers a number of functions that can be used to create new observables. These functions can simplify the process of creating observables from things such as events, timers, promises, and so on. For example:
|
RxJS offers a number of functions that can be used to create new observables. These functions can simplify the process of creating observables from things such as events, timers, promises, and so on. For example:
|
||||||
|
|
||||||
|
|
||||||
<code-example path="rx-library/src/simple-creation.ts" region="promise" header="Create an observable from a promise"></code-example>
|
<code-example path="rx-library/src/simple-creation.1.ts" region="promise" header="Create an observable from a promise"></code-example>
|
||||||
|
|
||||||
<code-example path="rx-library/src/simple-creation.ts" region="interval" header="Create an observable from a counter"></code-example>
|
<code-example path="rx-library/src/simple-creation.2.ts" region="interval" header="Create an observable from a counter"></code-example>
|
||||||
|
|
||||||
<code-example path="rx-library/src/simple-creation.ts" region="event" header="Create an observable from an event"></code-example>
|
<code-example path="rx-library/src/simple-creation.3.ts" region="event" header="Create an observable from an event"></code-example>
|
||||||
|
|
||||||
<code-example path="rx-library/src/simple-creation.ts" region="ajax" header="Create an observable that creates an AJAX request"></code-example>
|
<code-example path="rx-library/src/simple-creation.ts" region="ajax" header="Create an observable that creates an AJAX request"></code-example>
|
||||||
|
|
||||||
|
@ -267,6 +267,12 @@ By default, these criteria are:
|
|||||||
1. The URL must not contain a file extension (i.e. a `.`) in the last path segment.
|
1. The URL must not contain a file extension (i.e. a `.`) in the last path segment.
|
||||||
2. The URL must not contain `__`.
|
2. The URL must not contain `__`.
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
To configure whether navigation requests are sent through to the network or not, see the [navigationRequestStrategy](#navigation-request-strategy) section.
|
||||||
|
|
||||||
|
</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 may want to ignore specific routes (that are not part of the Angular app) and pass them through to the server.
|
While these default criteria are fine in most cases, it is sometimes desirable to configure different rules. For example, you may want to ignore specific routes (that are not part of the Angular app) and pass them through to the server.
|
||||||
@ -285,3 +291,32 @@ If the field is omitted, it defaults to:
|
|||||||
'!/**/*__*/**', // Exclude URLs containing `__` in any other segment.
|
'!/**/*__*/**', // Exclude URLs containing `__` in any other segment.
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
{@a navigation-request-strategy}
|
||||||
|
|
||||||
|
## `navigationRequestStrategy`
|
||||||
|
|
||||||
|
This optional property enables you to configure how the service worker handles navigation requests:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"navigationRequestStrategy": "freshness"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Possible values:
|
||||||
|
|
||||||
|
- `'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:
|
||||||
|
- Redirecting to an authentication website when authentication is not handled by the application.
|
||||||
|
- Redirecting specific URLs to avoid breaking existing links/bookmarks after a website redesign.
|
||||||
|
- Redirecting to a different website, such as a server-status page, while a page is temporarily down.
|
||||||
|
|
||||||
|
<div class="alert is-important">
|
||||||
|
|
||||||
|
The `freshness` strategy usually results in more requests sent to the server, which can increase response latency.
|
||||||
|
It is recommended that you use the default performance strategy whenever possible.
|
||||||
|
|
||||||
|
</div>
|
@ -1,17 +1,15 @@
|
|||||||
# Strict mode
|
# Strict mode
|
||||||
|
|
||||||
When you create a new workspace or a project you have an option to create them in a strict mode using the `--strict` flag.
|
When you create a new workspace or an application you have an option to create them in a strict mode using the `--strict` flag.
|
||||||
|
|
||||||
Enabling this flag initializes your new workspace or project with a few new settings that improve maintainability, help you catch bugs ahead of time, and allow the CLI to perform advanced optimizations on your application.
|
Enabling this flag initializes your new workspace or application with a few new settings that improve maintainability, help you catch bugs ahead of time.
|
||||||
Additionally, applications that use these stricter settings are easier to statically analyze, which can help the `ng update` command refactor code more safely and precisely when you are updating to future versions of Angular.
|
Additionally, applications that use these stricter settings are easier to statically analyze, which can help the `ng update` command refactor code more safely and precisely when you are updating to future versions of Angular.
|
||||||
|
|
||||||
Specifically, the `strict` flag does the following:
|
Specifically, the `strict` flag does the following:
|
||||||
|
|
||||||
* Enables [`strict` mode in TypeScript](https://www.staging-typescript.org/tsconfig#strict), as well as other strictness flags recommended by the TypeScript team. Specifically, `forceConsistentCasingInFileNames`, `noImplicitReturns`, `noFallthroughCasesInSwitch`.
|
* Enables [`strict` mode in TypeScript](https://www.staging-typescript.org/tsconfig#strict), as well as other strictness flags recommended by the TypeScript team. Specifically, `forceConsistentCasingInFileNames`, `noImplicitReturns`, `noFallthroughCasesInSwitch`.
|
||||||
* Turns on strict Angular compiler flags [`strictTemplates`](guide/angular-compiler-options#stricttemplates) and [`strictInjectionParameters`](guide/angular-compiler-options#strictinjectionparameters)
|
* Turns on strict Angular compiler flags [`strictTemplates`](guide/angular-compiler-options#stricttemplates), [`strictInjectionParameters`](guide/angular-compiler-options#strictinjectionparameters) and [`strictTemplates`](guide/angular-compiler-options#stricttemplates).
|
||||||
* [Bundle size budgets](guide/build#configuring-size-budgets) have been reduced by ~75%
|
* [Bundle size budgets](guide/build#configuring-size-budgets) have been reduced by ~75%.
|
||||||
* Turns on [`no-any` tslint rule](https://palantir.github.io/tslint/rules/no-any/) to prevent declarations of type `any`
|
|
||||||
* [Marks your application as side-effect free](https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free) to enable more advanced tree-shaking
|
|
||||||
|
|
||||||
You can apply these settings at the workspace and project level.
|
You can apply these settings at the workspace and project level.
|
||||||
|
|
||||||
@ -30,17 +28,3 @@ To create a new application in the strict mode within an existing non-strict wor
|
|||||||
ng generate application [project-name] --strict
|
ng generate application [project-name] --strict
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
{@a side-effect}
|
|
||||||
|
|
||||||
### Non-local side effects in applications
|
|
||||||
|
|
||||||
When you create projects and workspaces using the `strict` mode, you'll notice an additional `package.json` file, located in `src/app/` directory.
|
|
||||||
This file informs tools and bundlers that the code under this directory is free of non-local side effects. Non-local side effects in the application code are not common and using them is not considered a good coding pattern.
|
|
||||||
More importantly, code with these types of side effects cannot be optimized, resulting in increased bundle sizes and applications that load more slowly.
|
|
||||||
|
|
||||||
If you need more information, the following links may be helpful.
|
|
||||||
|
|
||||||
* [Tree-shaking](https://webpack.js.org/guides/tree-shaking/)
|
|
||||||
* [Dealing with side effects and pure functions in JavaScript](https://dev.to/vonheikemen/dealing-with-side-effects-and-pure-functions-in-javascript-16mg)
|
|
||||||
* [How to deal with dirty side effects in your pure function JavaScript](https://jrsinclair.com/articles/2018/how-to-deal-with-dirty-side-effects-in-your-pure-functional-javascript/)
|
|
133
aio/content/guide/style-precedence.md
Normal file
133
aio/content/guide/style-precedence.md
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
# Style Precedence
|
||||||
|
|
||||||
|
When there are multiple bindings to the same class name or style attribute, Angular uses a set of precedence rules to determine which classes or styles to apply to the element.
|
||||||
|
These rules specify an order for which style- and class-related bindings have priority.
|
||||||
|
This styling precedence is as follows, from the most specific with the highest priority to least specific with the lowest priorty:
|
||||||
|
|
||||||
|
1. Template bindings are the most specific because they apply to the element directly and exclusively, so they have the highest precedence.
|
||||||
|
<table width="100%">
|
||||||
|
<col width="40%"></col>
|
||||||
|
<col width="60%"></col>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Binding type</th>
|
||||||
|
<th>Example</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Property binding</td>
|
||||||
|
<td><code><div [class.foo]="hasFoo"></code><br><code><div [style.color]="color"></code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Map binding</td>
|
||||||
|
<td><code><div [class]="classExpression"></code><br><code><div [style]="styleExpression"></code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Static value</td>
|
||||||
|
<td><code><div class="foo"></code><br><code><div style="color: blue"></code></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
1. Directive host bindings are less specific because you can use directives in multiple locations, so they have a lower precedence than template bindings.
|
||||||
|
<table width="100%">
|
||||||
|
<col width="40%"></col>
|
||||||
|
<col width="60%"></col>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Binding type</th>
|
||||||
|
<th>Example</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Property binding</td>
|
||||||
|
<td><code>host: {'[class.foo]': 'hasFoo'}</code><br><code>host: {'[style.color]': 'color'}</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Map binding</td>
|
||||||
|
<td><code>host: {'[class]': 'classExpr'}</code><br><code>host: {'[style]': 'styleExpr'}</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Static value</td>
|
||||||
|
<td><code>host: {'class': 'foo'}</code><br><code>host: {'style': 'color: blue'}</code></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
1. Component host bindings have the lowest precedence.
|
||||||
|
<table width="100%">
|
||||||
|
<col width="40%"></col>
|
||||||
|
<col width="60%"></col>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Binding type</th>
|
||||||
|
<th>Example</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Property binding</td>
|
||||||
|
<td><code>host: {'[class.foo]': 'hasFoo'}</code><br><code>host: {'[style.color]': 'color'}</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Map binding</td>
|
||||||
|
<td><code>host: {'[class]': 'classExpression'}</code><br><code>host: {'[style]': 'styleExpression'}</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Static value</td>
|
||||||
|
<td><code>host: {'class': 'foo'}</code><br><code>host: {'style': 'color: blue'}</code></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## Precedence and specificity
|
||||||
|
|
||||||
|
In the following example, binding to a specific class, as in `[class.special]`, takes precedence over a generic `[class]` binding.
|
||||||
|
Similarly, binding to a specific style, as in `[style.color]`, takes precedence over a generic `[style]` binding.
|
||||||
|
|
||||||
|
<code-example path="attribute-binding/src/app/app.component.html" region="basic-specificity" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
|
## Precedence and bindings from different sources
|
||||||
|
|
||||||
|
Specificity rules also apply to bindings even when they originate from different sources.
|
||||||
|
An element can have bindings that originate from its own template, from host bindings on matched directives, and from host bindings on matched components.
|
||||||
|
|
||||||
|
<code-example path="attribute-binding/src/app/app.component.html" region="source-specificity" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
|
## Precedence of bindings and static attributes
|
||||||
|
|
||||||
|
Bindings take precedence over static attributes because they are dynamic.
|
||||||
|
In the following case, `class` and `[class]` have similar specificity, but the `[class]` binding takes precedence.
|
||||||
|
|
||||||
|
<code-example path="attribute-binding/src/app/app.component.html" region="dynamic-priority" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
|
{@a styling-delegation}
|
||||||
|
|
||||||
|
## Delegating to styles with lower precedence
|
||||||
|
|
||||||
|
Higher precedence styles can defer to lower precedence styles using `undefined` values.
|
||||||
|
For example, consider the following template:
|
||||||
|
|
||||||
|
<code-example path="attribute-binding/src/app/app.component.html" region="style-delegation" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
|
Imagine that the `dirWithHostBinding` directive and the `comp-with-host-binding` component both have a `[style.width]` host binding.
|
||||||
|
|
||||||
|
<code-example path="attribute-binding/src/app/comp-with-host-binding.component.ts" region="hostbinding" header="src/app/comp-with-host-binding.component.ts and dirWithHostBinding.directive.ts"></code-example>
|
||||||
|
|
||||||
|
If `dirWithHostBinding` sets its binding to `undefined`, the `width` property falls back to the value of the `comp-with-host-binding` host binding.
|
||||||
|
|
||||||
|
<code-example header="dirWithHostBinding directive">
|
||||||
|
@HostBinding('style.width')
|
||||||
|
width = ''; // undefined
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
If `dirWithHostBinding` sets its binding to `null`, Angular removes the `width` property entirely.
|
||||||
|
|
||||||
|
<code-example header="dirWithHostBinding">
|
||||||
|
@HostBinding('style.width')
|
||||||
|
width = null;
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
</div>
|
@ -1,7 +1,7 @@
|
|||||||
# Template statements
|
# Template statements
|
||||||
|
|
||||||
A template **statement** responds to an **event** raised by a binding target
|
Template statements are methods or properties that you can use in your HTML to respond to user events.
|
||||||
such as an element, component, or directive.
|
With template statements, your application can engage users through actions such as displaying dynamic content or submitting forms.
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
@ -10,24 +10,30 @@ the syntax and code snippets in this guide.
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The following template statement appears in quotes to the right of the `=` symbol as in `(event)="statement"`.
|
In the following example, the template statement `deleteHero()` appears in quotes to the right of the `=` symbol as in `(event)="statement"`.
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
|
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
A template statement *has a side effect*.
|
When the user clicks the **Delete hero** button, Angular calls the `deleteHero()` method in the component class.
|
||||||
That's the whole point of an event.
|
|
||||||
It's how you update application state from user action.
|
|
||||||
|
|
||||||
Responding to events is the other side of Angular's "unidirectional data flow".
|
You can use template statements with elements, components, or directives in response to events.
|
||||||
You're free to change anything, anywhere, during this turn of the event loop.
|
|
||||||
|
|
||||||
Like template expressions, template *statements* use a language that looks like JavaScript.
|
<div class="alert is-helpful">
|
||||||
The template statement parser differs from the template expression parser and
|
|
||||||
specifically supports both basic assignment (`=`) and chaining expressions with <code>;</code>.
|
|
||||||
|
|
||||||
However, certain JavaScript and template expression syntax is not allowed:
|
Responding to events is an aspect of Angular's [unidirectional data flow](guide/glossary#unidirectional-data-flow).
|
||||||
|
You can change anything in your application during a single event loop.
|
||||||
|
|
||||||
* <code>new</code>
|
</div>
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
Like [template expressions](guide/interpolation), template statements use a language that looks like JavaScript.
|
||||||
|
However, the parser for template statements differs from the parser for template expressions.
|
||||||
|
In addition, the template statements parser specifically supports both basic assignment, `=`, and chaining expressions with semicolons, `;`.
|
||||||
|
|
||||||
|
The following JavaScript and template expression syntax is not allowed:
|
||||||
|
|
||||||
|
* `new`
|
||||||
* increment and decrement operators, `++` and `--`
|
* increment and decrement operators, `++` and `--`
|
||||||
* operator assignment, such as `+=` and `-=`
|
* operator assignment, such as `+=` and `-=`
|
||||||
* the bitwise operators, such as `|` and `&`
|
* the bitwise operators, such as `|` and `&`
|
||||||
@ -35,31 +41,32 @@ However, certain JavaScript and template expression syntax is not allowed:
|
|||||||
|
|
||||||
## Statement context
|
## Statement context
|
||||||
|
|
||||||
As with expressions, statements can refer only to what's in the statement context
|
Statements have a context—a particular part of the application to which the statement belongs.
|
||||||
such as an event handling method of the component instance.
|
|
||||||
|
|
||||||
The *statement context* is typically the component instance.
|
Statements can refer only to what's in the statement context, which is typically the component instance.
|
||||||
The *deleteHero* in `(click)="deleteHero()"` is a method of the data-bound component.
|
For example, `deleteHero()` of `(click)="deleteHero()"` is a method of the component in the following snippet.
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
|
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
The statement context may also refer to properties of the template's own context.
|
The statement context may also refer to properties of the template's own context.
|
||||||
In the following examples, the template `$event` object,
|
In the following example, the component's event handling method, `onSave()` takes the template's own `$event` object as an argument.
|
||||||
a [template input variable](guide/built-in-directives#template-input-variable) (`let hero`),
|
On the next two lines, the `deleteHero()` method takes a [template input variable](guide/built-in-directives#template-input-variable), `hero`, and `onSubmit()` takes a [template reference variable](guide/template-reference-variables), `#heroForm`.
|
||||||
and a [template reference variable](guide/template-reference-variables) (`#heroForm`)
|
|
||||||
are passed to an event handling method of the component.
|
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="context-var-statement" header="src/app/app.component.html"></code-example>
|
<code-example path="template-syntax/src/app/app.component.html" region="context-var-statement" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
|
In this example, the context of the `$event` object, `hero`, and `#heroForm` is the template.
|
||||||
|
|
||||||
Template context names take precedence over component context names.
|
Template context names take precedence over component context names.
|
||||||
In `deleteHero(hero)` above, the `hero` is the template input variable,
|
In the preceding `deleteHero(hero)`, the `hero` is the template input variable, not the component's `hero` property.
|
||||||
not the component's `hero` property.
|
|
||||||
|
|
||||||
## Statement guidelines
|
## Statement best practices
|
||||||
|
|
||||||
Template statements cannot refer to anything in the global namespace. They
|
* **Conciseness**
|
||||||
can't refer to `window` or `document`.
|
|
||||||
They can't call `console.log` or `Math.max`.
|
|
||||||
|
|
||||||
As with expressions, avoid writing complex template statements.
|
Keep template statements minimal by using method calls or basic property assignments.
|
||||||
A method call or simple property assignment should be the norm.
|
|
||||||
|
* **Work within the context**
|
||||||
|
|
||||||
|
The context of a template statement can be the component class instance or the template.
|
||||||
|
Because of this, template statements cannot refer to anything in the global namespace such as `window` or `document`.
|
||||||
|
For example, template statements can't call `console.log()` or `Math.max()`.
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user