Compare commits
290 Commits
11.0.0-nex
...
10.1.6
Author | SHA1 | Date | |
---|---|---|---|
5db7b4c354 | |||
e08d02145e | |||
dae3a77c43 | |||
02e75df3e7 | |||
f752ab9367 | |||
9c875b30dc | |||
8fa78d10ab | |||
26988f0d62 | |||
6acf117386 | |||
8b731e374d | |||
5378f78e79 | |||
495c40e31c | |||
9963c5d8f7 | |||
13c4a7b1da | |||
70e85d226c | |||
dfb129dbfa | |||
1a1407aef6 | |||
da71f76a6a | |||
0c9b76e9e7 | |||
db34b23ab5 | |||
df0941554b | |||
5221df8e92 | |||
bc35c6a8ed | |||
23fc2b43ac | |||
c16995eb59 | |||
3487d5a13a | |||
1efc75cd72 | |||
c4190e3142 | |||
5d98834585 | |||
529ff73200 | |||
791205005c | |||
2d37e47e95 | |||
27c8066641 | |||
952fd8662c | |||
42be9047d8 | |||
52a0c6b36e | |||
8913aee527 | |||
7628c36f49 | |||
b417dd7b9c | |||
8cf16b4815 | |||
806aed63f4 | |||
eb34037cd7 | |||
ba0df5250f | |||
c4553bbed9 | |||
cc13b37446 | |||
166df5d2ca | |||
a5b474580c | |||
9f132d0d93 | |||
8dee378b3e | |||
0845d1148f | |||
7cfa57a5f7 | |||
f80c22002b | |||
7aa12412f3 | |||
072b707b38 | |||
223b80cb7d | |||
416403fc63 | |||
878e2f0deb | |||
4c30f5135b | |||
6791cd79af | |||
f50313f54d | |||
30433a0710 | |||
86ab9f92b4 | |||
42f9679376 | |||
bee10574d8 | |||
afce0f5038 | |||
bb19e61848 | |||
50e83e2566 | |||
d80c4890be | |||
4f8b716c13 | |||
cb85d69450 | |||
4a446878fa | |||
cb80f46c64 | |||
5199d55d45 | |||
bb11fd9058 | |||
771f7318f0 | |||
5d584b7728 | |||
1b5f6ee7a6 | |||
669e07580c | |||
965249d1da | |||
3a01856e7c | |||
a8134dcfd4 | |||
a1bf0de711 | |||
3dbc076159 | |||
3409efbeb3 | |||
3812f57789 | |||
8292e1cc51 | |||
d8de689080 | |||
d53cfb510c | |||
fb51e10954 | |||
7813529f4e | |||
066fca07f4 | |||
31be06a6f6 | |||
fd795da9d9 | |||
69302adc02 | |||
9891cef6e4 | |||
65d4e7a8af | |||
0fa208f624 | |||
15fa4bbdaf | |||
ed35adbea6 | |||
4645f43c3c | |||
b613639e8a | |||
bc6ff7745e | |||
33aaa9e7d0 | |||
689651b52f | |||
875fcfe5a9 | |||
0d238aa9d9 | |||
398b44640f | |||
5b1c714068 | |||
6df71d52c1 | |||
be25ccb94c | |||
6e994272e8 | |||
79ac811550 | |||
bbe6cf38ff | |||
c95fabf96d | |||
3fad0ffb3a | |||
ef0be182bb | |||
139c5b4eab | |||
1328236810 | |||
5453772648 | |||
7c5f89d2c3 | |||
bc0d140a1d | |||
14ecc9ead2 | |||
62a2fc8981 | |||
8f59e3750b | |||
3d9ebb4a52 | |||
ea141f86d3 | |||
c100dbe860 | |||
9c82e27ab6 | |||
4035e472d0 | |||
d03e2e35cb | |||
b29c32b758 | |||
3e9986871c | |||
617858df61 | |||
68cb77d9ab | |||
5cdf2e4e30 | |||
39bfa349c7 | |||
b3752e6524 | |||
9833b0b31c | |||
2eb8447c95 | |||
78c9972195 | |||
5bf55132fb | |||
50f5827fb4 | |||
e98f11ac9e | |||
1f2c7f6728 | |||
3d2799b73c | |||
685281337b | |||
9e8fa0748f | |||
525af1e5f0 | |||
58f2abef01 | |||
9d918d8e6c | |||
8236904933 | |||
73550967e4 | |||
c6505001a9 | |||
73c7882629 | |||
32e32e5bdc | |||
0dda8bf265 | |||
8db865d9b0 | |||
667c10a0f9 | |||
02862338fa | |||
8f523c1658 | |||
790e483982 | |||
a7650b0f76 | |||
04d0aa6781 | |||
7d5b5153cf | |||
27a6e5a31c | |||
2700d88912 | |||
3902ec0dbe | |||
6f579b20f8 | |||
c4a7516747 | |||
33055da5c7 | |||
fb163df6dc | |||
6b05dc432b | |||
093c3a10f6 | |||
a0756e9fa4 | |||
d8657ddb5c | |||
2f71995ef7 | |||
32a8713620 | |||
0958a8da61 | |||
e64c0c3730 | |||
730277806b | |||
674620a5ed | |||
997713e2bb | |||
234e5af636 | |||
e43c701388 | |||
2e1264fb5d | |||
a89dcba0d6 | |||
bd9f441370 | |||
fbcb66e70c | |||
3d94919800 | |||
af3b401e15 | |||
e4c12c8f9e | |||
ea36466060 | |||
58411e7ad9 | |||
84bd1a233d | |||
ef13d8f33a | |||
dc4f85888e | |||
2bdfb14be0 | |||
26deef2d3e | |||
f52a494248 | |||
ebede67433 | |||
565840515c | |||
c62d1cb80a | |||
aa43cbf8c5 | |||
b05d79d14a | |||
04c2bb9580 | |||
ec2dbe7fb4 | |||
8096c63c66 | |||
6ff9e6e2bd | |||
31d0ee4cbf | |||
a47383d1e8 | |||
9078187378 | |||
dcb473db34 | |||
edb7f90363 | |||
9c51ba321e | |||
d8714d045d | |||
5de2ac3e1b | |||
7669bd856f | |||
18d911d807 | |||
38ff66dc32 | |||
5672aba2f9 | |||
5567bdc48e | |||
1c156eb304 | |||
396548442e | |||
c54161098d | |||
f1b355b54f | |||
e40ffb95c8 | |||
20564f997f | |||
b1398d1771 | |||
7e9134aae8 | |||
0a55058440 | |||
bb1122d087 | |||
2ea49c7add | |||
83d69978fd | |||
62de2131e1 | |||
e156e29edd | |||
775c305771 | |||
190dca0fdc | |||
309709d4b2 | |||
028ef30b34 | |||
56d5ff2a89 | |||
b4eb016e56 | |||
6b0dba48b1 | |||
cfd4c0b4dc | |||
38762020d3 | |||
a1c34c6f0a | |||
b084bffb64 | |||
6a28675a5e | |||
4de8dc3554 | |||
ab4f953c78 | |||
ee432aaab8 | |||
5863537575 | |||
fcd2eb2ffb | |||
251a28cb15 | |||
54bb1c3d6a | |||
6c6dd5f38c | |||
9794f20674 | |||
027b041cfd | |||
4886cf5965 | |||
f21d50d2e6 | |||
0ef985368e | |||
0a277c6c40 | |||
9bf32c4dcb | |||
1c2ccfed4d | |||
25afbcc459 | |||
29c89c9297 | |||
efc76064d9 | |||
dbab74429f | |||
6aac499ee7 | |||
32f33f095f | |||
b0bd777ba9 | |||
c01bd0fe8e | |||
5588324802 | |||
437ecc8090 | |||
0dda97ea66 | |||
5e4aeaa348 | |||
cbbf8b542f | |||
91dfb18840 | |||
e44ddf5baa | |||
6b1a505566 | |||
659705ad78 | |||
8864b0ed69 | |||
4e596b672f | |||
83866827c3 | |||
7006cac50a | |||
dd82f2fefd | |||
bf003340ab | |||
5e35edd724 | |||
c132dcd0ae | |||
bbe331569b | |||
21e9a0032c |
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
|
||||
# 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)
|
||||
build --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/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/tools,integration/bazel/test/e2e
|
||||
|
||||
################################
|
||||
# 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]
|
||||
# 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.
|
||||
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
|
||||
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
|
||||
build --local_cpu_resources=8
|
||||
build --local_ram_resources=14336
|
||||
build --local_cpu_resources=20
|
||||
build --local_ram_resources=32768
|
||||
|
||||
# All build executed remotely should be done using our RBE configuration.
|
||||
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
|
||||
|
||||
# Manually set the local resources used in windows CI runs
|
||||
build --local_ram_resources=13500
|
||||
build --local_cpu_resources=4
|
||||
build --local_ram_resources=120000
|
||||
build --local_cpu_resources=32
|
||||
|
||||
# All windows jobs run on master and should use http caching
|
||||
build --remote_http_cache=https://storage.googleapis.com/angular-team-cache
|
||||
|
@ -80,7 +80,7 @@ executors:
|
||||
|
||||
windows-executor:
|
||||
working_directory: ~/ng
|
||||
resource_class: windows.medium
|
||||
resource_class: windows.2xlarge
|
||||
# CircleCI windows VMs do have the GitBash shell available:
|
||||
# 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
|
||||
@ -273,6 +273,7 @@ jobs:
|
||||
- run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check
|
||||
- run: yarn -s ts-circular-deps:check
|
||||
- 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
|
||||
|
||||
test:
|
||||
@ -857,9 +858,16 @@ workflows:
|
||||
- build-npm-packages
|
||||
- build-ivy-npm-packages
|
||||
- legacy-unit-tests-saucelabs
|
||||
- components-repo-unit-tests:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
# Temporarily disabled components-repo-unit-tests to update rules_nodejs to 2.0.0. Breaking changes in
|
||||
# rules_nodejs create a dependency sandwich between angular/angular & angular/components that are very
|
||||
# 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:
|
||||
requires:
|
||||
- setup
|
||||
|
@ -41,7 +41,8 @@ copy .circleci\bazel.windows.rc ${Env:USERPROFILE}\.bazelrc
|
||||
####################################################################################################
|
||||
# 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.
|
||||
# choco install yarn --version 1.16.0 --no-progress
|
||||
|
55
.github/angular-robot.yml
vendored
55
.github/angular-robot.yml
vendored
@ -38,6 +38,7 @@ merge:
|
||||
- "modules/benchmarks/**"
|
||||
- "modules/system.d.ts"
|
||||
- "packages/**"
|
||||
- "dev-infra/benchmark/driver-utilities/**"
|
||||
# list of patterns to ignore for the files changed by the PR
|
||||
exclude:
|
||||
- "packages/*"
|
||||
@ -47,7 +48,10 @@ merge:
|
||||
- "packages/bazel/src/ng_package/**"
|
||||
- "packages/bazel/src/protractor/**"
|
||||
- "packages/bazel/src/schematics/**"
|
||||
- "packages/compiler-cli/src/ngcc/**"
|
||||
- "packages/compiler-cli/linker/**"
|
||||
- "packages/compiler-cli/ngcc/**"
|
||||
- "packages/compiler-cli/src/ngtsc/sourcemaps/**"
|
||||
- "packages/docs/**"
|
||||
- "packages/elements/schematics/**"
|
||||
- "packages/examples/**"
|
||||
@ -55,6 +59,8 @@ merge:
|
||||
- "packages/localize/**"
|
||||
- "packages/private/**"
|
||||
- "packages/service-worker/**"
|
||||
- "packages/common/locales/**"
|
||||
- "packages/http/**"
|
||||
- "**/.gitignore"
|
||||
- "**/.gitkeep"
|
||||
- "**/yarn.lock"
|
||||
@ -68,20 +74,20 @@ merge:
|
||||
- "packages/**/integrationtest/**"
|
||||
- "packages/**/test/**"
|
||||
- "packages/zone.js/*"
|
||||
- "packages/zone.js/dist/**"
|
||||
- "packages/zone.js/doc/**"
|
||||
- "packages/zone.js/example/**"
|
||||
- "packages/zone.js/scripts/**"
|
||||
|
||||
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
|
||||
mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.
|
||||
\nPlease help to unblock it by resolving these conflicts. Thanks!"
|
||||
mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.\nPlease help to unblock it by resolving these conflicts. Thanks!"
|
||||
|
||||
# label to monitor
|
||||
mergeLabel: "PR action: merge"
|
||||
mergeLabel: "action: merge"
|
||||
|
||||
# adding any of these labels will also add the merge label
|
||||
mergeLinkedLabels:
|
||||
- "PR action: merge-assistance"
|
||||
- "action: merge-assistance"
|
||||
|
||||
# list of checks that will determine if the merge label can be added
|
||||
checks:
|
||||
@ -94,17 +100,17 @@ merge:
|
||||
|
||||
# whether the PR shouldn't have a conflict with the base branch
|
||||
noConflict: true
|
||||
# list of labels that a PR needs to have, checked with a regexp (e.g. "PR target:" will work for the label "PR target: master")
|
||||
# list of labels that a PR needs to have, checked with a regexp (e.g. "target:" will work for the label "target: master")
|
||||
requiredLabels:
|
||||
- "PR target: *"
|
||||
- "target: *"
|
||||
- "cla: yes"
|
||||
|
||||
# list of labels that a PR shouldn't have, checked after the required labels with a regexp
|
||||
forbiddenLabels:
|
||||
- "PR target: TBD"
|
||||
- "PR action: cleanup"
|
||||
- "PR action: review"
|
||||
- "PR state: blocked"
|
||||
- "target: TBD"
|
||||
- "action: cleanup"
|
||||
- "action: review"
|
||||
- "state: blocked"
|
||||
- "cla: no"
|
||||
|
||||
# list of PR statuses that need to be successful
|
||||
@ -121,12 +127,7 @@ merge:
|
||||
# the comment that will be added when the merge label is added despite failing checks, leave empty or set to false to disable
|
||||
# {{MERGE_LABEL}} will be replaced by the value of the mergeLabel option
|
||||
# {{PLACEHOLDER}} will be replaced by the list of failing checks
|
||||
mergeRemovedComment: "I see that you just added the `{{MERGE_LABEL}}` label, but the following checks are still failing:
|
||||
\n{{PLACEHOLDER}}
|
||||
\n
|
||||
\n**If you want your PR to be merged, it has to pass all the CI checks.**
|
||||
\n
|
||||
\nIf you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help."
|
||||
mergeRemovedComment: "I see that you just added the `{{MERGE_LABEL}}` label, but the following checks are still failing:\n{{PLACEHOLDER}}\n\n**If you want your PR to be merged, it has to pass all the CI checks.**\n\nIf you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help."
|
||||
|
||||
# options for the triage plugin
|
||||
triage:
|
||||
@ -141,24 +142,28 @@ triage:
|
||||
# arrays of labels that determine if an issue has been fully triaged
|
||||
l2TriageLabels:
|
||||
-
|
||||
- "type: bug/fix"
|
||||
- "severity*"
|
||||
- "freq*"
|
||||
- "P0"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: feature"
|
||||
- "P1"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: refactor"
|
||||
- "P2"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: RFC / Discussion / question"
|
||||
- "P3"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: confusing"
|
||||
- "P4"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: use-case"
|
||||
- "P5"
|
||||
- "comp: *"
|
||||
-
|
||||
- "feature"
|
||||
- "comp: *"
|
||||
-
|
||||
- "discussion"
|
||||
- "comp: *"
|
||||
|
||||
# options for the triage PR plugin
|
||||
@ -186,4 +191,4 @@ rerunCircleCI:
|
||||
# set to true to disable
|
||||
disabled: false
|
||||
# the label which when added triggers a rerun of the default CircleCI workflow
|
||||
triggerRerunLabel: "PR action: rerun CI at HEAD"
|
||||
triggerRerunLabel: "action: rerun CI at HEAD"
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -40,6 +40,9 @@ yarn-error.log
|
||||
# User specific bazel settings
|
||||
.bazelrc.user
|
||||
|
||||
# User specific ng-dev settings
|
||||
.ng-dev.user*
|
||||
|
||||
.notes.md
|
||||
baseline.json
|
||||
|
||||
|
19
.ng-dev/caretaker.ts
Normal file
19
.ng-dev/caretaker.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {CaretakerConfig} from '../dev-infra/caretaker/config';
|
||||
|
||||
/** The configuration for `ng-dev caretaker` commands. */
|
||||
export const caretaker: CaretakerConfig = {
|
||||
githubQueries: [
|
||||
{
|
||||
name: 'Merge Queue',
|
||||
query: `is:pr is:open status:success label:"action: merge"`,
|
||||
},
|
||||
{
|
||||
name: 'Merge Assistance Queue',
|
||||
query: `is:pr is:open label:"action: merge-assistance"`,
|
||||
},
|
||||
{
|
||||
name: 'Initial Triage Queue',
|
||||
query: `is:open no:milestone`,
|
||||
}
|
||||
]
|
||||
};
|
@ -1,11 +1,15 @@
|
||||
import {caretaker} from './caretaker';
|
||||
import {commitMessage} from './commit-message';
|
||||
import {format} from './format';
|
||||
import {github} from './github';
|
||||
import {merge} from './merge';
|
||||
import {release} from './release';
|
||||
|
||||
module.exports = {
|
||||
commitMessage,
|
||||
format,
|
||||
github,
|
||||
merge,
|
||||
caretaker,
|
||||
release,
|
||||
};
|
||||
|
@ -1,38 +1,28 @@
|
||||
import {MergeConfig} from '../dev-infra/pr/merge/config';
|
||||
import {DevInfraMergeConfig} from '../dev-infra/pr/merge/config';
|
||||
import {getDefaultTargetLabelConfiguration} from '../dev-infra/pr/merge/defaults';
|
||||
import {github} from './github';
|
||||
import {release} from './release';
|
||||
|
||||
/**
|
||||
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
|
||||
* are respected by the merge script (e.g. the target labels).
|
||||
*/
|
||||
export const merge = (): MergeConfig => {
|
||||
// TODO: resume dynamically determining patch branch
|
||||
const patch = '10.0.x';
|
||||
export const merge: DevInfraMergeConfig['merge'] = async api => {
|
||||
return {
|
||||
githubApiMerge: false,
|
||||
claSignedLabel: 'cla: yes',
|
||||
mergeReadyLabel: /^PR action: merge(-assistance)?/,
|
||||
caretakerNoteLabel: 'PR action: merge-assistance',
|
||||
mergeReadyLabel: /^action: merge(-assistance)?/,
|
||||
caretakerNoteLabel: 'action: merge-assistance',
|
||||
commitMessageFixupLabel: 'commit message fixup',
|
||||
labels: [
|
||||
{
|
||||
pattern: 'PR target: master-only',
|
||||
branches: ['master'],
|
||||
},
|
||||
{
|
||||
pattern: 'PR target: patch-only',
|
||||
branches: [patch],
|
||||
},
|
||||
{
|
||||
pattern: 'PR target: master & patch',
|
||||
branches: ['master', patch],
|
||||
},
|
||||
],
|
||||
// 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: {
|
||||
// PRs that target either `master` or the patch branch, need to be rebased
|
||||
// on top of the latest commit message validation fix.
|
||||
// These SHAs are the commits that update the required license text in the header.
|
||||
'master': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a',
|
||||
[patch]: '27b95ba64a5d99757f4042073fd1860e20e3ed24'
|
||||
'10.0.x': '27b95ba64a5d99757f4042073fd1860e20e3ed24',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
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, '../')});
|
||||
},
|
||||
};
|
@ -284,7 +284,7 @@ groups:
|
||||
users:
|
||||
- alxhub
|
||||
- crisbeto
|
||||
- devversion
|
||||
# OOO as of 2020-09-28 - devversion
|
||||
|
||||
|
||||
# =========================================================
|
||||
@ -326,6 +326,7 @@ groups:
|
||||
'aio/content/examples/component-interaction/**',
|
||||
'aio/content/images/guide/component-interaction/**',
|
||||
'aio/content/guide/component-styles.md',
|
||||
'aio/content/guide/view-encapsulation.md',
|
||||
'aio/content/examples/component-styles/**',
|
||||
'aio/content/guide/dependency-injection.md',
|
||||
'aio/content/examples/dependency-injection/**',
|
||||
@ -419,7 +420,7 @@ groups:
|
||||
- atscott
|
||||
- ~kara # do not request reviews from Kara, but allow her to approve PRs
|
||||
- mhevery
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
|
||||
|
||||
# =========================================================
|
||||
@ -662,7 +663,7 @@ groups:
|
||||
users:
|
||||
- AndrewKushnir
|
||||
- IgorMinar
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
|
||||
|
||||
# =========================================================
|
||||
@ -679,7 +680,7 @@ groups:
|
||||
reviewers:
|
||||
users:
|
||||
- IgorMinar
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
|
||||
|
||||
# =========================================================
|
||||
@ -697,7 +698,7 @@ groups:
|
||||
users:
|
||||
- IgorMinar
|
||||
- jelbourn
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
|
||||
|
||||
# =========================================================
|
||||
@ -723,7 +724,7 @@ groups:
|
||||
- IgorMinar
|
||||
- mhevery
|
||||
- jelbourn
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
reviews:
|
||||
request: -1 # request reviews from everyone
|
||||
required: 2 # require at least 2 approvals
|
||||
@ -1115,6 +1116,7 @@ groups:
|
||||
'docs/DEBUG.md',
|
||||
'docs/DEBUG_COMPONENTS_REPO_IVY.md',
|
||||
'docs/DEVELOPER.md',
|
||||
'docs/FIXUP_COMMITS.md',
|
||||
'docs/GITHUB_PROCESS.md',
|
||||
'docs/PR_REVIEW.md',
|
||||
'docs/SAVED_REPLIES.md',
|
||||
@ -1150,7 +1152,7 @@ groups:
|
||||
])
|
||||
reviewers:
|
||||
users:
|
||||
- devversion
|
||||
# OOO as of 2020-09-28 - devversion
|
||||
- filipesilva
|
||||
- gkalpak
|
||||
- IgorMinar
|
||||
@ -1184,7 +1186,7 @@ groups:
|
||||
- atscott
|
||||
- jelbourn
|
||||
- petebacondarwin
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
reviews:
|
||||
request: 4 # Request reviews from four people
|
||||
required: 3 # Require that three people approve
|
||||
@ -1212,7 +1214,7 @@ groups:
|
||||
- atscott
|
||||
- jelbourn
|
||||
- petebacondarwin
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
reviews:
|
||||
request: 4 # Request reviews from four people
|
||||
required: 2 # Require that two people approve
|
||||
@ -1240,7 +1242,7 @@ groups:
|
||||
- atscott
|
||||
- jelbourn
|
||||
- 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,
|
||||
|
||||
// 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)([
|
||||
// never ignore these files
|
||||
@ -44663,6 +44663,7 @@ const NEVER_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([
|
||||
function packWithIgnoreAndHeaders(cwd, ignoreFunction, { mapHeader } = {}) {
|
||||
return tar.pack(cwd, {
|
||||
ignore: ignoreFunction,
|
||||
sort: true,
|
||||
map: header => {
|
||||
const suffix = header.name === '.' ? '' : `/${header.name}`;
|
||||
header.name = `package${suffix}`;
|
||||
@ -46678,7 +46679,7 @@ function mkdirfix (name, opts, cb) {
|
||||
/* 194 */
|
||||
/***/ (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 */
|
||||
@ -98338,7 +98339,7 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def
|
||||
|
||||
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)}...`);
|
||||
yield (_fs || _load_fs()).mkdirp(path.dirname(yarnPath));
|
||||
yield (_fs || _load_fs()).writeFile(yarnPath, bundle);
|
||||
@ -100190,7 +100191,7 @@ let main = exports.main = (() => {
|
||||
|
||||
const config = new (_config || _load_config()).default(reporter);
|
||||
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) {
|
||||
reporter.header(commandName, { name: 'yarn', version: (_yarnVersion || _load_yarnVersion()).version });
|
||||
@ -100604,7 +100605,7 @@ let start = (() => {
|
||||
});
|
||||
|
||||
try {
|
||||
if (yarnPath.endsWith(`.js`)) {
|
||||
if (/\.[cm]?js$/.test(yarnPath)) {
|
||||
exitCode = yield (0, (_child || _load_child()).spawnp)(process.execPath, [yarnPath, ...argv], opts);
|
||||
} else {
|
||||
exitCode = yield (0, (_child || _load_child()).spawnp)(yarnPath, argv, opts);
|
2
.yarnrc
2
.yarnrc
@ -2,4 +2,4 @@
|
||||
# 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(
|
||||
name = "angularjs_scripts",
|
||||
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)
|
||||
"@npm//:node_modules/angular/angular.js",
|
||||
"@npm//:node_modules/angular/angular.min.js",
|
||||
@ -47,3 +47,9 @@ filegroup(
|
||||
"@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"},
|
||||
)
|
||||
|
360
CHANGELOG.md
360
CHANGELOG.md
@ -1,11 +1,182 @@
|
||||
<a name="10.1.0-rc.0"></a>
|
||||
# 10.1.0-rc.0 (2020-08-26)
|
||||
<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="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="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="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="10.1.2"></a>
|
||||
## 10.1.2 (2020-09-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** detect pipes in ICUs in template binder ([#38810](https://github.com/angular/angular/issues/38810)) ([ec2dbe7](https://github.com/angular/angular/commit/ec2dbe7)), closes [#38539](https://github.com/angular/angular/issues/38539) [#38539](https://github.com/angular/angular/issues/38539) [#38539](https://github.com/angular/angular/issues/38539)
|
||||
* **core:** clear the `RefreshTransplantedView` when detached ([#38768](https://github.com/angular/angular/issues/38768)) ([edb7f90](https://github.com/angular/angular/commit/edb7f90)), closes [#38619](https://github.com/angular/angular/issues/38619)
|
||||
* **localize:** ensure that `formatOptions` is optional ([#38787](https://github.com/angular/angular/issues/38787)) ([a47383d](https://github.com/angular/angular/commit/a47383d))
|
||||
* **router:** Ensure routes are processed in priority order and only if needed ([#38780](https://github.com/angular/angular/issues/38780)) ([9c51ba3](https://github.com/angular/angular/commit/9c51ba3)), closes [#38691](https://github.com/angular/angular/issues/38691)
|
||||
* **upgrade:** add try/catch when downgrading injectables ([#38671](https://github.com/angular/angular/issues/38671)) ([5de2ac3](https://github.com/angular/angular/commit/5de2ac3)), closes [#37579](https://github.com/angular/angular/issues/37579)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **compiler-cli:** only emit directive/pipe references that are used ([#38843](https://github.com/angular/angular/issues/38843)) ([5658405](https://github.com/angular/angular/commit/5658405))
|
||||
* **compiler-cli:** optimize computation of type-check scope information ([#38843](https://github.com/angular/angular/issues/38843)) ([ebede67](https://github.com/angular/angular/commit/ebede67))
|
||||
* **ngcc:** introduce cache for sharing data across entry-points ([#38840](https://github.com/angular/angular/issues/38840)) ([58411e7](https://github.com/angular/angular/commit/58411e7))
|
||||
* **ngcc:** reduce maximum worker count ([#38840](https://github.com/angular/angular/issues/38840)) ([ea36466](https://github.com/angular/angular/commit/ea36466))
|
||||
|
||||
|
||||
|
||||
<a name="10.1.1"></a>
|
||||
## 10.1.1 (2020-09-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** correct confusion between field and property names ([#38685](https://github.com/angular/angular/issues/38685)) ([a1c34c6](https://github.com/angular/angular/commit/a1c34c6))
|
||||
* **compiler-cli:** compute source-mappings for localized strings ([#38747](https://github.com/angular/angular/issues/38747)) ([b4eb016](https://github.com/angular/angular/commit/b4eb016)), closes [#38588](https://github.com/angular/angular/issues/38588)
|
||||
* **compiler-cli:** ensure that a declaration is available in type-to-value conversion ([#38684](https://github.com/angular/angular/issues/38684)) ([56d5ff2](https://github.com/angular/angular/commit/56d5ff2)), closes [#38670](https://github.com/angular/angular/issues/38670)
|
||||
* **core:** reset `tView` between tests in Ivy TestBed ([#38659](https://github.com/angular/angular/issues/38659)) ([efc7606](https://github.com/angular/angular/commit/efc7606)), closes [#38600](https://github.com/angular/angular/issues/38600)
|
||||
* **localize:** do not expose NodeJS typings in $localize runtime code ([#38700](https://github.com/angular/angular/issues/38700)) ([4de8dc3](https://github.com/angular/angular/commit/4de8dc3)), closes [#38692](https://github.com/angular/angular/issues/38692)
|
||||
* **localize:** enable whitespace preservation marker in XLIFF files ([#38737](https://github.com/angular/angular/issues/38737)) ([190dca0](https://github.com/angular/angular/commit/190dca0)), closes [#38679](https://github.com/angular/angular/issues/38679)
|
||||
* **localize:** install `[@angular](https://github.com/angular)/localize` in `devDependencies` by default ([#38680](https://github.com/angular/angular/issues/38680)) ([dbab744](https://github.com/angular/angular/commit/dbab744)), closes [#38329](https://github.com/angular/angular/issues/38329)
|
||||
* **localize:** render context of translation file parse errors ([#38673](https://github.com/angular/angular/issues/38673)) ([32f33f0](https://github.com/angular/angular/commit/32f33f0)), closes [#38377](https://github.com/angular/angular/issues/38377)
|
||||
* **localize:** render location in XLIFF 2 even if there is no metadata ([#38713](https://github.com/angular/angular/issues/38713)) ([ab4f953](https://github.com/angular/angular/commit/ab4f953)), closes [#38705](https://github.com/angular/angular/issues/38705)
|
||||
* **ngcc:** use aliased exported types correctly ([#38666](https://github.com/angular/angular/issues/38666)) ([6a28675](https://github.com/angular/angular/commit/6a28675)), closes [#38238](https://github.com/angular/angular/issues/38238)
|
||||
* **router:** If users are using the Alt key when clicking the router links, prioritize browser’s default behavior ([#38375](https://github.com/angular/angular/issues/38375)) ([309709d](https://github.com/angular/angular/commit/309709d))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **core:** use `ngDevMode` to tree-shake error messages ([#38612](https://github.com/angular/angular/issues/38612)) ([b084bff](https://github.com/angular/angular/commit/b084bff))
|
||||
|
||||
|
||||
|
||||
<a name="10.1.0"></a>
|
||||
# 10.1.0 (2020-09-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **bazel:** provide LinkablePackageInfo from ng_module ([#37623](https://github.com/angular/angular/issues/37623)) ([6898eab](https://github.com/angular/angular/commit/6898eab))
|
||||
* **common:** add ReadonlyMap in place of Map in keyValuePipe ([#37311](https://github.com/angular/angular/issues/37311)) ([3373453](https://github.com/angular/angular/commit/3373453)), closes [#37308](https://github.com/angular/angular/issues/37308)
|
||||
* **compiler-cli:** add `SourceFile.getOriginalLocation()` to sourcemaps package ([#32912](https://github.com/angular/angular/issues/32912)) ([6abb8d0](https://github.com/angular/angular/commit/6abb8d0))
|
||||
* **compiler-cli:** Add compiler option to report errors when assigning to restricted input fields ([#38249](https://github.com/angular/angular/issues/38249)) ([71138f6](https://github.com/angular/angular/commit/71138f6))
|
||||
* **compiler-cli:** add support for TypeScript 4.0 ([#38076](https://github.com/angular/angular/issues/38076)) ([0fc44e0](https://github.com/angular/angular/commit/0fc44e0))
|
||||
* **compiler-cli:** explain why an expression cannot be used in AOT compilations ([#37587](https://github.com/angular/angular/issues/37587)) ([712f1bd](https://github.com/angular/angular/commit/712f1bd))
|
||||
* **compiler:** support unary operators for more accurate type checking ([#37918](https://github.com/angular/angular/issues/37918)) ([874792d](https://github.com/angular/angular/commit/874792d)), closes [#20845](https://github.com/angular/angular/issues/20845) [#36178](https://github.com/angular/angular/issues/36178)
|
||||
* **core:** rename async to waitForAsync to avoid confusing ([#37583](https://github.com/angular/angular/issues/37583)) ([8f07429](https://github.com/angular/angular/commit/8f07429))
|
||||
* **core:** support injection token as predicate in queries ([#37506](https://github.com/angular/angular/issues/37506)) ([97dc85b](https://github.com/angular/angular/commit/97dc85b)), closes [#21152](https://github.com/angular/angular/issues/21152) [#36144](https://github.com/angular/angular/issues/36144)
|
||||
* **core:** update reference and doc to change `async` to `waitAsync`. ([#37583](https://github.com/angular/angular/issues/37583)) ([8fbf40b](https://github.com/angular/angular/commit/8fbf40b))
|
||||
* **forms:** AbstractControl to store raw validators in addition to combined validators function ([#37881](https://github.com/angular/angular/issues/37881)) ([ad7046b](https://github.com/angular/angular/commit/ad7046b))
|
||||
* **localize:** allow duplicate messages to be handled during extraction ([#38082](https://github.com/angular/angular/issues/38082)) ([cf9a47b](https://github.com/angular/angular/commit/cf9a47b)), closes [#38077](https://github.com/angular/angular/issues/38077)
|
||||
* **localize:** expose `canParse()` diagnostics ([#37909](https://github.com/angular/angular/issues/37909)) ([ec32eba](https://github.com/angular/angular/commit/ec32eba)), closes [#37901](https://github.com/angular/angular/issues/37901)
|
||||
* **localize:** implement message extraction tool ([#32912](https://github.com/angular/angular/issues/32912)) ([190561d](https://github.com/angular/angular/commit/190561d))
|
||||
* **platform-browser:** Allow `sms`-URLs ([#31463](https://github.com/angular/angular/issues/31463)) ([fc5c34d](https://github.com/angular/angular/commit/fc5c34d)), closes [#31462](https://github.com/angular/angular/issues/31462)
|
||||
* **platform-server:** add option for absolute URL HTTP support ([#37539](https://github.com/angular/angular/issues/37539)) ([d37049a](https://github.com/angular/angular/commit/d37049a)), closes [#37071](https://github.com/angular/angular/issues/37071)
|
||||
* **router:** better warning message when a router outlet has not been instantiated ([#30246](https://github.com/angular/angular/issues/30246)) ([1609815](https://github.com/angular/angular/commit/1609815))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** fix integration test for bazel building ([#38629](https://github.com/angular/angular/issues/38629)) ([dd82f2f](https://github.com/angular/angular/commit/dd82f2f))
|
||||
* **common:** date pipe gives wrong week number ([#37632](https://github.com/angular/angular/issues/37632)) ([ef1fb6d](https://github.com/angular/angular/commit/ef1fb6d)), closes [#33961](https://github.com/angular/angular/issues/33961)
|
||||
* **common:** narrow `NgIf` context variables in template type checker ([#36627](https://github.com/angular/angular/issues/36627)) ([9c8bc4a](https://github.com/angular/angular/commit/9c8bc4a))
|
||||
* **compiler-cli:** avoid creating value expressions for symbols from type-only imports ([#37912](https://github.com/angular/angular/issues/37912)) ([18098d3](https://github.com/angular/angular/commit/18098d3)), closes [#37900](https://github.com/angular/angular/issues/37900)
|
||||
* **compiler-cli:** ensure source-maps can handle webpack:// protocol ([#32912](https://github.com/angular/angular/issues/32912)) ([decd95e](https://github.com/angular/angular/commit/decd95e))
|
||||
* **compiler-cli:** only read source-map comment from last line ([#32912](https://github.com/angular/angular/issues/32912)) ([07a07e3](https://github.com/angular/angular/commit/07a07e3))
|
||||
* **compiler-cli:** type-check inputs that include undefined when there's coercion members ([#38273](https://github.com/angular/angular/issues/38273)) ([7525f3a](https://github.com/angular/angular/commit/7525f3a))
|
||||
* **compiler:** incorrectly inferring namespace for HTML nodes inside SVG ([#38477](https://github.com/angular/angular/issues/38477)) ([0dda97e](https://github.com/angular/angular/commit/0dda97e)), closes [#37218](https://github.com/angular/angular/issues/37218)
|
||||
* **compiler:** mark `NgModuleFactory` construction as not side effectful ([#38147](https://github.com/angular/angular/issues/38147)) ([7f8c222](https://github.com/angular/angular/commit/7f8c222))
|
||||
* **core:** Allow modification of lifecycle hooks any time before bootstrap ([#35464](https://github.com/angular/angular/issues/35464)) ([737506e](https://github.com/angular/angular/commit/737506e)), closes [#30497](https://github.com/angular/angular/issues/30497)
|
||||
* **core:** detect DI parameters in JIT mode for downleveled ES2015 classes ([#38463](https://github.com/angular/angular/issues/38463)) ([ca07da4](https://github.com/angular/angular/commit/ca07da4)), closes [#38453](https://github.com/angular/angular/issues/38453)
|
||||
* **core:** determine required DOMParser feature availability ([#36578](https://github.com/angular/angular/issues/36578)) ([#36578](https://github.com/angular/angular/issues/36578)) ([c509243](https://github.com/angular/angular/commit/c509243))
|
||||
* **core:** do not trigger CSP alert/report in Firefox and Chrome ([#36578](https://github.com/angular/angular/issues/36578)) ([#36578](https://github.com/angular/angular/issues/36578)) ([b950d46](https://github.com/angular/angular/commit/b950d46)), closes [#25214](https://github.com/angular/angular/issues/25214)
|
||||
* **core:** move generated i18n statements to the `consts` field of ComponentDef ([#38404](https://github.com/angular/angular/issues/38404)) ([cb05c01](https://github.com/angular/angular/commit/cb05c01))
|
||||
* **elements:** run strategy methods in correct zone ([#37814](https://github.com/angular/angular/issues/37814)) ([8df888d](https://github.com/angular/angular/commit/8df888d)), closes [#24181](https://github.com/angular/angular/issues/24181)
|
||||
* **forms:** handle form groups/arrays own pending async validation ([#22575](https://github.com/angular/angular/issues/22575)) ([77b62a5](https://github.com/angular/angular/commit/77b62a5)), closes [#10064](https://github.com/angular/angular/issues/10064)
|
||||
* **language-service:** non-existent module format in package output ([#37623](https://github.com/angular/angular/issues/37623)) ([413a0fb](https://github.com/angular/angular/commit/413a0fb))
|
||||
* **localize:** ensure required XLIFF parameters are serialized ([#38575](https://github.com/angular/angular/issues/38575)) ([f0af387](https://github.com/angular/angular/commit/f0af387)), closes [#38570](https://github.com/angular/angular/issues/38570)
|
||||
* **localize:** extract the correct message ids ([#38498](https://github.com/angular/angular/issues/38498)) ([ac461e1](https://github.com/angular/angular/commit/ac461e1))
|
||||
* **localize:** render ICU placeholders in extracted translation files ([#38484](https://github.com/angular/angular/issues/38484)) ([81c3e80](https://github.com/angular/angular/commit/81c3e80))
|
||||
* **localize:** render text of extracted placeholders ([#38536](https://github.com/angular/angular/issues/38536)) ([14e90be](https://github.com/angular/angular/commit/14e90be))
|
||||
* **ngcc:** detect synthesized delegate constructors for downleveled ES2015 classes ([#38463](https://github.com/angular/angular/issues/38463)) ([3b9c802](https://github.com/angular/angular/commit/3b9c802)), closes [#38453](https://github.com/angular/angular/issues/38453) [#38453](https://github.com/angular/angular/issues/38453)
|
||||
* **router:** defer loading of wildcard module until needed ([#38348](https://github.com/angular/angular/issues/38348)) ([8f708b5](https://github.com/angular/angular/commit/8f708b5)), closes [#25494](https://github.com/angular/angular/issues/25494)
|
||||
* **router:** fix navigation ignoring logic to compare to the browser url ([#37716](https://github.com/angular/angular/issues/37716)) ([a5ffca0](https://github.com/angular/angular/commit/a5ffca0)), closes [#16710](https://github.com/angular/angular/issues/16710) [#13586](https://github.com/angular/angular/issues/13586)
|
||||
* **router:** properly compare array queryParams for equality ([#37709](https://github.com/angular/angular/issues/37709)) ([#37860](https://github.com/angular/angular/issues/37860)) ([1801d0c](https://github.com/angular/angular/commit/1801d0c))
|
||||
* **router:** remove parenthesis for primary outlet segment after removing auxiliary outlet segment ([#24656](https://github.com/angular/angular/issues/24656)) ([#37163](https://github.com/angular/angular/issues/37163)) ([71f008f](https://github.com/angular/angular/commit/71f008f))
|
||||
* **router:** restore 'history.state' object for navigations coming from Angular router ([#28108](https://github.com/angular/angular/issues/28108)) ([#28176](https://github.com/angular/angular/issues/28176)) ([df76a20](https://github.com/angular/angular/commit/df76a20))
|
||||
|
||||
### Code Refactoring
|
||||
* **router:** export DefaultRouteReuseStrategy to Router public_api ([#31575](https://github.com/angular/angular/issues/31575)) ([ca79880](https://github.com/angular/angular/commit/ca79880))
|
||||
|
||||
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
* **compiler-cli:** don't emit template guards when child scope is empty ([#38418](https://github.com/angular/angular/issues/38418)) ([1388c17](https://github.com/angular/angular/commit/1388c17))
|
||||
* **compiler-cli:** fix regressions in incremental program reuse ([#37641](https://github.com/angular/angular/issues/37641)) ([5103d90](https://github.com/angular/angular/commit/5103d90))
|
||||
* **compiler-cli:** only generate directive declarations when used ([#38418](https://github.com/angular/angular/issues/38418)) ([fb8f4b4](https://github.com/angular/angular/commit/fb8f4b4))
|
||||
* **compiler-cli:** only generate type-check code for referenced DOM elements ([#38418](https://github.com/angular/angular/issues/38418)) ([f42e6ce](https://github.com/angular/angular/commit/f42e6ce))
|
||||
* **forms:** use internal `ngDevMode` flag to tree-shake error messages in prod builds ([#37821](https://github.com/angular/angular/issues/37821)) ([201a546](https://github.com/angular/angular/commit/201a546)), closes [#37697](https://github.com/angular/angular/issues/37697)
|
||||
* **ngcc:** shortcircuit tokenizing in ESM dependency host ([#37639](https://github.com/angular/angular/issues/37639)) ([bd7f440](https://github.com/angular/angular/commit/bd7f440))
|
||||
* **ngcc:** use `EntryPointManifest` to speed up noop `ProgramBaseEntryPointFinder` ([#37665](https://github.com/angular/angular/issues/37665)) ([9318e23](https://github.com/angular/angular/commit/9318e23))
|
||||
* **router:** apply prioritizedGuardValue operator to optimize CanLoad guards ([#37523](https://github.com/angular/angular/issues/37523)) ([d7dd295](https://github.com/angular/angular/commit/d7dd295))
|
||||
|
||||
|
||||
|
||||
@ -13,29 +184,6 @@
|
||||
## 10.0.14 (2020-08-26)
|
||||
|
||||
|
||||
|
||||
<a name="10.1.0-next.8"></a>
|
||||
# 10.1.0-next.8 (2020-08-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **localize:** extract the correct message ids ([#38498](https://github.com/angular/angular/issues/38498)) ([ac461e1](https://github.com/angular/angular/commit/ac461e1))
|
||||
* **router:** support lazy loading for empty path named outlets ([#38379](https://github.com/angular/angular/issues/38379)) ([7ad3264](https://github.com/angular/angular/commit/7ad3264)), closes [#12842](https://github.com/angular/angular/issues/12842)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** support unary operators for more accurate type checking ([#37918](https://github.com/angular/angular/issues/37918)) ([874792d](https://github.com/angular/angular/commit/874792d)), closes [#20845](https://github.com/angular/angular/issues/20845) [#36178](https://github.com/angular/angular/issues/36178)
|
||||
* **compiler-cli:** add support for TypeScript 4.0 ([#38076](https://github.com/angular/angular/issues/38076)) ([0fc44e0](https://github.com/angular/angular/commit/0fc44e0))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **forms:** use internal `ngDevMode` flag to tree-shake error messages in prod builds ([#37821](https://github.com/angular/angular/issues/37821)) ([201a546](https://github.com/angular/angular/commit/201a546)), closes [#37697](https://github.com/angular/angular/issues/37697)
|
||||
|
||||
|
||||
|
||||
<a name="10.0.12"></a>
|
||||
## 10.0.12 (2020-08-24)
|
||||
|
||||
@ -53,13 +201,6 @@
|
||||
* **language-service:** introduce hybrid visitor to locate AST node ([#38540](https://github.com/angular/angular/issues/38540)) ([66d8c22](https://github.com/angular/angular/commit/66d8c22))
|
||||
|
||||
|
||||
|
||||
<a name="10.1.0-next.7"></a>
|
||||
# 10.1.0-next.7 (2020-08-19)
|
||||
|
||||
This release contains various API docs improvements.
|
||||
|
||||
|
||||
<a name="10.0.11"></a>
|
||||
## 10.0.11 (2020-08-19)
|
||||
|
||||
@ -70,29 +211,6 @@ This release contains various API docs improvements.
|
||||
|
||||
|
||||
|
||||
<a name="10.1.0-next.6"></a>
|
||||
# 10.1.0-next.6 (2020-08-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** detect DI parameters in JIT mode for downleveled ES2015 classes ([#38463](https://github.com/angular/angular/issues/38463)) ([ca07da4](https://github.com/angular/angular/commit/ca07da4)), closes [#38453](https://github.com/angular/angular/issues/38453)
|
||||
* **core:** move generated i18n statements to the `consts` field of ComponentDef ([#38404](https://github.com/angular/angular/issues/38404)) ([cb05c01](https://github.com/angular/angular/commit/cb05c01))
|
||||
* **localize:** render ICU placeholders in extracted translation files ([#38484](https://github.com/angular/angular/issues/38484)) ([81c3e80](https://github.com/angular/angular/commit/81c3e80))
|
||||
* **ngcc:** detect synthesized delegate constructors for downleveled ES2015 classes ([#38463](https://github.com/angular/angular/issues/38463)) ([3b9c802](https://github.com/angular/angular/commit/3b9c802)), closes [#38453](https://github.com/angular/angular/issues/38453) [#38453](https://github.com/angular/angular/issues/38453)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **compiler-cli:** don't emit template guards when child scope is empty ([#38418](https://github.com/angular/angular/issues/38418)) ([1388c17](https://github.com/angular/angular/commit/1388c17))
|
||||
* **compiler-cli:** only generate directive declarations when used ([#38418](https://github.com/angular/angular/issues/38418)) ([fb8f4b4](https://github.com/angular/angular/commit/fb8f4b4))
|
||||
* **compiler-cli:** only generate type-check code for referenced DOM elements ([#38418](https://github.com/angular/angular/issues/38418)) ([f42e6ce](https://github.com/angular/angular/commit/f42e6ce))
|
||||
|
||||
### Code Refactoring
|
||||
* **router:** export DefaultRouteReuseStrategy to Router public_api ([#31575](https://github.com/angular/angular/issues/31575)) ([ca79880](https://github.com/angular/angular/commit/ca79880))
|
||||
|
||||
|
||||
|
||||
<a name="10.0.10"></a>
|
||||
## 10.0.10 (2020-08-17)
|
||||
|
||||
@ -108,25 +226,6 @@ This release contains various API docs improvements.
|
||||
|
||||
|
||||
|
||||
<a name="10.1.0-next.5"></a>
|
||||
# 10.1.0-next.5 (2020-08-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** avoid creating value expressions for symbols from type-only imports ([#37912](https://github.com/angular/angular/issues/37912)) ([18098d3](https://github.com/angular/angular/commit/18098d3)), closes [#37900](https://github.com/angular/angular/issues/37900)
|
||||
* **compiler-cli:** type-check inputs that include undefined when there's coercion members ([#38273](https://github.com/angular/angular/issues/38273)) ([7525f3a](https://github.com/angular/angular/commit/7525f3a))
|
||||
* **router:** defer loading of wildcard module until needed ([#38348](https://github.com/angular/angular/issues/38348)) ([8f708b5](https://github.com/angular/angular/commit/8f708b5)), closes [#25494](https://github.com/angular/angular/issues/25494)
|
||||
* **router:** restore 'history.state' object for navigations coming from Angular router ([#28108](https://github.com/angular/angular/issues/28108)) ([#28176](https://github.com/angular/angular/issues/28176)) ([df76a20](https://github.com/angular/angular/commit/df76a20))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler-cli:** Add compiler option to report errors when assigning to restricted input fields ([#38249](https://github.com/angular/angular/issues/38249)) ([71138f6](https://github.com/angular/angular/commit/71138f6))
|
||||
* **router:** better warning message when a router outlet has not been instantiated ([#30246](https://github.com/angular/angular/issues/30246)) ([1609815](https://github.com/angular/angular/commit/1609815))
|
||||
|
||||
|
||||
|
||||
<a name="10.0.9"></a>
|
||||
## 10.0.9 (2020-08-12)
|
||||
|
||||
@ -148,24 +247,6 @@ This release contains various API docs improvements.
|
||||
* **service-worker:** fix the chrome debugger syntax highlighter ([#38332](https://github.com/angular/angular/issues/38332)) ([f5d5bac](https://github.com/angular/angular/commit/f5d5bac))
|
||||
|
||||
|
||||
|
||||
<a name="10.1.0-next.4"></a>
|
||||
# 10.1.0-next.4 (2020-08-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** narrow `NgIf` context variables in template type checker ([#36627](https://github.com/angular/angular/issues/36627)) ([9c8bc4a](https://github.com/angular/angular/commit/9c8bc4a))
|
||||
* **compiler:** mark `NgModuleFactory` construction as not side effectful ([#38147](https://github.com/angular/angular/issues/38147)) ([7f8c222](https://github.com/angular/angular/commit/7f8c222))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** rename async to waitForAsync to avoid confusing ([#37583](https://github.com/angular/angular/issues/37583)) ([8f07429](https://github.com/angular/angular/commit/8f07429))
|
||||
* **core:** update reference and doc to change `async` to `waitAsync`. ([#37583](https://github.com/angular/angular/issues/37583)) ([8fbf40b](https://github.com/angular/angular/commit/8fbf40b))
|
||||
|
||||
|
||||
|
||||
<a name="10.0.8"></a>
|
||||
## 10.0.8 (2020-08-04)
|
||||
|
||||
@ -187,16 +268,6 @@ This release contains various API docs improvements.
|
||||
|
||||
|
||||
|
||||
<a name="10.1.0-next.3"></a>
|
||||
# 10.1.0-next.3 (2020-07-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **elements:** run strategy methods in correct zone ([#37814](https://github.com/angular/angular/issues/37814)) ([8df888d](https://github.com/angular/angular/commit/8df888d)), closes [#24181](https://github.com/angular/angular/issues/24181)
|
||||
|
||||
|
||||
|
||||
<a name="10.0.6"></a>
|
||||
## 10.0.6 (2020-07-28)
|
||||
|
||||
@ -210,23 +281,6 @@ This release contains various API docs improvements.
|
||||
|
||||
|
||||
|
||||
<a name="10.1.0-next.2"></a>
|
||||
# 10.1.0-next.2 (2020-07-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** Allow modification of lifecycle hooks any time before bootstrap ([#35464](https://github.com/angular/angular/issues/35464)) ([737506e](https://github.com/angular/angular/commit/737506e)), closes [#30497](https://github.com/angular/angular/issues/30497)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **common:** add ReadonlyMap in place of Map in keyValuePipe ([#37311](https://github.com/angular/angular/issues/37311)) ([3373453](https://github.com/angular/angular/commit/3373453)), closes [#37308](https://github.com/angular/angular/issues/37308)
|
||||
* **forms:** AbstractControl to store raw validators in addition to combined validators function ([#37881](https://github.com/angular/angular/issues/37881)) ([ad7046b](https://github.com/angular/angular/commit/ad7046b))
|
||||
* **localize:** allow duplicate messages to be handled during extraction ([#38082](https://github.com/angular/angular/issues/38082)) ([cf9a47b](https://github.com/angular/angular/commit/cf9a47b)), closes [#38077](https://github.com/angular/angular/issues/38077)
|
||||
|
||||
|
||||
|
||||
<a name="10.0.5"></a>
|
||||
## 10.0.5 (2020-07-22)
|
||||
|
||||
@ -261,62 +315,6 @@ This release contains various API docs improvements.
|
||||
* **bazel:** provide LinkablePackageInfo from ng_module ([#37778](https://github.com/angular/angular/issues/37778)) ([6cd10a1](https://github.com/angular/angular/commit/6cd10a1)), closes [/github.com/bazelbuild/rules_nodejs/blob/9a5de3728b05bf1647bbb87ad99f54e626604705/internal/linker/link_node_modules.bzl#L144-L146](https://github.com//github.com/bazelbuild/rules_nodejs/blob/9a5de3728b05bf1647bbb87ad99f54e626604705/internal/linker/link_node_modules.bzl/issues/L144-L146)
|
||||
|
||||
|
||||
<a name="10.1.0-next.1"></a>
|
||||
# 10.1.0-next.1 (2020-07-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** ng_module rule does not expose flat module information in Ivy ([#36971](https://github.com/angular/angular/issues/36971)) ([1550663](https://github.com/angular/angular/commit/1550663))
|
||||
* **compiler:** check more cases for pipe usage inside host bindings ([#37883](https://github.com/angular/angular/issues/37883)) ([9322b9a](https://github.com/angular/angular/commit/9322b9a)), closes [#34655](https://github.com/angular/angular/issues/34655) [#37610](https://github.com/angular/angular/issues/37610)
|
||||
* **compiler-cli:** ensure file_system handles mixed Windows drives ([#37959](https://github.com/angular/angular/issues/37959)) ([6b31155](https://github.com/angular/angular/commit/6b31155)), closes [#36777](https://github.com/angular/angular/issues/36777)
|
||||
* **language-service:** remove completion for string ([#37983](https://github.com/angular/angular/issues/37983)) ([10aba15](https://github.com/angular/angular/commit/10aba15))
|
||||
* **ngcc:** report a warning if ngcc tries to use a solution-style tsconfig ([#38003](https://github.com/angular/angular/issues/38003)) ([b358495](https://github.com/angular/angular/commit/b358495)), closes [#36386](https://github.com/angular/angular/issues/36386)
|
||||
* **router:** ensure duplicate popstate/hashchange events are handled correctly ([#37674](https://github.com/angular/angular/issues/37674)) ([9185c6e](https://github.com/angular/angular/commit/9185c6e)), closes [/github.com/angular/angular/issues/16710#issuecomment-646919529](https://github.com//github.com/angular/angular/issues/16710/issues/issuecomment-646919529) [#16710](https://github.com/angular/angular/issues/16710)
|
||||
* **service-worker:** correctly handle relative base href ([#37922](https://github.com/angular/angular/issues/37922)) ([d19ef65](https://github.com/angular/angular/commit/d19ef65)), closes [#25055](https://github.com/angular/angular/issues/25055) [#25055](https://github.com/angular/angular/issues/25055)
|
||||
* **service-worker:** correctly serve `ngsw/state` with a non-root SW scope ([#37922](https://github.com/angular/angular/issues/37922)) ([2156bee](https://github.com/angular/angular/commit/2156bee)), closes [#30505](https://github.com/angular/angular/issues/30505)
|
||||
|
||||
|
||||
|
||||
<a name="10.1.0-next.0"></a>
|
||||
# 10.1.0-next.0 (2020-07-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** date pipe gives wrong week number ([#37632](https://github.com/angular/angular/issues/37632)) ([ef1fb6d](https://github.com/angular/angular/commit/ef1fb6d)), closes [#33961](https://github.com/angular/angular/issues/33961)
|
||||
* **compiler-cli:** ensure source-maps can handle webpack:// protocol ([#32912](https://github.com/angular/angular/issues/32912)) ([decd95e](https://github.com/angular/angular/commit/decd95e))
|
||||
* **compiler-cli:** only read source-map comment from last line ([#32912](https://github.com/angular/angular/issues/32912)) ([07a07e3](https://github.com/angular/angular/commit/07a07e3))
|
||||
* **core:** determine required DOMParser feature availability ([#36578](https://github.com/angular/angular/issues/36578)) ([#36578](https://github.com/angular/angular/issues/36578)) ([c509243](https://github.com/angular/angular/commit/c509243))
|
||||
* **core:** do not trigger CSP alert/report in Firefox and Chrome ([#36578](https://github.com/angular/angular/issues/36578)) ([#36578](https://github.com/angular/angular/issues/36578)) ([b950d46](https://github.com/angular/angular/commit/b950d46)), closes [#25214](https://github.com/angular/angular/issues/25214)
|
||||
* **forms:** handle form groups/arrays own pending async validation ([#22575](https://github.com/angular/angular/issues/22575)) ([77b62a5](https://github.com/angular/angular/commit/77b62a5)), closes [#10064](https://github.com/angular/angular/issues/10064)
|
||||
* **language-service:** non-existent module format in package output ([#37623](https://github.com/angular/angular/issues/37623)) ([413a0fb](https://github.com/angular/angular/commit/413a0fb))
|
||||
* **router:** fix navigation ignoring logic to compare to the browser url ([#37716](https://github.com/angular/angular/issues/37716)) ([a5ffca0](https://github.com/angular/angular/commit/a5ffca0)), closes [#16710](https://github.com/angular/angular/issues/16710) [#13586](https://github.com/angular/angular/issues/13586)
|
||||
* **router:** properly compare array queryParams for equality ([#37709](https://github.com/angular/angular/issues/37709)) ([#37860](https://github.com/angular/angular/issues/37860)) ([1801d0c](https://github.com/angular/angular/commit/1801d0c))
|
||||
* **router:** remove parenthesis for primary outlet segment after removing auxiliary outlet segment ([#24656](https://github.com/angular/angular/issues/24656)) ([#37163](https://github.com/angular/angular/issues/37163)) ([71f008f](https://github.com/angular/angular/commit/71f008f))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **bazel:** provide LinkablePackageInfo from ng_module ([#37623](https://github.com/angular/angular/issues/37623)) ([6898eab](https://github.com/angular/angular/commit/6898eab))
|
||||
* **compiler-cli:** add `SourceFile.getOriginalLocation()` to sourcemaps package ([#32912](https://github.com/angular/angular/issues/32912)) ([6abb8d0](https://github.com/angular/angular/commit/6abb8d0))
|
||||
* **compiler-cli:** explain why an expression cannot be used in AOT compilations ([#37587](https://github.com/angular/angular/issues/37587)) ([712f1bd](https://github.com/angular/angular/commit/712f1bd))
|
||||
* **core:** support injection token as predicate in queries ([#37506](https://github.com/angular/angular/issues/37506)) ([97dc85b](https://github.com/angular/angular/commit/97dc85b)), closes [#21152](https://github.com/angular/angular/issues/21152) [#36144](https://github.com/angular/angular/issues/36144)
|
||||
* **localize:** expose `canParse()` diagnostics ([#37909](https://github.com/angular/angular/issues/37909)) ([ec32eba](https://github.com/angular/angular/commit/ec32eba)), closes [#37901](https://github.com/angular/angular/issues/37901)
|
||||
* **localize:** implement message extraction tool ([#32912](https://github.com/angular/angular/issues/32912)) ([190561d](https://github.com/angular/angular/commit/190561d))
|
||||
* **platform-browser:** Allow `sms`-URLs ([#31463](https://github.com/angular/angular/issues/31463)) ([fc5c34d](https://github.com/angular/angular/commit/fc5c34d)), closes [#31462](https://github.com/angular/angular/issues/31462)
|
||||
* **platform-server:** add option for absolute URL HTTP support ([#37539](https://github.com/angular/angular/issues/37539)) ([d37049a](https://github.com/angular/angular/commit/d37049a)), closes [#37071](https://github.com/angular/angular/issues/37071)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **compiler-cli:** fix regressions in incremental program reuse ([#37641](https://github.com/angular/angular/issues/37641)) ([5103d90](https://github.com/angular/angular/commit/5103d90))
|
||||
* **ngcc:** shortcircuit tokenizing in ESM dependency host ([#37639](https://github.com/angular/angular/issues/37639)) ([bd7f440](https://github.com/angular/angular/commit/bd7f440))
|
||||
* **ngcc:** use `EntryPointManifest` to speed up noop `ProgramBaseEntryPointFinder` ([#37665](https://github.com/angular/angular/issues/37665)) ([9318e23](https://github.com/angular/angular/commit/9318e23))
|
||||
* **router:** apply prioritizedGuardValue operator to optimize CanLoad guards ([#37523](https://github.com/angular/angular/issues/37523)) ([d7dd295](https://github.com/angular/angular/commit/d7dd295))
|
||||
|
||||
|
||||
|
||||
<a name="10.0.3"></a>
|
||||
## 10.0.3 (2020-07-08)
|
||||
|
||||
@ -598,12 +596,12 @@ https://github.com/microsoft/TypeScript/issues/38374 for more
|
||||
information and updates.
|
||||
|
||||
If you used Closure Compiler with Angular in the past, you will likely
|
||||
be better off consuming Angular packages built from sources directly
|
||||
be better off consuming Angular packages built from sources directly
|
||||
rather than consuming the version we publish on npm,
|
||||
which is primarily optimized for Webpack/Rollup + Terser build pipeline.
|
||||
|
||||
As a temporary workaround, you might consider using your current build
|
||||
pipeline with Closure flag `--compilation_level=SIMPLE`. This flag
|
||||
pipeline with Closure flag `--compilation_level=SIMPLE`. This flag
|
||||
will ensure that your build pipeline produces buildable and
|
||||
runnable artifacts, at the cost of increased payload size due to
|
||||
advanced optimizations being disabled.
|
||||
@ -611,17 +609,17 @@ advanced optimizations being disabled.
|
||||
If you were affected by this change, please help us understand your
|
||||
needs by leaving a comment on https://github.com/angular/angular/issues/37234.
|
||||
|
||||
* **core:** make generic mandatory for ModuleWithProviders
|
||||
* **core:** make generic mandatory for ModuleWithProviders
|
||||
|
||||
A generic type parameter has always been required for the `ModuleWithProviders` pattern to work with Ivy, but prior to this commit, View Engine allowed the generic type to be omitted (though support was officially deprecated).
|
||||
If you're using `ModuleWithProviders` without a generic type in your application code, a v10 migration will update your code for you.
|
||||
If you're using `ModuleWithProviders` without a generic type in your application code, a v10 migration will update your code for you.
|
||||
|
||||
However, if you are using View Engine and also depending on a library that omits the generic type, you will now get a build time error similar to:
|
||||
|
||||
```
|
||||
error TS2314: Generic type 'ModuleWithProviders<T>' requires 1 type argument(s).
|
||||
```
|
||||
|
||||
|
||||
In this case, ngcc won't help you (because it's Ivy-only) and the migration only covers application code.
|
||||
You should contact the library author to fix their library to provide a type parameter when they use this class.
|
||||
|
||||
@ -1949,7 +1947,7 @@ API surface going forward.
|
||||
* **core:** Injector.get now accepts abstract classes to return
|
||||
type-safe values. Previous implementation returned `any` through the
|
||||
deprecated implementation.
|
||||
* Angular now compiles with Ivy by default ([#32219](https://github.com/angular/angular/issues/32219)) ([ec4381d](https://github.com/angular/angular/commit/ec4381d)).
|
||||
* Angular now compiles with Ivy by default ([#32219](https://github.com/angular/angular/issues/32219)) ([ec4381d](https://github.com/angular/angular/commit/ec4381d)).
|
||||
|
||||
If you aren't familiar with Ivy, read our [blog post about the Ivy preview](https://blog.angular.io/its-time-for-the-compatibility-opt-in-preview-of-ivy-38f3542a282f?gi=8bfeb44b05c) and see the list of changes [here](https://docs.google.com/document/d/1Dije0AsJ0PxL3NaeNPxpYDeapj30b_QC0xfeIvIIzgg/preview).
|
||||
|
||||
|
@ -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.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
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?
|
||||
@ -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.
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
git commit --all
|
||||
```
|
||||
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`.
|
||||
|
||||
If we ask for changes via code reviews then:
|
||||
|
||||
* Make the required updates.
|
||||
* 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):
|
||||
#### Addressing review feedback
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
git push -f
|
||||
```
|
||||
If we ask for changes via code reviews then:
|
||||
|
||||
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!
|
||||
|
||||
|
||||
##### 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, you can safely delete your branch and pull the changes from the main (upstream) repository:
|
||||
@ -230,7 +265,6 @@ Must be one of the following:
|
||||
* **fix**: A bug fix
|
||||
* **perf**: A code change that improves performance
|
||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
|
||||
|
||||
@ -350,7 +384,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
|
||||
[dev-doc]: https://github.com/angular/angular/blob/master/docs/DEVELOPER.md
|
||||
[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
|
||||
[js-style-guide]: https://google.github.io/styleguide/jsguide.html
|
||||
[jsfiddle]: http://jsfiddle.net
|
||||
|
@ -1,4 +1,5 @@
|
||||
[](https://circleci.com/gh/angular/workflows/angular/tree/master)
|
||||
[](https://discord.gg/angular)
|
||||
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://www.npmjs.com/@angular/core)
|
||||
|
||||
|
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
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"],
|
||||
sha256 = "4952ef879704ab4ad6729a29007e7094aef213ea79e9f2e94cbe1c9a753e63ef",
|
||||
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
|
||||
@ -17,7 +17,7 @@ http_archive(
|
||||
# assert on that.
|
||||
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
|
||||
node_repositories(
|
||||
@ -39,23 +39,18 @@ yarn_install(
|
||||
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("//packages/bazel:package.bzl", "rules_angular_dev_dependencies")
|
||||
|
||||
rules_angular_dev_dependencies()
|
||||
|
||||
# Load protractor dependencies
|
||||
load("@npm_bazel_protractor//:package.bzl", "npm_bazel_protractor_dependencies")
|
||||
load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies")
|
||||
|
||||
npm_bazel_protractor_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()
|
||||
|
||||
@ -68,11 +63,6 @@ load("//dev-infra/browsers:browser_repositories.bzl", "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
|
||||
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
|
||||
# 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:
|
||||
# https://github.com/bazelbuild/bazel-toolchains/blob/3.2.0/configs/ubuntu16_04_clang/versions.bzl
|
||||
base_container_digest = "sha256:5e750dd878df9fcf4e185c6f52b9826090f6e532b097f286913a428290622332",
|
||||
# https://github.com/bazelbuild/bazel-toolchains/blob/3.5.1/configs/ubuntu16_04_clang/versions.bzl
|
||||
base_container_digest = "sha256:f6568d8168b14aafd1b707019927a63c2d37113a03bcee188218f99bd0327ea1",
|
||||
# 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>
|
||||
# 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 tooling for setting up examples for development; and generating live-example and zip files from the examples.
|
||||
|
||||
<a name="developer-tasks"></a>
|
||||
## Developer tasks
|
||||
|
||||
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
|
||||
|
@ -16,13 +16,6 @@ import {BuildNums, PrNums, SHA} from './constants';
|
||||
|
||||
const logger = new Logger('mock-external-apis');
|
||||
|
||||
const log = (...args: any[]) => {
|
||||
// Filter out non-matching URL checks
|
||||
if (!/^matching.+: false$/.test(args[0])) {
|
||||
logger.log(...args);
|
||||
}
|
||||
};
|
||||
|
||||
const AIO_CIRCLE_CI_TOKEN = getEnvVar('AIO_CIRCLE_CI_TOKEN');
|
||||
const AIO_GITHUB_TOKEN = getEnvVar('AIO_GITHUB_TOKEN');
|
||||
|
||||
@ -91,8 +84,8 @@ const createArchive = (buildNum: number, prNum: number, sha: string) => {
|
||||
};
|
||||
|
||||
// Create request scopes
|
||||
const circleCiApi = nock(CIRCLE_CI_API_HOST).log(log).persist();
|
||||
const githubApi = nock(GITHUB_API_HOST).log(log).persist().matchHeader('Authorization', `token ${AIO_GITHUB_TOKEN}`);
|
||||
const circleCiApi = nock(CIRCLE_CI_API_HOST).persist();
|
||||
const githubApi = nock(GITHUB_API_HOST).persist().matchHeader('Authorization', `token ${AIO_GITHUB_TOKEN}`);
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
|
@ -27,28 +27,28 @@
|
||||
"body-parser": "^1.19.0",
|
||||
"delete-empty": "^3.0.0",
|
||||
"express": "^4.17.1",
|
||||
"jasmine": "^3.5.0",
|
||||
"nock": "^12.0.3",
|
||||
"node-fetch": "^2.6.0",
|
||||
"jasmine": "^3.6.1",
|
||||
"nock": "^13.0.4",
|
||||
"node-fetch": "^2.6.1",
|
||||
"shelljs": "^0.8.4",
|
||||
"source-map-support": "^0.5.19",
|
||||
"tar-stream": "^2.1.2",
|
||||
"tslib": "^1.11.1"
|
||||
"tar-stream": "^2.1.3",
|
||||
"tslib": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/body-parser": "^1.19.0",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/jasmine": "^3.5.10",
|
||||
"@types/express": "^4.17.8",
|
||||
"@types/jasmine": "^3.5.14",
|
||||
"@types/nock": "^11.1.0",
|
||||
"@types/node": "^13.13.2",
|
||||
"@types/node": "^14.6.4",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/shelljs": "^0.8.7",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"nodemon": "^2.0.3",
|
||||
"@types/shelljs": "^0.8.8",
|
||||
"@types/supertest": "^2.0.10",
|
||||
"nodemon": "^2.0.4",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"supertest": "^4.0.2",
|
||||
"tslint": "^6.1.1",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-jasmine-noSkipOrFocus": "^1.0.9",
|
||||
"typescript": "^3.8.3"
|
||||
"typescript": "^4.0.2"
|
||||
}
|
||||
}
|
||||
|
@ -214,23 +214,24 @@ describe('GithubApi', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should call \'https.request()\' with the correct options', () => {
|
||||
it('should call \'https.request()\' with the correct options', async () => {
|
||||
const requestHandler = nock('https://api.github.com')
|
||||
.intercept('/path', 'method')
|
||||
.reply(200);
|
||||
|
||||
(api as any).request('method', '/path');
|
||||
await (api as any).request('method', '/path');
|
||||
requestHandler.done();
|
||||
});
|
||||
|
||||
|
||||
it('should add the \'Authorization\' header containing the \'githubToken\'', () => {
|
||||
it('should add the \'Authorization\' header containing the \'githubToken\'', async () => {
|
||||
const requestHandler = nock('https://api.github.com')
|
||||
.intercept('/path', 'method', undefined, {
|
||||
reqheaders: {Authorization: 'token 12345'},
|
||||
})
|
||||
.reply(200);
|
||||
(api as any).request('method', '/path');
|
||||
|
||||
await (api as any).request('method', '/path');
|
||||
requestHandler.done();
|
||||
});
|
||||
|
||||
@ -244,12 +245,13 @@ describe('GithubApi', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should \'JSON.stringify\' and send the data along with the request', () => {
|
||||
it('should \'JSON.stringify\' and send the data along with the request', async () => {
|
||||
const data = {key: 'value'};
|
||||
const requestHandler = nock('https://api.github.com')
|
||||
.intercept('/path', 'method', JSON.stringify(data))
|
||||
.reply(200);
|
||||
(api as any).request('method', '/path', data);
|
||||
|
||||
await (api as any).request('method', '/path', data);
|
||||
requestHandler.done();
|
||||
});
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,7 @@ you don't need to specify values for those.
|
||||
The domain name of the server.
|
||||
|
||||
- `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`.
|
||||
|
||||
- `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
|
||||
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
|
||||
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.
|
||||
Here too, we use the token by @mary-poppins.
|
||||
|
||||
|
2
aio/content/examples/.gitignore
vendored
2
aio/content/examples/.gitignore
vendored
@ -17,6 +17,7 @@
|
||||
**/e2e/tsconfig.e2e.json
|
||||
**/src/karma.conf.js
|
||||
**/.angular-cli.json
|
||||
**/.browserslistrc
|
||||
**/.editorconfig
|
||||
**/.gitignore
|
||||
**/angular.json
|
||||
@ -30,7 +31,6 @@
|
||||
**/tslint.json
|
||||
**/karma-test-shim.js
|
||||
**/browser-test-shim.js
|
||||
**/browserslist
|
||||
**/node_modules
|
||||
|
||||
# built files
|
||||
|
@ -21,11 +21,13 @@ import { ItemDirective } from './item.directive';
|
||||
ItemDirective
|
||||
],
|
||||
// #enddocregion declarations
|
||||
// #docregion imports
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule
|
||||
],
|
||||
// #enddocregion imports
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
@ -1,12 +1,8 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
import { browser, by, element } from 'protractor';
|
||||
|
||||
describe('Component Communication Cookbook Tests', () => {
|
||||
|
||||
// Note: '?e2e' which app can read to know it is running in protractor
|
||||
// e.g. `if (!/e2e/.test(location.search)) { ...`
|
||||
beforeAll(() => {
|
||||
browser.get('?e2e');
|
||||
});
|
||||
beforeEach(() => browser.get(browser.baseUrl));
|
||||
|
||||
describe('Parent-to-child communication', () => {
|
||||
// #docregion parent-to-child
|
||||
@ -15,7 +11,7 @@ describe('Component Communication Cookbook Tests', () => {
|
||||
const masterName = 'Master';
|
||||
|
||||
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'));
|
||||
|
||||
for (let i = 0; i < heroNames.length; i++) {
|
||||
@ -35,7 +31,7 @@ describe('Component Communication Cookbook Tests', () => {
|
||||
it('should display trimmed, non-empty names', () => {
|
||||
const nonEmptyNameIndex = 0;
|
||||
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 displayName = hero.element(by.tagName('h3')).getText();
|
||||
@ -45,7 +41,7 @@ describe('Component Communication Cookbook Tests', () => {
|
||||
it('should replace empty name with default name', () => {
|
||||
const emptyNameIndex = 1;
|
||||
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 displayName = hero.element(by.tagName('h3')).getText();
|
||||
@ -70,38 +66,36 @@ describe('Component Communication Cookbook Tests', () => {
|
||||
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 newMinorButton = repoTag.all(by.tagName('button')).get(0);
|
||||
|
||||
newMinorButton.click().then(() => {
|
||||
newMinorButton.click().then(() => {
|
||||
const actual = getActual();
|
||||
await newMinorButton.click();
|
||||
await newMinorButton.click();
|
||||
|
||||
const labelAfter2Minor = 'Version 1.25';
|
||||
const logAfter2Minor = 'minor changed from 24 to 25';
|
||||
const actual = getActual();
|
||||
|
||||
expect(actual.label).toBe(labelAfter2Minor);
|
||||
expect(actual.count).toBe(3);
|
||||
expect(actual.logs.get(2).getText()).toBe(logAfter2Minor);
|
||||
});
|
||||
});
|
||||
const labelAfter2Minor = 'Version 1.25';
|
||||
const logAfter2Minor = 'minor changed from 24 to 25';
|
||||
|
||||
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 newMajorButton = repoTag.all(by.tagName('button')).get(1);
|
||||
|
||||
newMajorButton.click().then(() => {
|
||||
const actual = getActual();
|
||||
await newMajorButton.click();
|
||||
const actual = getActual();
|
||||
|
||||
const labelAfterMajor = 'Version 2.0';
|
||||
const logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
|
||||
const labelAfterMajor = 'Version 2.0';
|
||||
const logAfterMajor = 'major changed from 1 to 2, minor changed from 23 to 0';
|
||||
|
||||
expect(actual.label).toBe(labelAfterMajor);
|
||||
expect(actual.count).toBe(4);
|
||||
expect(actual.logs.get(3).getText()).toBe(logAfterMajor);
|
||||
});
|
||||
expect(actual.label).toBe(labelAfterMajor);
|
||||
expect(actual.count).toBe(2);
|
||||
expect(actual.logs.get(1).getText()).toBe(logAfterMajor);
|
||||
});
|
||||
|
||||
function getActual() {
|
||||
@ -118,110 +112,125 @@ describe('Component Communication Cookbook Tests', () => {
|
||||
}
|
||||
// ...
|
||||
// #enddocregion parent-to-child-onchanges
|
||||
|
||||
});
|
||||
|
||||
describe('Child-to-parent communication', () => {
|
||||
// #docregion child-to-parent
|
||||
// ...
|
||||
it('should not emit the event initially', () => {
|
||||
const voteLabel = element(by.tagName('app-vote-taker'))
|
||||
.element(by.tagName('h3')).getText();
|
||||
expect(voteLabel).toBe('Agree: 0, Disagree: 0');
|
||||
const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
|
||||
expect(voteLabel.getText()).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)
|
||||
.all(by.tagName('button')).get(0);
|
||||
agreeButton1.click().then(() => {
|
||||
const voteLabel = element(by.tagName('app-vote-taker'))
|
||||
.element(by.tagName('h3')).getText();
|
||||
expect(voteLabel).toBe('Agree: 1, Disagree: 0');
|
||||
});
|
||||
|
||||
await agreeButton1.click();
|
||||
|
||||
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)
|
||||
.all(by.tagName('button')).get(1);
|
||||
agreeButton1.click().then(() => {
|
||||
const voteLabel = element(by.tagName('app-vote-taker'))
|
||||
.element(by.tagName('h3')).getText();
|
||||
expect(voteLabel).toBe('Agree: 1, Disagree: 1');
|
||||
});
|
||||
|
||||
await agreeButton1.click();
|
||||
|
||||
expect(voteLabel.getText()).toBe('Agree: 0, Disagree: 1');
|
||||
});
|
||||
// ...
|
||||
// #enddocregion child-to-parent
|
||||
});
|
||||
|
||||
// Can't run timer tests in protractor because
|
||||
// interaction w/ zones causes all tests to freeze & timeout.
|
||||
xdescribe('Parent calls child via local var', () => {
|
||||
countDownTimerTests('countdown-parent-lv');
|
||||
describe('Parent calls child via local var', () => {
|
||||
countDownTimerTests('app-countdown-parent-lv');
|
||||
});
|
||||
|
||||
xdescribe('Parent calls ViewChild', () => {
|
||||
countDownTimerTests('countdown-parent-vc');
|
||||
describe('Parent calls ViewChild', () => {
|
||||
countDownTimerTests('app-countdown-parent-vc');
|
||||
});
|
||||
|
||||
function countDownTimerTests(parentTag: string) {
|
||||
// #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 message = parent.element(by.tagName('app-countdown-timer')).getText();
|
||||
browser.sleep(10); // give `seconds` a chance to catchup with `message`
|
||||
const seconds = parent.element(by.className('seconds')).getText();
|
||||
expect(message).toContain(seconds);
|
||||
const startButton = parent.element(by.buttonText('Start'));
|
||||
const seconds = parent.element(by.className('seconds'));
|
||||
const timer = parent.element(by.tagName('app-countdown-timer'));
|
||||
|
||||
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 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(() => {
|
||||
const message = parent.element(by.tagName('app-countdown-timer')).getText();
|
||||
expect(message).toContain('Holding');
|
||||
});
|
||||
await startButton.click();
|
||||
expect(await timer.getText()).not.toContain('Holding');
|
||||
|
||||
await stopButton.click();
|
||||
expect(await timer.getText()).toContain('Holding');
|
||||
});
|
||||
// ...
|
||||
// #enddocregion countdown-timer-tests
|
||||
}
|
||||
|
||||
|
||||
describe('Parent and children communicate via a service', () => {
|
||||
// #docregion bidirectional-service
|
||||
// ...
|
||||
it('should announce a mission', () => {
|
||||
it('should announce a mission', async () => {
|
||||
const missionControl = element(by.tagName('app-mission-control'));
|
||||
const announceButton = missionControl.all(by.tagName('button')).get(0);
|
||||
announceButton.click().then(() => {
|
||||
const history = missionControl.all(by.tagName('li'));
|
||||
expect(history.count()).toBe(1);
|
||||
expect(history.get(0).getText()).toMatch(/Mission.* announced/);
|
||||
});
|
||||
const history = missionControl.all(by.tagName('li'));
|
||||
|
||||
await announceButton.click();
|
||||
|
||||
expect(history.count()).toBe(1);
|
||||
expect(history.get(0).getText()).toMatch(/Mission.* announced/);
|
||||
});
|
||||
|
||||
it('should confirm the mission by Lovell', () => {
|
||||
testConfirmMission(1, 2, 'Lovell');
|
||||
it('should confirm the mission by Lovell', async () => {
|
||||
await testConfirmMission(1, 'Lovell');
|
||||
});
|
||||
|
||||
it('should confirm the mission by Haise', () => {
|
||||
testConfirmMission(3, 3, 'Haise');
|
||||
it('should confirm the mission by Haise', async () => {
|
||||
await testConfirmMission(3, 'Haise');
|
||||
});
|
||||
|
||||
it('should confirm the mission by Swigert', () => {
|
||||
testConfirmMission(2, 4, 'Swigert');
|
||||
it('should confirm the mission by Swigert', async () => {
|
||||
await testConfirmMission(2, 'Swigert');
|
||||
});
|
||||
|
||||
function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
|
||||
const confirmedLog = ' confirmed the mission';
|
||||
async function testConfirmMission(buttonIndex: number, astronaut: string) {
|
||||
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);
|
||||
confirmButton.click().then(() => {
|
||||
const history = missionControl.all(by.tagName('li'));
|
||||
expect(history.count()).toBe(expectedLogCount);
|
||||
expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + confirmedLog);
|
||||
});
|
||||
const history = missionControl.all(by.tagName('li'));
|
||||
|
||||
await announceButton.click();
|
||||
await confirmButton.click();
|
||||
|
||||
expect(history.count()).toBe(2);
|
||||
expect(history.get(1).getText()).toBe(`${astronaut} confirmed the mission`);
|
||||
}
|
||||
// ...
|
||||
// #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>
|
||||
</div>
|
||||
<a href="#top" class="to-top">Back to Top</a>
|
||||
<hr>
|
||||
|
||||
<hr>
|
||||
<div id="parent-to-child-local-var">
|
||||
<app-countdown-parent-lv></app-countdown-parent-lv>
|
||||
</div>
|
||||
<a href="#top" class="to-top">Back to Top</a>
|
||||
<hr>
|
||||
|
||||
<hr>
|
||||
<div id="parent-to-view-child">
|
||||
<app-countdown-parent-vc></app-countdown-parent-vc>
|
||||
</div>
|
||||
<a href="#top" class="to-top">Back to Top</a>
|
||||
<hr>
|
||||
|
||||
<hr>
|
||||
<div id="bidirectional-service">
|
||||
<app-mission-control></app-mission-control>
|
||||
</div>
|
||||
<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 { AppComponent } from './app.component';
|
||||
@ -15,10 +15,17 @@ import { VersionParentComponent } from './version-parent.component';
|
||||
import { VoterComponent } from './voter.component';
|
||||
import { VoteTakerComponent } from './votetaker.component';
|
||||
|
||||
const directives: any[] = [
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
AstronautComponent,
|
||||
CountdownLocalVarParentComponent,
|
||||
CountdownTimerComponent,
|
||||
CountdownViewChildParentComponent,
|
||||
HeroChildComponent,
|
||||
HeroParentComponent,
|
||||
MissionControlComponent,
|
||||
@ -27,28 +34,8 @@ const directives: any[] = [
|
||||
VersionChildComponent,
|
||||
VersionParentComponent,
|
||||
VoterComponent,
|
||||
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
|
||||
VoteTakerComponent,
|
||||
],
|
||||
declarations: directives,
|
||||
bootstrap: [ AppComponent ],
|
||||
schemas
|
||||
})
|
||||
export class AppModule { }
|
||||
|
@ -1,19 +1,16 @@
|
||||
// #docregion
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-countdown-timer',
|
||||
template: '<p>{{message}}</p>'
|
||||
})
|
||||
export class CountdownTimerComponent implements OnInit, OnDestroy {
|
||||
export class CountdownTimerComponent implements OnDestroy {
|
||||
|
||||
intervalId = 0;
|
||||
message = '';
|
||||
seconds = 11;
|
||||
|
||||
clearTimer() { clearInterval(this.intervalId); }
|
||||
|
||||
ngOnInit() { this.start(); }
|
||||
ngOnDestroy() { this.clearTimer(); }
|
||||
|
||||
start() { this.countDown(); }
|
||||
@ -22,6 +19,8 @@ export class CountdownTimerComponent implements OnInit, OnDestroy {
|
||||
this.message = `Holding at T-${this.seconds} seconds`;
|
||||
}
|
||||
|
||||
private clearTimer() { clearInterval(this.intervalId); }
|
||||
|
||||
private countDown() {
|
||||
this.clearTimer();
|
||||
this.intervalId = window.setInterval(() => {
|
||||
|
@ -24,7 +24,7 @@ export class UploaderService {
|
||||
// }
|
||||
|
||||
upload(file: File) {
|
||||
if (!file) { return; }
|
||||
if (!file) { return of<string>(); }
|
||||
|
||||
// COULD HAVE WRITTEN:
|
||||
// return this.http.post('/upload/file', file, {
|
||||
|
@ -41,7 +41,6 @@
|
||||
<!-- #enddocregion translated-plural -->
|
||||
<!-- #docregion translated-select -->
|
||||
<!-- #docregion translate-select-1 -->
|
||||
</trans-unit>
|
||||
<trans-unit id="f99f34ac9bd4606345071bd813858dec29f3b7d1" datatype="html">
|
||||
<source>The author is <x id="ICU" equiv-text="{gender, select, male {...} female {...} other {...}}"/></source>
|
||||
<target>L'auteur est <x id="ICU" equiv-text="{gender, select, male {...} female {...} other {...}}"/></target>
|
||||
|
@ -1,3 +1,4 @@
|
||||
// TODO: Add unit tests for this file.
|
||||
// tslint:disable: no-output-native
|
||||
// #docregion
|
||||
import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core';
|
||||
|
@ -2,7 +2,11 @@
|
||||
"tests": [
|
||||
{
|
||||
"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';
|
||||
|
||||
// #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
|
||||
function sequenceSubscriber(observer) {
|
||||
// synchronously deliver 1, 2, and 3, then complete
|
||||
observer.next(1);
|
||||
observer.next(2);
|
||||
observer.next(3);
|
||||
observer.complete();
|
||||
// unsubscribe function doesn't need to do anything in this
|
||||
// because values are delivered synchronously
|
||||
return {unsubscribe() {}};
|
||||
}
|
||||
|
||||
// unsubscribe function doesn't need to do anything in this
|
||||
// because values are delivered synchronously
|
||||
return {unsubscribe() {}};
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
function fromEvent(target, eventName) {
|
||||
@ -51,16 +52,18 @@ function fromEvent(target, eventName) {
|
||||
|
||||
// #enddocregion fromevent
|
||||
|
||||
// #docregion fromevent_use
|
||||
export function docRegionFromEvent(document) {
|
||||
// #docregion fromevent_use
|
||||
|
||||
const ESC_KEY = 27;
|
||||
const nameInput = document.getElementById('name') as HTMLInputElement;
|
||||
const ESC_KEY = 27;
|
||||
const nameInput = document.getElementById('name') as HTMLInputElement;
|
||||
|
||||
const subscription = fromEvent(nameInput, 'keydown')
|
||||
.subscribe((e: KeyboardEvent) => {
|
||||
const subscription = fromEvent(nameInput, 'keydown').subscribe((e: KeyboardEvent) => {
|
||||
if (e.keyCode === ESC_KEY) {
|
||||
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';
|
||||
|
||||
// #docregion
|
||||
|
||||
// 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';
|
||||
|
||||
// #docregion delay_sequence
|
||||
export function docRegionDelaySequence(console) {
|
||||
// #docregion delay_sequence
|
||||
function sequenceSubscriber(observer) {
|
||||
const seq = [1, 2, 3];
|
||||
let timeoutId;
|
||||
|
||||
function sequenceSubscriber(observer) {
|
||||
const seq = [1, 2, 3];
|
||||
let timeoutId;
|
||||
// Will run through an array of numbers, emitting one value
|
||||
// per second until it gets to the end of the array.
|
||||
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.
|
||||
function doInSequence(arr, idx) {
|
||||
timeoutId = setTimeout(() => {
|
||||
function doSequence(observer, arr, idx) {
|
||||
return setTimeout(() => {
|
||||
observer.next(arr[idx]);
|
||||
if (idx === arr.length - 1) {
|
||||
observer.complete();
|
||||
} else {
|
||||
doInSequence(arr, ++idx);
|
||||
doSequence(observer, arr, ++idx);
|
||||
}
|
||||
}, 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
|
||||
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(() => {
|
||||
// Subscribe starts the clock, and begins to emit after 1 second
|
||||
multicastSequence.subscribe({
|
||||
next(num) { console.log('2nd subscribe: ' + num); },
|
||||
complete() { console.log('2nd sequence finished.'); }
|
||||
next(num) { console.log('1st subscribe: ' + num); },
|
||||
complete() { console.log('1st sequence finished.'); }
|
||||
});
|
||||
}, 1500);
|
||||
|
||||
// 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
|
||||
// After 1 1/2 seconds, subscribe again (should "miss" the first value).
|
||||
setTimeout(() => {
|
||||
multicastSequence.subscribe({
|
||||
next(num) { console.log('2nd subscribe: ' + num); },
|
||||
complete() { console.log('2nd sequence finished.'); }
|
||||
});
|
||||
}, 1500);
|
||||
|
||||
// #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
|
||||
const myObservable = of(1, 2, 3);
|
||||
// Create observer object
|
||||
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
|
||||
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'),
|
||||
};
|
||||
// Execute with the observer object
|
||||
myObservable.subscribe(myObserver);
|
||||
|
||||
// Execute with the observer object
|
||||
myObservable.subscribe(myObserver);
|
||||
// Logs:
|
||||
// Observer got a next value: 1
|
||||
// Observer got a next value: 2
|
||||
// Observer got a next value: 3
|
||||
// Observer got a complete notification
|
||||
// Logs:
|
||||
// Observer got a next value: 1
|
||||
// Observer got a next value: 2
|
||||
// Observer got a next value: 3
|
||||
// Observer got a complete notification
|
||||
|
||||
// #enddocregion observer
|
||||
// #enddocregion observer
|
||||
|
||||
// #docregion sub_fn
|
||||
myObservable.subscribe(
|
||||
x => console.log('Observer got a next value: ' + x),
|
||||
err => console.error('Observer got an error: ' + err),
|
||||
() => console.log('Observer got a complete notification')
|
||||
);
|
||||
// #enddocregion sub_fn
|
||||
// #docregion sub_fn
|
||||
myObservable.subscribe(
|
||||
x => console.log('Observer got a next value: ' + x),
|
||||
err => console.error('Observer got an error: ' + err),
|
||||
() => console.log('Observer got a complete notification')
|
||||
);
|
||||
// #enddocregion sub_fn
|
||||
}
|
||||
|
@ -2,7 +2,11 @@
|
||||
"tests": [
|
||||
{
|
||||
"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 @@
|
||||
|
||||
import { pipe, range, timer, zip } from 'rxjs';
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { of, pipe, range, throwError, timer, zip } from 'rxjs';
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
import { retryWhen, map, mergeMap } from 'rxjs/operators';
|
||||
import { map, mergeMap, retryWhen } from 'rxjs/operators';
|
||||
|
||||
function backoff(maxTries, ms) {
|
||||
return pipe(
|
||||
retryWhen(attempts => zip(range(1, maxTries), attempts)
|
||||
.pipe(
|
||||
map(([i]) => i * i),
|
||||
mergeMap(i => timer(i * ms))
|
||||
)
|
||||
)
|
||||
);
|
||||
export function backoff(maxTries, delay) {
|
||||
return pipe(
|
||||
retryWhen(attempts =>
|
||||
zip(range(1, maxTries + 1), attempts).pipe(
|
||||
mergeMap(([i, err]) => (i > maxTries) ? throwError(err) : of(i)),
|
||||
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')
|
||||
.pipe(backoff(3, 250))
|
||||
.subscribe(data => handleData(data));
|
||||
|
||||
function handleData(data) {
|
||||
// ...
|
||||
.subscribe(function handleData(data) { /* ... */ });
|
||||
// #enddocregion
|
||||
}
|
||||
|
@ -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';
|
||||
import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';
|
||||
/*
|
||||
Because of how the code is merged together using the doc regions,
|
||||
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(
|
||||
map((e: KeyboardEvent) => (e.target as HTMLInputElement).value),
|
||||
filter(text => text.length > 2),
|
||||
debounceTime(10),
|
||||
distinctUntilChanged(),
|
||||
switchMap(() => ajax('/api/endpoint'))
|
||||
);
|
||||
typeahead.subscribe(data => {
|
||||
// Handle the data from the API
|
||||
});
|
||||
|
||||
typeahead.subscribe(data => {
|
||||
// Handle the data from the API
|
||||
});
|
||||
// #enddocregion
|
||||
return typeahead;
|
||||
}
|
||||
|
@ -2,7 +2,11 @@
|
||||
"tests": [
|
||||
{
|
||||
"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 @@
|
||||
|
||||
import { 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:no-shadowed-variable */
|
||||
/* tslint:disable:align */
|
||||
// #docregion
|
||||
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
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'); }
|
||||
});
|
||||
import { of } from 'rxjs';
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
import { map, catchError } from 'rxjs/operators';
|
||||
|
||||
// #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
|
||||
|
||||
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));
|
||||
import { of, pipe } from 'rxjs';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
|
||||
// #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
|
||||
|
||||
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));
|
||||
import { of } from 'rxjs';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
|
||||
// #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 @@
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
import { of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
// #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 occured.'],
|
||||
['Subscribed to AJAX'],
|
||||
['Error occured.'],
|
||||
['Subscribed to AJAX'],
|
||||
['Error occured.'],
|
||||
['Subscribed to AJAX'],
|
||||
['Error occured.'],
|
||||
['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 @@
|
||||
|
||||
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:no-shadowed-variable */
|
||||
/* tslint:disable:align */
|
||||
// #docregion
|
||||
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
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'); }
|
||||
});
|
||||
import { of } from 'rxjs';
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
import { map, retry, catchError } from 'rxjs/operators';
|
||||
|
||||
// #enddocregion
|
||||
|
||||
export function docRegionDefault(console, ajax) {
|
||||
// #docregion
|
||||
const apiData = ajax('/api/data').pipe(
|
||||
map((res: any) => {
|
||||
if (!res.response) {
|
||||
console.log('Error occured.');
|
||||
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 @@
|
||||
|
||||
// #docregion promise
|
||||
|
||||
import { from } from 'rxjs';
|
||||
|
||||
// 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
|
||||
|
||||
// #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
|
||||
|
||||
|
||||
// #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:no-shadowed-variable */
|
||||
/* tslint:disable:align */
|
||||
// #docregion ajax
|
||||
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ import { Hero } from '../shared/hero.model';
|
||||
template: `
|
||||
<section>
|
||||
Our list of heroes:
|
||||
<hero-profile *ngFor="let hero of heroes" [hero]="hero">
|
||||
</hero-profile>
|
||||
<toh-hero *ngFor="let hero of heroes" [hero]="hero">
|
||||
</toh-hero>
|
||||
Total powers: {{totalPowers}}<br>
|
||||
Average power: {{totalPowers / heroes.length}}
|
||||
</section>
|
||||
|
@ -28,7 +28,7 @@ import {
|
||||
ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { addMatchers, newEvent, click } from '../../testing';
|
||||
import { addMatchers, click } from '../../testing';
|
||||
|
||||
export class NotProvided extends ValueService { /* example below */ }
|
||||
beforeEach(addMatchers);
|
||||
@ -274,9 +274,11 @@ describe('demo (with TestBed):', () => {
|
||||
expect(comp.name).toBe(expectedOrigName,
|
||||
`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
|
||||
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();
|
||||
})
|
||||
.then(() => {
|
||||
@ -312,9 +314,11 @@ describe('demo (with TestBed):', () => {
|
||||
expect(comp.name).toBe(expectedOrigName,
|
||||
`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
|
||||
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();
|
||||
expect(comp.name).toBe(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
|
||||
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
|
||||
// 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();
|
||||
fixture.detectChanges();
|
||||
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 {
|
||||
ActivatedRoute, ActivatedRouteStub, asyncData, click, newEvent
|
||||
ActivatedRoute, ActivatedRouteStub, asyncData, click
|
||||
} from '../../testing';
|
||||
|
||||
import { Hero } from '../model/hero';
|
||||
@ -99,7 +99,10 @@ function overrideSetup() {
|
||||
const newName = 'New Name';
|
||||
|
||||
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(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
|
||||
nameInput.value = 'quick BROWN fOx';
|
||||
|
||||
// dispatch a DOM event so that Angular learns of input value change.
|
||||
// use newEvent utility function (not provided by Angular) for better browser compatibility
|
||||
nameInput.dispatchEvent(newEvent('input'));
|
||||
// Dispatch a DOM event so that Angular learns of 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
|
||||
nameInput.dispatchEvent(new Event('input'));
|
||||
|
||||
// Tell Angular to update the display binding through the title pipe
|
||||
fixture.detectChanges();
|
||||
|
@ -6,7 +6,7 @@ import { DebugElement } from '@angular/core';
|
||||
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { addMatchers, newEvent } from '../../testing';
|
||||
import { addMatchers } from '../../testing';
|
||||
import { HeroService } from '../model/hero.service';
|
||||
import { getTestHeroes, TestHeroService } from '../model/testing/test-hero.service';
|
||||
|
||||
@ -53,7 +53,10 @@ describe('HeroListComponent', () => {
|
||||
it('should select hero on click', fakeAsync(() => {
|
||||
const expectedHero = HEROES[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();
|
||||
// `.toEqual` because selectedHero is clone of expectedHero; see FakeHeroService
|
||||
expect(comp.selectedHero).toEqual(expectedHero);
|
||||
@ -62,7 +65,10 @@ describe('HeroListComponent', () => {
|
||||
it('should navigate to selected hero detail on click', fakeAsync(() => {
|
||||
const expectedHero = HEROES[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();
|
||||
|
||||
// should have navigated
|
||||
|
@ -3,7 +3,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { HighlightDirective } from './highlight.directive';
|
||||
import { newEvent } from '../../testing';
|
||||
|
||||
// #docregion test-component
|
||||
@Component({
|
||||
@ -59,9 +58,12 @@ describe('HighlightDirective', () => {
|
||||
const input = des[2].nativeElement as HTMLInputElement;
|
||||
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.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();
|
||||
|
||||
expect(input.style.backgroundColor).toBe('green', 'changed backgroundColor');
|
||||
|
@ -14,18 +14,6 @@ export function advance(f: ComponentFixture<any>): void {
|
||||
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
|
||||
// #docregion click-event
|
||||
/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */
|
||||
|
@ -1,5 +1,8 @@
|
||||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
],
|
||||
"rules": {
|
||||
"align": {
|
||||
"options": [
|
||||
@ -13,22 +16,6 @@
|
||||
"deprecation": {
|
||||
"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,
|
||||
"import-blacklist": [
|
||||
true,
|
||||
@ -56,6 +43,8 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
// TODO(gkalpak): Fix the code and enable this.
|
||||
// "no-any": true,
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
@ -95,6 +84,11 @@
|
||||
"named": "never"
|
||||
}
|
||||
},
|
||||
// TODO(gkalpak): Fix the code and enable this.
|
||||
// "typedef": [
|
||||
// true,
|
||||
// "call-signature"
|
||||
// ],
|
||||
"typedef-whitespace": {
|
||||
"options": [
|
||||
{
|
||||
@ -130,6 +124,9 @@
|
||||
"check-typecast"
|
||||
]
|
||||
},
|
||||
"component-class-suffix": true,
|
||||
"contextual-lifecycle": true,
|
||||
"directive-class-suffix": true,
|
||||
"no-conflicting-lifecycle": true,
|
||||
"no-host-metadata-property": true,
|
||||
"no-input-rename": true,
|
||||
@ -141,9 +138,19 @@
|
||||
"template-banana-in-box": true,
|
||||
"template-no-negated-async": true,
|
||||
"use-lifecycle-interface": true,
|
||||
"use-pipe-transform-interface": true
|
||||
},
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
]
|
||||
"use-pipe-transform-interface": true,
|
||||
"directive-selector": [
|
||||
true,
|
||||
"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 'angular-route';
|
||||
|
||||
const appName = 'myApp';
|
||||
|
||||
angular.module(appName, [
|
||||
const appModule = angular.module('myApp', [
|
||||
'ngRoute'
|
||||
])
|
||||
.config(['$routeProvider', '$locationProvider',
|
||||
@ -25,5 +23,5 @@ angular.module(appName, [
|
||||
);
|
||||
|
||||
export function bootstrap(el: HTMLElement) {
|
||||
return angular.bootstrap(el, [appName]);
|
||||
return angular.bootstrap(el, [appModule.name]);
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ In the following example, the `@Component()` metadata object and the class const
|
||||
@Component({
|
||||
selector: 'app-typical',
|
||||
template: '<div>A typical component for {{data.name}}</div>'
|
||||
)}
|
||||
})
|
||||
export class TypicalComponent {
|
||||
@Input() data: TypicalData;
|
||||
constructor(private someService: SomeService) { ... }
|
||||
|
@ -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
|
||||
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({
|
||||
|
@ -18,8 +18,6 @@ When you use the [Angular CLI](cli) command `ng new` to generate an app, the def
|
||||
/* JavaScript imports */
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
@ -29,9 +27,7 @@ import { AppComponent } from './app.component';
|
||||
AppComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule
|
||||
BrowserModule
|
||||
],
|
||||
providers: [],
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
{@a imports}
|
||||
|
||||
## 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.
|
||||
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
|
||||
that component templates in this module reference. In this case, the component is
|
||||
`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
|
||||
the class was imported from another module.
|
||||
|
||||
|
||||
|
||||
{@a bootstrap-array}
|
||||
|
||||
## The `providers` array
|
||||
|
@ -119,13 +119,13 @@ The `/deep/` combinator also has the aliases `>>>`, and `::ng-deep`.
|
||||
|
||||
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
|
||||
[Controlling view encapsulation](guide/component-styles#view-encapsulation) section.
|
||||
[View Encapsulation](guide/view-encapsulation) section.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
The shadow-piercing descendant combinator is deprecated and [support is being removed from major browsers](https://www.chromestatus.com/features/6750456638341120) and tools.
|
||||
The shadow-piercing descendant combinator is deprecated and [support is being removed from major browsers](https://www.chromestatus.com/feature/6750456638341120) and tools.
|
||||
As such we plan to drop support in Angular (for all 3 of `/deep/`, `>>>` and `::ng-deep`).
|
||||
Until then `::ng-deep` should be preferred for a broader compatibility with the tools.
|
||||
|
||||
@ -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.
|
||||
|
||||
</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.
|
||||
|
@ -26,7 +26,7 @@ The `ng generate` command creates the `projects/my-lib` folder in your workspace
|
||||
|
||||
</div>
|
||||
|
||||
When you generate a new library, the workspace configuration file, `angular.json`, is updated with a project of type 'library'.
|
||||
When you generate a new library, the workspace configuration file, `angular.json`, is updated with a project of type `library`.
|
||||
|
||||
<code-example format="json">
|
||||
"projects": {
|
||||
@ -109,7 +109,7 @@ If you want a dropdown that would contain different passed-in values each time,
|
||||
|
||||
Suppose you want to read a configuration file and then generate a form based on that configuration.
|
||||
If that form will need additional customization by the developer who is using your library, it might work best as a schematic.
|
||||
However, if the forms will always be the same and not need much customization by developers, then you could create a dynamic component that takes the configuration and generates the form.
|
||||
However, if the form will always be the same and not need much customization by developers, then you could create a dynamic component that takes the configuration and generates the form.
|
||||
In general, the more complex the customization, the more useful the schematic approach.
|
||||
|
||||
To learn more, see [Schematics Overview](guide/schematics) and [Schematics for Libraries](guide/schematics-for-libraries).
|
||||
@ -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.
|
||||
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.
|
||||
This ensures that when modules ask for Angular, they all get the exact same module.
|
||||
|
@ -387,7 +387,7 @@ List the generated bundles in the `dist/` folder.
|
||||
|
||||
<code-example language="none" class="code-shell">
|
||||
|
||||
ls dist/*.bundle.js
|
||||
ls dist/*.js
|
||||
|
||||
</code-example>
|
||||
|
||||
@ -396,7 +396,7 @@ The following example displays the graph for the _main_ bundle.
|
||||
|
||||
<code-example language="none" class="code-shell">
|
||||
|
||||
node_modules/.bin/source-map-explorer dist/main.*.bundle.js
|
||||
node_modules/.bin/source-map-explorer dist/main*
|
||||
|
||||
</code-example>
|
||||
|
||||
@ -511,9 +511,9 @@ Each script tag has a `type="module"` or `nomodule` attribute. Browsers with nat
|
||||
|
||||
To include differential loading in your application builds, you must configure the Browserslist and TypeScript configuration files in your application project.
|
||||
|
||||
The following examples show a `browserlistrc` and `tsconfig.json` file for a newly created Angular application. In this configuration, legacy browsers such as IE 9-11 are ignored, and the compilation target is ES2015.
|
||||
The following examples show a `.browserslistrc` and `tsconfig.json` file for a newly created Angular application. In this configuration, legacy browsers such as IE 9-11 are ignored, and the compilation target is ES2015.
|
||||
|
||||
<code-example language="none" header="browserslistrc">
|
||||
<code-example language="none" header=".browserslistrc">
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
@ -527,7 +527,7 @@ The following examples show a `browserlistrc` and `tsconfig.json` file for a new
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major version
|
||||
last 2 Safari major versions
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||
|
@ -603,18 +603,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).
|
||||
|
||||
{@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}
|
||||
### 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.
|
||||
|
@ -119,7 +119,14 @@ The recently-developed [custom elements](https://developer.mozilla.org/en-US/doc
|
||||
|
||||
In browsers that support Custom Elements natively, the specification requires developers use ES2015 classes to define Custom Elements - developers can opt-in to this by setting the `target: "es2015"` property in their project's [TypeScript configuration file](/guide/typescript-configuration). As Custom Element and ES2015 support may not be available in all browsers, developers can instead choose to use a polyfill to support older browsers and ES5 code.
|
||||
|
||||
Use the [Angular CLI](cli) to automatically set up your project with the correct polyfill: `ng add @angular/elements --project=*your_project_name*`.
|
||||
Use the [Angular CLI](cli) to automatically set up your project with the correct polyfill:
|
||||
|
||||
<code-example language="sh">
|
||||
|
||||
ng add @angular/elements --project=*your_project_name*
|
||||
|
||||
</code-example>
|
||||
|
||||
- For more information about polyfills, see [polyfill documentation](https://www.webcomponents.org/polyfills).
|
||||
|
||||
- For more information about Angular browser support, see [Browser Support](guide/browser-support).
|
||||
|
@ -76,6 +76,12 @@ All router components must be entry components. Because this would require you t
|
||||
|
||||
## The `entryComponents` array
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Since 9.0.0 with Ivy, the `entryComponents` property is no longer necessary. See [deprecations guide](guide/deprecations#entryComponents).
|
||||
|
||||
</div>
|
||||
|
||||
Though the `@NgModule` decorator has an `entryComponents` array, most of the time
|
||||
you won't have to explicitly set any entry components because Angular adds components listed in `@NgModule.bootstrap` and those in route definitions to entry components automatically. Though these two mechanisms account for most entry components, if your app happens to bootstrap or dynamically load a component by type imperatively,
|
||||
you must add it to `entryComponents` explicitly.
|
||||
|
@ -627,6 +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).
|
||||
|
||||
{@ ngc}
|
||||
## ngc
|
||||
`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.
|
||||
|
||||
{@a O}
|
||||
|
||||
{@a observable}
|
||||
|
@ -87,7 +87,7 @@ To make one, enter the following command in the terminal, where `customers` is t
|
||||
ng generate module customers --route customers --module app.module
|
||||
</code-example>
|
||||
|
||||
This creates a `customers` folder with the new lazy-loadable module `CustomersModule` defined in the `customers.module.ts` file. The command automatically declares the `CustomersComponent` inside the new feature module.
|
||||
This creates a `customers` folder having the new lazy-loadable feature module `CustomersModule` defined in the `customers.module.ts` file and the routing module `CustomersRoutingModule` defined in the `customers-routing.module.ts` file. The command automatically declares the `CustomersComponent` and imports `CustomersRoutingModule` inside the new feature module.
|
||||
|
||||
Because the new module is meant to be lazy-loaded, the command does NOT add a reference to the new feature module in the application's root module file, `app.module.ts`.
|
||||
Instead, it adds the declared route, `customers` to the `routes` array declared in the module provided as the `--module` option.
|
||||
|
@ -62,6 +62,8 @@ Angular executes hook methods in the following sequence. You can use them to per
|
||||
|
||||
Called before `ngOnInit()` and whenever one or more data-bound input properties change.
|
||||
|
||||
Note that if your component has no inputs or you use it without providing any inputs, the framework will not call `ngOnChanges()`.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style='vertical-align:top'>
|
||||
|
@ -141,7 +141,7 @@ Because the token is now an abstract class, and the injectable component impleme
|
||||
The implementation of the method (with all of its code overhead) resides in the injectable component that can be tree-shaken.
|
||||
This allows the parent to communicate with the child (if it is present) in a type-safe manner.
|
||||
|
||||
For example, the `LibCardComponent` now queries`LibHeaderToken` rather than `LibHeaderComponent`.
|
||||
For example, the `LibCardComponent` now queries `LibHeaderToken` rather than `LibHeaderComponent`.
|
||||
The following example shows how the pattern allows `LibCardComponent` to communicate with the `LibHeaderComponent` without actually referring to `LibHeaderComponent`.
|
||||
|
||||
```
|
||||
|
@ -397,7 +397,7 @@ The following code example binds an observable of message strings
|
||||
## 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.
|
||||
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.
|
||||
You can also create an impure pipe to make and cache an HTTP request.
|
||||
|
@ -223,6 +223,6 @@ content harmlessly. The following is the browser output
|
||||
of the `evilTitle` examples.
|
||||
|
||||
<code-example language="bash">
|
||||
"Template <script>alert("evil never sleeps")</script> Syntax" is the interpolated evil title.
|
||||
"Template alert("evil never sleeps")Syntax" is the property bound evil title.
|
||||
"Template <script>alert("evil never sleeps")</script> Syntax" is the interpolated evil title.
|
||||
"Template Syntax" is the property bound evil title.
|
||||
</code-example>
|
||||
|
@ -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>
|
||||
|
||||
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**
|
||||
|
||||
|
@ -94,7 +94,7 @@ All of our major releases are supported for 18 months.
|
||||
|
||||
* 6 months of *active support*, during which regularly-scheduled updates and patches are released.
|
||||
|
||||
* 12 months of *long-term support (LTS)*, during which only critical fixes and security patches are released.
|
||||
* 12 months of *long-term support (LTS)*, during which only [critical fixes and security patches](#lts-fixes) are released.
|
||||
|
||||
The following table provides the status for Angular versions under support.
|
||||
|
||||
@ -102,11 +102,18 @@ The following table provides the status for Angular versions under support.
|
||||
Version | Status | Released | Active Ends | LTS Ends
|
||||
------- | ------ | ------------ | ------------ | ------------
|
||||
^10.0.0 | Active | Jun 24, 2020 | Dec 24, 2020 | Dec 24, 2021
|
||||
^9.0.0 | Active | Feb 06, 2020 | Aug 06, 2020 | Aug 06, 2021
|
||||
^9.0.0 | LTS | Feb 06, 2020 | Aug 06, 2020 | Aug 06, 2021
|
||||
^8.0.0 | LTS | May 28, 2019 | Nov 28, 2019 | Nov 28, 2020
|
||||
|
||||
Angular versions ^4.0.0, ^5.0.0, ^6.0.0 and ^7.0.0 are no longer under support.
|
||||
|
||||
### LTS fixes
|
||||
|
||||
As a general rule, a fix is considered for an LTS version if it resolves one of:
|
||||
|
||||
* a newly identified security vulnerability,
|
||||
* a regression, since the start of LTS, caused by a 3rd party change, such as a new browser version.
|
||||
|
||||
{@a deprecation}
|
||||
## Deprecation practices
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
@ -53,7 +53,7 @@ RxJS provides many operators, but only a handful are used frequently. For a list
|
||||
|
||||
| Area | Operators |
|
||||
| :------------| :----------|
|
||||
| Creation | `from`,`fromEvent`, `of` |
|
||||
| Creation | `from`, `fromEvent`, `of` |
|
||||
| Combination | `combineLatest`, `concat`, `merge`, `startWith` , `withLatestFrom`, `zip` |
|
||||
| Filtering | `debounceTime`, `distinctUntilChanged`, `filter`, `take`, `takeUntil` |
|
||||
| Transformation | `bufferTime`, `concatMap`, `map`, `mergeMap`, `scan`, `switchMap` |
|
||||
|
@ -107,7 +107,7 @@ Notice that all of the files the browser needs to render this application are ca
|
||||
<div class="alert is-helpful">
|
||||
Pay attention to two key points:
|
||||
|
||||
1. The generated `ngsw-config.json` includes a limited list of cacheable fonts and images extentions. In some cases, you might want to modify the glob pattern to suit your needs.
|
||||
1. The generated `ngsw-config.json` includes a limited list of cacheable fonts and images extensions. In some cases, you might want to modify the glob pattern to suit your needs.
|
||||
|
||||
1. If `resourcesOutputPath` or `assets` paths are modified after the generation of configuration file, you need to change the paths manually in `ngsw-config.json`.
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Template statements
|
||||
|
||||
A template **statement** responds to an **event** raised by a binding target
|
||||
such as an element, component, or directive.
|
||||
Template statements are methods or properties that you can use in your HTML to respond to user events.
|
||||
With template statements, your application can engage users through actions such as displaying dynamic content or submitting forms.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -10,24 +10,30 @@ the syntax and code snippets in this guide.
|
||||
|
||||
</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>
|
||||
|
||||
A template statement *has a side effect*.
|
||||
That's the whole point of an event.
|
||||
It's how you update application state from user action.
|
||||
When the user clicks the **Delete hero** button, Angular calls the `deleteHero()` function in the component class.
|
||||
|
||||
Responding to events is the other side of Angular's "unidirectional data flow".
|
||||
You're free to change anything, anywhere, during this turn of the event loop.
|
||||
You can use template statements with elements, components, or directives in response to events.
|
||||
|
||||
Like template expressions, template *statements* use a language that looks like JavaScript.
|
||||
The template statement parser differs from the template expression parser and
|
||||
specifically supports both basic assignment (`=`) and chaining expressions with <code>;</code>.
|
||||
<div class="alert is-helpful">
|
||||
|
||||
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 `--`
|
||||
* operator assignment, 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
|
||||
|
||||
As with expressions, statements can refer only to what's in the statement context
|
||||
such as an event handling method of the component instance.
|
||||
Statements have a context—a particular part of the application to which the statement belongs.
|
||||
|
||||
The *statement context* is typically the component instance.
|
||||
The *deleteHero* in `(click)="deleteHero()"` is a method of the data-bound component.
|
||||
Statements can refer only to what's in the statement context, which is typically the component instance.
|
||||
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>
|
||||
|
||||
The statement context may also refer to properties of the template's own context.
|
||||
In the following examples, the template `$event` object,
|
||||
a [template input variable](guide/built-in-directives#template-input-variable) (`let hero`),
|
||||
and a [template reference variable](guide/template-reference-variables) (`#heroForm`)
|
||||
are passed to an event handling method of the component.
|
||||
In the following example, the component's event handling method, `onSave()` takes the template's own `$event` object as an argument.
|
||||
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`.
|
||||
|
||||
<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.
|
||||
In `deleteHero(hero)` above, the `hero` is the template input variable,
|
||||
not the component's `hero` property.
|
||||
In the preceding `deleteHero(hero)`, the `hero` is the template input variable, not the component's `hero` property.
|
||||
|
||||
## Statement guidelines
|
||||
## Statement best practices
|
||||
|
||||
Template statements cannot refer to anything in the global namespace. They
|
||||
can't refer to `window` or `document`.
|
||||
They can't call `console.log` or `Math.max`.
|
||||
* **Conciseness**
|
||||
|
||||
As with expressions, avoid writing complex template statements.
|
||||
A method call or simple property assignment should be the norm.
|
||||
Keep template statements minimal by using method calls or basic property assignments.
|
||||
|
||||
* **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()`.
|
||||
|
@ -217,7 +217,7 @@ script:
|
||||
- npm run e2e -- --protractor-config=e2e/protractor-ci.conf.js
|
||||
```
|
||||
|
||||
This does the same things as the Circle CI configuration, except that Travis doesn't come with Chrome, so we use Chromium instead.
|
||||
This does the same things as the CircleCI configuration, except that Travis doesn't come with Chrome, so use Chromium instead.
|
||||
|
||||
Step 2: Commit your changes and push them to your repository.
|
||||
|
||||
|
@ -479,6 +479,15 @@ using the `downgradeComponent()` method. The result is an AngularJS
|
||||
<code-example path="upgrade-module/src/app/downgrade-static/app.module.ts" region="downgradecomponent" header="app.module.ts">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
By default, Angular change detection will also run on the component for every
|
||||
AngularJS `$digest` cycle. If you wish to only have change detection run when
|
||||
the inputs change, you can set `propagateDigest` to `false` when calling
|
||||
`downgradeComponent()`.
|
||||
|
||||
</div>
|
||||
|
||||
Because `HeroDetailComponent` is an Angular component, you must also add it to the
|
||||
`declarations` in the `AppModule`.
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user