Compare commits
299 Commits
10.1.1
...
11.0.0-nex
Author | SHA1 | Date | |
---|---|---|---|
6ae1f198e6 | |||
1a0e7cca2d | |||
c8d757624b | |||
31e42f0947 | |||
ddc9e8e47a | |||
1e3f810f8f | |||
e8084ff906 | |||
827245fdb6 | |||
8b7acc4f8f | |||
393ce5574b | |||
2aeaedc519 | |||
8f66540152 | |||
8f11b516f8 | |||
c8958d76db | |||
f6052a915d | |||
37ed4bd9ff | |||
efe0e03fe1 | |||
ded9aeb447 | |||
b16a69c7a4 | |||
a6971ba89a | |||
93c3d8f9fd | |||
1b70dc931d | |||
3ba97ab391 | |||
e2997ed5ee | |||
bf010b9a07 | |||
387fd89b3f | |||
e991fee6bf | |||
06525cfed3 | |||
e9a8f9f705 | |||
3f9be429fc | |||
4fd635a03a | |||
53a7ed4317 | |||
2df39ee338 | |||
2b6b9c95f9 | |||
c5e178997c | |||
cb7164a236 | |||
4a9f4520ce | |||
914d7099ee | |||
e459dc7c42 | |||
fd7146cc71 | |||
5ef2c983b9 | |||
c74917a7d5 | |||
494a2f3be4 | |||
fc3b3fe39e | |||
5448e84cf0 | |||
7bd18fca19 | |||
5a86fb33ba | |||
5db84d7221 | |||
eb32b6bd6b | |||
b2579d43cd | |||
9fb541787c | |||
812615bb99 | |||
2ede800f0c | |||
f96dcc5ce0 | |||
372a6cf8d7 | |||
b9dce19b3d | |||
964ac1542a | |||
8b01d42e5d | |||
474ecf6d00 | |||
caa7f9808f | |||
69bb49e530 | |||
9dccaa9570 | |||
e1c11a36c7 | |||
c1b47bcfaa | |||
d7ff8d765c | |||
3a598cf5ed | |||
b041c118e3 | |||
6af638d58e | |||
9e0e763156 | |||
758d0e2045 | |||
4744c229db | |||
4dfe0fa068 | |||
3b919ef10f | |||
7b2aac97df | |||
daf8b7f100 | |||
5f815c0565 | |||
c7d5555dfb | |||
323be39297 | |||
0dd859757a | |||
239968d2f1 | |||
cf9176eb46 | |||
2f6777df18 | |||
4ca1c736bb | |||
f2c00aab8d | |||
9aff09b827 | |||
e790c8547e | |||
b627f7f02e | |||
7f6f8bbffb | |||
e4f4d18e7e | |||
837889f0a4 | |||
bd7d8744fa | |||
4c8766573d | |||
8422633b8a | |||
37297cfed7 | |||
19d543f71e | |||
f979914d4e | |||
40975e06c6 | |||
ffe89fb07d | |||
15ea811f05 | |||
4baabf9cd3 | |||
7244e1b4e3 | |||
b1682526dd | |||
75610505c6 | |||
ba3f4c26bb | |||
c8f056beb6 | |||
a2068523fd | |||
145ab3d7e0 | |||
3082f7378b | |||
297b123151 | |||
a93605f2a4 | |||
15dfd3439a | |||
123bff7cb6 | |||
6158dc16b4 | |||
b0a43872a8 | |||
856e74ac98 | |||
6ae3b68acf | |||
49f27e31ed | |||
1a62f74496 | |||
2b1b7180db | |||
fdd4fa0009 | |||
984ed39195 | |||
0c0c54d615 | |||
dfbfabc052 | |||
f3f6a42342 | |||
e498ea9b5a | |||
a91f0f6b82 | |||
d5ddb9f340 | |||
b68fab547a | |||
16a560119a | |||
88d7bb8386 | |||
2d6105a784 | |||
95b8a8706a | |||
d92a0dd72f | |||
55485713a3 | |||
d3169c533e | |||
e4424863c2 | |||
d795a00137 | |||
7fb388f929 | |||
02c6bff9cf | |||
ef28e15884 | |||
a33d630a21 | |||
c4b8964424 | |||
dd8d8c8289 | |||
129107191c | |||
722699fb0c | |||
5614258cc7 | |||
e62a918542 | |||
49ee90b1b5 | |||
7849fdde09 | |||
97adc27207 | |||
41bc2701c4 | |||
a765530277 | |||
5b33798796 | |||
44074499dc | |||
ba54671993 | |||
a85109fd72 | |||
85a2626620 | |||
d192c87f6a | |||
3817e5f1df | |||
171a0d0696 | |||
a1c1c450dc | |||
fd44d84a33 | |||
f0688b4d18 | |||
b9ca6d20cc | |||
e00535a2a4 | |||
593bd594e3 | |||
284c70ee9d | |||
78e1ecb161 | |||
3934f0a833 | |||
297c060ae7 | |||
077f51685a | |||
e162da0753 | |||
354138eba9 | |||
6768fe9927 | |||
57c442f930 | |||
f667e374a9 | |||
15207e3c9c | |||
66129f8ea6 | |||
da14b72550 | |||
27cc56b359 | |||
6acea54f62 | |||
2d52c80332 | |||
03447ba52f | |||
8f349b2375 | |||
19598b47ca | |||
f56ece4fdc | |||
cf2e8b99a8 | |||
c4556db9f5 | |||
a46e0e48a3 | |||
9e77bd3087 | |||
26f28200bf | |||
ce1efc1af2 | |||
d1415162cb | |||
db21c4fb44 | |||
281865bbcf | |||
3406ec15a4 | |||
85951a0465 | |||
0ae00bb1f7 | |||
1373a98e25 | |||
1ed6913b8b | |||
3c4b8b97c1 | |||
f9421184ef | |||
c6ebb77cec | |||
a69507a0ad | |||
886f58d4fe | |||
18f84a0328 | |||
b0ca3cd0c4 | |||
3d77b64fc3 | |||
f645d26e3f | |||
6d9bfb8368 | |||
7997fc5f97 | |||
3cb2a79399 | |||
d896c33b0e | |||
19a484302d | |||
ded075a79c | |||
ba95b79a21 | |||
4faac78e32 | |||
4360eed9b7 | |||
c880e393e9 | |||
b0c79f2373 | |||
a32a317ea1 | |||
bfb7eec698 | |||
7e0b3fd953 | |||
7a6a061a9e | |||
109555b33a | |||
bf31ef29f6 | |||
40096bee00 | |||
45a73dddfd | |||
687477279b | |||
4007422cc6 | |||
1150649139 | |||
7869de6136 | |||
2c4a98a285 | |||
92ff6d93eb | |||
83ace4ed30 | |||
926ffcd8ac | |||
97475d7408 | |||
a29f9a9fb3 | |||
9f28f82598 | |||
261f689e9b | |||
1d9873c44c | |||
d9da7e5a18 | |||
79d8795633 | |||
3e57ca1d98 | |||
c2d017de83 | |||
7baa7ebfc4 | |||
4e5286180b | |||
73001b42fe | |||
c90eb5450d | |||
3e97435f1c | |||
1c7e5cef3e | |||
2cb3d58b42 | |||
44bb85ade4 | |||
50f4d8a1ce | |||
fdea1804d6 | |||
1d8c5d88cd | |||
a68f1a78a7 | |||
86e11f1110 | |||
5da1934115 | |||
86e7cd8117 | |||
e6ee7c2aeb | |||
54687f7765 | |||
59c234cfb4 | |||
a6f3cd93a9 | |||
d9fea857db | |||
03dbcc7a56 | |||
c142b071eb | |||
71acf9dd49 | |||
f5a148b1b7 | |||
4f28192d62 | |||
0fc2bef0cd | |||
f5d1e9a2d1 | |||
036a2faf02 | |||
5be4edfa17 | |||
38d6596742 | |||
0a7a5e3aff | |||
d5fabc303d | |||
ebc0e46501 | |||
3487b549fd | |||
52c7a4bfc6 | |||
827ba05914 | |||
b2857b4e3a | |||
5d5caf21b8 | |||
c1bc070b40 | |||
930eeaf177 | |||
2dd29fbae7 | |||
9613660fee | |||
c0523fc3b4 | |||
de1cffb23b | |||
31f4557621 | |||
7723bfd9ba | |||
e8ea839df8 | |||
90cec40cce | |||
4036281007 | |||
164cd274a4 | |||
fedcfec346 | |||
618cb32407 | |||
4aee0087ea | |||
0681a20d28 |
@ -653,8 +653,10 @@ jobs:
|
|||||||
name: Starting Saucelabs tunnel service
|
name: Starting Saucelabs tunnel service
|
||||||
command: ./tools/saucelabs/sauce-service.sh run
|
command: ./tools/saucelabs/sauce-service.sh run
|
||||||
background: true
|
background: true
|
||||||
- run: yarn tsc -p packages
|
# add module umd tsc compile option so the test can work
|
||||||
- run: yarn tsc -p modules
|
# properly in the legacy browsers
|
||||||
|
- run: yarn tsc -p packages --module UMD
|
||||||
|
- run: yarn tsc -p modules --module UMD
|
||||||
- run: yarn bazel build //packages/zone.js:npm_package
|
- run: yarn bazel build //packages/zone.js:npm_package
|
||||||
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
|
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
|
||||||
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
|
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
|
||||||
@ -747,6 +749,8 @@ jobs:
|
|||||||
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/ &&
|
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/ &&
|
||||||
yarn --cwd packages/zone.js electrontest
|
yarn --cwd packages/zone.js electrontest
|
||||||
- run: yarn --cwd packages/zone.js jesttest
|
- run: yarn --cwd packages/zone.js jesttest
|
||||||
|
- run: yarn --cwd packages/zone.js/test/typings install --frozen-lockfile --non-interactive
|
||||||
|
- run: yarn --cwd packages/zone.js/test/typings test
|
||||||
|
|
||||||
# Windows jobs
|
# Windows jobs
|
||||||
# Docs: https://circleci.com/docs/2.0/hello-world-windows/
|
# Docs: https://circleci.com/docs/2.0/hello-world-windows/
|
||||||
|
2
.github/angular-robot.yml
vendored
2
.github/angular-robot.yml
vendored
@ -48,6 +48,7 @@ merge:
|
|||||||
- "packages/bazel/src/protractor/**"
|
- "packages/bazel/src/protractor/**"
|
||||||
- "packages/bazel/src/schematics/**"
|
- "packages/bazel/src/schematics/**"
|
||||||
- "packages/compiler-cli/ngcc/**"
|
- "packages/compiler-cli/ngcc/**"
|
||||||
|
- "packages/compiler-cli/src/ngtsc/sourcemaps/**",
|
||||||
- "packages/docs/**"
|
- "packages/docs/**"
|
||||||
- "packages/elements/schematics/**"
|
- "packages/elements/schematics/**"
|
||||||
- "packages/examples/**"
|
- "packages/examples/**"
|
||||||
@ -68,6 +69,7 @@ merge:
|
|||||||
- "packages/**/integrationtest/**"
|
- "packages/**/integrationtest/**"
|
||||||
- "packages/**/test/**"
|
- "packages/**/test/**"
|
||||||
- "packages/zone.js/*"
|
- "packages/zone.js/*"
|
||||||
|
- "packages/zone.js/dist/**"
|
||||||
- "packages/zone.js/doc/**"
|
- "packages/zone.js/doc/**"
|
||||||
- "packages/zone.js/example/**"
|
- "packages/zone.js/example/**"
|
||||||
- "packages/zone.js/scripts/**"
|
- "packages/zone.js/scripts/**"
|
||||||
|
2
.github/workflows/lock-closed.yml
vendored
2
.github/workflows/lock-closed.yml
vendored
@ -10,6 +10,6 @@ jobs:
|
|||||||
if: github.repository == 'angular/angular'
|
if: github.repository == 'angular/angular'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: angular/dev-infra/github-actions/lock-closed@66462f6
|
- uses: angular/dev-infra/github-actions/lock-closed@414834b2b24dd2df37c6ed00808387ee6fd91b66
|
||||||
with:
|
with:
|
||||||
lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }}
|
lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }}
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -40,6 +40,9 @@ yarn-error.log
|
|||||||
# User specific bazel settings
|
# User specific bazel settings
|
||||||
.bazelrc.user
|
.bazelrc.user
|
||||||
|
|
||||||
|
# User specific ng-dev settings
|
||||||
|
.ng-dev.user*
|
||||||
|
|
||||||
.notes.md
|
.notes.md
|
||||||
baseline.json
|
baseline.json
|
||||||
|
|
||||||
|
@ -105,9 +105,9 @@ Fixes #<issue number>
|
|||||||
# │ │
|
# │ │
|
||||||
# │ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
# │ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
||||||
# │ elements|forms|http|language-service|localize|platform-browser|
|
# │ elements|forms|http|language-service|localize|platform-browser|
|
||||||
# │ platform-browser-dynamic|platform-server|platform-webworker|
|
# │ platform-browser-dynamic|platform-server|router|service-worker|
|
||||||
# │ platform-webworker-dynamic|router|service-worker|upgrade|zone.js|
|
# │ upgrade|zone.js|packaging|changelog|dev-infra|docs-infra|migrations|
|
||||||
# │ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve
|
# │ ngcc|ve
|
||||||
# │ https://github.com/angular/angular/blob/master/CONTRIBUTING.md#scope
|
# │ https://github.com/angular/angular/blob/master/CONTRIBUTING.md#scope
|
||||||
# │
|
# │
|
||||||
# └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
|
# └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
|
||||||
|
@ -3,6 +3,7 @@ import {commitMessage} from './commit-message';
|
|||||||
import {format} from './format';
|
import {format} from './format';
|
||||||
import {github} from './github';
|
import {github} from './github';
|
||||||
import {merge} from './merge';
|
import {merge} from './merge';
|
||||||
|
import {release} from './release';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
commitMessage,
|
commitMessage,
|
||||||
@ -10,4 +11,5 @@ module.exports = {
|
|||||||
github,
|
github,
|
||||||
merge,
|
merge,
|
||||||
caretaker,
|
caretaker,
|
||||||
|
release,
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {DevInfraMergeConfig} from '../dev-infra/pr/merge/config';
|
import {DevInfraMergeConfig} from '../dev-infra/pr/merge/config';
|
||||||
import {getDefaultTargetLabelConfiguration} from '../dev-infra/pr/merge/defaults';
|
import {getDefaultTargetLabelConfiguration} from '../dev-infra/pr/merge/defaults';
|
||||||
import {github} from './github';
|
import {github} from './github';
|
||||||
|
import {release} from './release';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
|
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
|
||||||
@ -13,7 +14,9 @@ export const merge: DevInfraMergeConfig['merge'] = async api => {
|
|||||||
mergeReadyLabel: /^action: merge(-assistance)?/,
|
mergeReadyLabel: /^action: merge(-assistance)?/,
|
||||||
caretakerNoteLabel: 'action: merge-assistance',
|
caretakerNoteLabel: 'action: merge-assistance',
|
||||||
commitMessageFixupLabel: 'commit message fixup',
|
commitMessageFixupLabel: 'commit message fixup',
|
||||||
labels: await getDefaultTargetLabelConfiguration(api, github, '@angular/core'),
|
// We can pick any of the NPM packages as we are in a monorepo where all packages are
|
||||||
|
// published together with the same version and branching.
|
||||||
|
labels: await getDefaultTargetLabelConfiguration(api, github, release),
|
||||||
requiredBaseCommits: {
|
requiredBaseCommits: {
|
||||||
// PRs that target either `master` or the patch branch, need to be rebased
|
// PRs that target either `master` or the patch branch, need to be rebased
|
||||||
// on top of the latest commit message validation fix.
|
// on top of the latest commit message validation fix.
|
||||||
|
33
.ng-dev/release.ts
Normal file
33
.ng-dev/release.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import {join} from 'path';
|
||||||
|
import {exec} from 'shelljs';
|
||||||
|
import {ReleaseConfig} from '../dev-infra/release/config';
|
||||||
|
|
||||||
|
/** Configuration for the `ng-dev release` command. */
|
||||||
|
export const release: ReleaseConfig = {
|
||||||
|
npmPackages: [
|
||||||
|
'@angular/animations',
|
||||||
|
'@angular/bazel',
|
||||||
|
'@angular/common',
|
||||||
|
'@angular/compiler',
|
||||||
|
'@angular/compiler-cli',
|
||||||
|
'@angular/core',
|
||||||
|
'@angular/elements',
|
||||||
|
'@angular/forms',
|
||||||
|
'@angular/language-service',
|
||||||
|
'@angular/localize',
|
||||||
|
'@angular/platform-browser',
|
||||||
|
'@angular/platform-browser-dynamic',
|
||||||
|
'@angular/platform-server',
|
||||||
|
'@angular/platform-webworker',
|
||||||
|
'@angular/platform-webworker-dynamic',
|
||||||
|
'@angular/router',
|
||||||
|
'@angular/service-worker',
|
||||||
|
'@angular/upgrade',
|
||||||
|
],
|
||||||
|
// TODO: Implement release package building here.
|
||||||
|
buildPackages: async () => [],
|
||||||
|
// TODO: This can be removed once there is a org-wide tool for changelog generation.
|
||||||
|
generateReleaseNotesForHead: async () => {
|
||||||
|
exec('yarn -s gulp changelog', {cwd: join(__dirname, '../')});
|
||||||
|
},
|
||||||
|
};
|
@ -284,7 +284,7 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- alxhub
|
- alxhub
|
||||||
- crisbeto
|
- crisbeto
|
||||||
- devversion
|
# OOO as of 2020-09-28 - devversion
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
@ -303,8 +303,6 @@ groups:
|
|||||||
'packages/platform-browser/**',
|
'packages/platform-browser/**',
|
||||||
'packages/examples/platform-browser/**',
|
'packages/examples/platform-browser/**',
|
||||||
'packages/platform-browser-dynamic/**',
|
'packages/platform-browser-dynamic/**',
|
||||||
'packages/platform-webworker/**',
|
|
||||||
'packages/platform-webworker-dynamic/**',
|
|
||||||
'packages/examples/common/**',
|
'packages/examples/common/**',
|
||||||
'packages/docs/**',
|
'packages/docs/**',
|
||||||
'aio/content/guide/accessibility.md',
|
'aio/content/guide/accessibility.md',
|
||||||
@ -419,7 +417,7 @@ groups:
|
|||||||
- atscott
|
- atscott
|
||||||
- ~kara # do not request reviews from Kara, but allow her to approve PRs
|
- ~kara # do not request reviews from Kara, but allow her to approve PRs
|
||||||
- mhevery
|
- mhevery
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
@ -662,7 +660,7 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- AndrewKushnir
|
- AndrewKushnir
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
@ -679,7 +677,7 @@ groups:
|
|||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
@ -697,7 +695,7 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
@ -723,7 +721,7 @@ groups:
|
|||||||
- IgorMinar
|
- IgorMinar
|
||||||
- mhevery
|
- mhevery
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
reviews:
|
reviews:
|
||||||
request: -1 # request reviews from everyone
|
request: -1 # request reviews from everyone
|
||||||
required: 2 # require at least 2 approvals
|
required: 2 # require at least 2 approvals
|
||||||
@ -1150,7 +1148,7 @@ groups:
|
|||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- devversion
|
# OOO as of 2020-09-28 - devversion
|
||||||
- filipesilva
|
- filipesilva
|
||||||
- gkalpak
|
- gkalpak
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
@ -1184,7 +1182,7 @@ groups:
|
|||||||
- atscott
|
- atscott
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- petebacondarwin
|
- petebacondarwin
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
reviews:
|
reviews:
|
||||||
request: 4 # Request reviews from four people
|
request: 4 # Request reviews from four people
|
||||||
required: 3 # Require that three people approve
|
required: 3 # Require that three people approve
|
||||||
@ -1212,7 +1210,7 @@ groups:
|
|||||||
- atscott
|
- atscott
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- petebacondarwin
|
- petebacondarwin
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
reviews:
|
reviews:
|
||||||
request: 4 # Request reviews from four people
|
request: 4 # Request reviews from four people
|
||||||
required: 2 # Require that two people approve
|
required: 2 # Require that two people approve
|
||||||
@ -1240,7 +1238,7 @@ groups:
|
|||||||
- atscott
|
- atscott
|
||||||
- jelbourn
|
- jelbourn
|
||||||
- petebacondarwin
|
- petebacondarwin
|
||||||
- pkozlowski-opensource
|
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
####################################################################################
|
####################################################################################
|
||||||
|
@ -44654,7 +44654,7 @@ const FOLDERS_IGNORE = [
|
|||||||
const DEFAULT_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([...FOLDERS_IGNORE,
|
const DEFAULT_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([...FOLDERS_IGNORE,
|
||||||
|
|
||||||
// ignore cruft
|
// ignore cruft
|
||||||
'yarn.lock', '.lock-wscript', '.wafpickle-{0..9}', '*.swp', '._*', 'npm-debug.log', 'yarn-error.log', '.npmrc', '.yarnrc', '.npmignore', '.gitignore', '.DS_Store']);
|
'yarn.lock', '.lock-wscript', '.wafpickle-{0..9}', '*.swp', '._*', 'npm-debug.log', 'yarn-error.log', '.npmrc', '.yarnrc', '.yarnrc.yml', '.npmignore', '.gitignore', '.DS_Store']);
|
||||||
|
|
||||||
const NEVER_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([
|
const NEVER_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([
|
||||||
// never ignore these files
|
// never ignore these files
|
||||||
@ -44663,6 +44663,7 @@ const NEVER_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([
|
|||||||
function packWithIgnoreAndHeaders(cwd, ignoreFunction, { mapHeader } = {}) {
|
function packWithIgnoreAndHeaders(cwd, ignoreFunction, { mapHeader } = {}) {
|
||||||
return tar.pack(cwd, {
|
return tar.pack(cwd, {
|
||||||
ignore: ignoreFunction,
|
ignore: ignoreFunction,
|
||||||
|
sort: true,
|
||||||
map: header => {
|
map: header => {
|
||||||
const suffix = header.name === '.' ? '' : `/${header.name}`;
|
const suffix = header.name === '.' ? '' : `/${header.name}`;
|
||||||
header.name = `package${suffix}`;
|
header.name = `package${suffix}`;
|
||||||
@ -46678,7 +46679,7 @@ function mkdirfix (name, opts, cb) {
|
|||||||
/* 194 */
|
/* 194 */
|
||||||
/***/ (function(module, exports) {
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.22.4","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}}
|
module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.22.5","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}}
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
/* 195 */
|
/* 195 */
|
||||||
@ -98338,7 +98339,7 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def
|
|||||||
|
|
||||||
const bundle = yield fetchBundle(config, bundleUrl);
|
const bundle = yield fetchBundle(config, bundleUrl);
|
||||||
|
|
||||||
const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.js`);
|
const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.cjs`);
|
||||||
reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`);
|
reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`);
|
||||||
yield (_fs || _load_fs()).mkdirp(path.dirname(yarnPath));
|
yield (_fs || _load_fs()).mkdirp(path.dirname(yarnPath));
|
||||||
yield (_fs || _load_fs()).writeFile(yarnPath, bundle);
|
yield (_fs || _load_fs()).writeFile(yarnPath, bundle);
|
||||||
@ -100190,7 +100191,7 @@ let main = exports.main = (() => {
|
|||||||
|
|
||||||
const config = new (_config || _load_config()).default(reporter);
|
const config = new (_config || _load_config()).default(reporter);
|
||||||
const outputWrapperEnabled = (0, (_conversion || _load_conversion()).boolifyWithDefault)(process.env.YARN_WRAP_OUTPUT, true);
|
const outputWrapperEnabled = (0, (_conversion || _load_conversion()).boolifyWithDefault)(process.env.YARN_WRAP_OUTPUT, true);
|
||||||
const shouldWrapOutput = outputWrapperEnabled && !(_commander || _load_commander()).default.json && command.hasWrapper((_commander || _load_commander()).default, (_commander || _load_commander()).default.args);
|
const shouldWrapOutput = outputWrapperEnabled && !(_commander || _load_commander()).default.json && command.hasWrapper((_commander || _load_commander()).default, (_commander || _load_commander()).default.args) && !(commandName === 'init' && (_commander || _load_commander()).default[`2`]);
|
||||||
|
|
||||||
if (shouldWrapOutput) {
|
if (shouldWrapOutput) {
|
||||||
reporter.header(commandName, { name: 'yarn', version: (_yarnVersion || _load_yarnVersion()).version });
|
reporter.header(commandName, { name: 'yarn', version: (_yarnVersion || _load_yarnVersion()).version });
|
||||||
@ -100604,7 +100605,7 @@ let start = (() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (yarnPath.endsWith(`.js`)) {
|
if (/\.[cm]?js$/.test(yarnPath)) {
|
||||||
exitCode = yield (0, (_child || _load_child()).spawnp)(process.execPath, [yarnPath, ...argv], opts);
|
exitCode = yield (0, (_child || _load_child()).spawnp)(process.execPath, [yarnPath, ...argv], opts);
|
||||||
} else {
|
} else {
|
||||||
exitCode = yield (0, (_child || _load_child()).spawnp)(yarnPath, argv, opts);
|
exitCode = yield (0, (_child || _load_child()).spawnp)(yarnPath, argv, opts);
|
2
.yarnrc
2
.yarnrc
@ -2,4 +2,4 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
yarn-path ".yarn/releases/yarn-1.22.4.js"
|
yarn-path ".yarn/releases/yarn-1.22.5.js"
|
||||||
|
@ -34,7 +34,7 @@ filegroup(
|
|||||||
filegroup(
|
filegroup(
|
||||||
name = "angularjs_scripts",
|
name = "angularjs_scripts",
|
||||||
srcs = [
|
srcs = [
|
||||||
# We also declare the unminfied AngularJS files since these can be used for
|
# We also declare the unminified AngularJS files since these can be used for
|
||||||
# local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts)
|
# local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts)
|
||||||
"@npm//:node_modules/angular/angular.js",
|
"@npm//:node_modules/angular/angular.js",
|
||||||
"@npm//:node_modules/angular/angular.min.js",
|
"@npm//:node_modules/angular/angular.min.js",
|
||||||
|
260
CHANGELOG.md
260
CHANGELOG.md
@ -1,3 +1,238 @@
|
|||||||
|
<a name="11.0.0-next.4"></a>
|
||||||
|
# 11.0.0-next.4 (2020-09-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** correct and simplify typing of `KeyValuePipe` ([#37447](https://github.com/angular/angular/issues/37447)) ([4dfe0fa](https://github.com/angular/angular/commit/4dfe0fa))
|
||||||
|
* **common:** correct and simplify typing of AsyncPipe ([#37447](https://github.com/angular/angular/issues/37447)) ([5f815c0](https://github.com/angular/angular/commit/5f815c0))
|
||||||
|
* **common:** correct and simplify typing of I18nPluralPipe ([#37447](https://github.com/angular/angular/issues/37447)) ([3b919ef](https://github.com/angular/angular/commit/3b919ef))
|
||||||
|
* **common:** correct typing and implementation of `SlicePipe` ([#37447](https://github.com/angular/angular/issues/37447)) ([4744c22](https://github.com/angular/angular/commit/4744c22))
|
||||||
|
* **common:** let case conversion pipes accept type unions with `null` ([#36259](https://github.com/angular/angular/issues/36259)) ([#37447](https://github.com/angular/angular/issues/37447)) ([c7d5555](https://github.com/angular/angular/commit/c7d5555))
|
||||||
|
* **compiler-cli:** perform DOM schema checks even in basic mode in g3 ([#38943](https://github.com/angular/angular/issues/38943)) ([40975e0](https://github.com/angular/angular/commit/40975e0))
|
||||||
|
* **language-service:** hybrid visitor returns parent node of BoundAttribute ([#38995](https://github.com/angular/angular/issues/38995)) ([323be39](https://github.com/angular/angular/commit/323be39))
|
||||||
|
* **packaging:** remove polyfills needed to run tests on IE9 and IE 10 ([#38931](https://github.com/angular/angular/issues/38931)) ([4ca1c73](https://github.com/angular/angular/commit/4ca1c73))
|
||||||
|
* **platform-webworker:** remove platform-webworker and platform-webworker-dynamic ([#38846](https://github.com/angular/angular/issues/38846)) ([93c3d8f](https://github.com/angular/angular/commit/93c3d8f))
|
||||||
|
* **router:** make relativeLinkResolution corrected by default ([#25609](https://github.com/angular/angular/issues/25609)) ([837889f](https://github.com/angular/angular/commit/837889f)), closes [#22394](https://github.com/angular/angular/issues/22394)
|
||||||
|
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* **router:** Adjust type of parameter in navigateByUrl and createUrlTree to be more accurate ([#38227](https://github.com/angular/angular/issues/38227)) ([e4f4d18](https://github.com/angular/angular/commit/e4f4d18)), closes [#18798](https://github.com/angular/angular/issues/18798)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **common:** stricter types for DatePipe ([#37447](https://github.com/angular/angular/issues/37447)) ([daf8b7f](https://github.com/angular/angular/commit/daf8b7f))
|
||||||
|
* **common:** stricter types for number pipes ([#37447](https://github.com/angular/angular/issues/37447)) ([7b2aac9](https://github.com/angular/angular/commit/7b2aac9))
|
||||||
|
* **compiler:** Add keySpan to Variable Node ([#38965](https://github.com/angular/angular/issues/38965)) ([239968d](https://github.com/angular/angular/commit/239968d))
|
||||||
|
* **router:** Add `relativeLinkResolution` migration to update default value ([#38698](https://github.com/angular/angular/issues/38698)) ([15ea811](https://github.com/angular/angular/commit/15ea811))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **packaging:** In v10, IE 9, 10, and IE mobile support was deprecated. In v11, Angular framework removes IE 9,
|
||||||
|
10, and IE mobile support completely.
|
||||||
|
Supporting outdated browsers like these increases bundle size, code complexity, and test load,
|
||||||
|
and also requires time and effort that could be spent on improvements to the framework.
|
||||||
|
For example, fixing issues can be more difficult, as a straightforward fix for modern browsers
|
||||||
|
could break old ones that have quirks due to not receiving updates from vendors.
|
||||||
|
* **platform-webworker:** @angular/platform-webworker and @angular/platform-webworker-dynamic
|
||||||
|
have been removed as they were deprecated in v8
|
||||||
|
* **common:** The `slice` pipe now returns `null` for the `undefined` input value,
|
||||||
|
which is consistent with the behavior of most pipes. If you rely on
|
||||||
|
`undefined` being the result in that case, you now need to check for it
|
||||||
|
explicitly.
|
||||||
|
* **common:** The typing of the `keyvalue` pipe has been fixed to report that for
|
||||||
|
input objects that have `number` keys, the result will contain the
|
||||||
|
string representation of the keys. This was already the case and the
|
||||||
|
code has simply been updated to reflect this. Please update the
|
||||||
|
consumers of the pipe output if they were relying on the incorrect
|
||||||
|
types. Note that this does not affect use cases where the input values
|
||||||
|
are `Map`s, so if you need to preserve `number`s, this is an effective
|
||||||
|
way.
|
||||||
|
* **common:** The signatures of the number pipes now explicitly state which types are
|
||||||
|
accepted. This should only cause issues in corner cases, as any other
|
||||||
|
values would result in runtime exceptions.
|
||||||
|
* **common:** The signature of the `date` pipe now explicitly states which types are
|
||||||
|
accepted. This should only cause issues in corner cases, as any other
|
||||||
|
values would result in runtime exceptions.
|
||||||
|
* **common:** The async pipe no longer claims to return `undefined` for an input that
|
||||||
|
was typed as `undefined`. Note that the code actually returned `null` on
|
||||||
|
`undefined` inputs. In the unlikely case you were relying on this,
|
||||||
|
please fix the typing of the consumers of the pipe output.
|
||||||
|
* **common:** The case conversion pipes no longer let falsy values through. They now
|
||||||
|
map both `null` and `undefined` to `null` and raise an exception on
|
||||||
|
invalid input (`0`, `false`, `NaN`) just like most "common pipes". If
|
||||||
|
your code required falsy values to pass through, you need to handle them
|
||||||
|
explicitly.
|
||||||
|
* **router:** While the new parameter types allow a variable of type
|
||||||
|
`NavigationExtras` to be passed in, they will not allow object literals,
|
||||||
|
as they may only specify known properties. They will also not accept
|
||||||
|
types that do not have properties in common with the ones in the `Pick`.
|
||||||
|
To fix this error, only specify properties from the `NavigationExtras` which are
|
||||||
|
actually used in the respective function calls or use a type assertion
|
||||||
|
on the object or variable: `as NavigationExtras`.
|
||||||
|
* **router:** This commit changes the default value of
|
||||||
|
`relativeLinkResolution` from `'legacy'` to `'default'`. If your
|
||||||
|
application previously used the default by not specifying a value in the
|
||||||
|
`ExtraOptions` and uses relative links when navigating from children of
|
||||||
|
empty path routes, you will need to update your `RouterModule` to
|
||||||
|
specifically specify `'legacy'` for `relativeLinkResolution`.
|
||||||
|
See https://angular.io/api/router/ExtraOptions#relativeLinkResolution
|
||||||
|
for more details.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.1.4"></a>
|
||||||
|
## 10.1.4 (2020-09-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler-cli:** enable [@types](https://github.com/types) discovery in incremental rebuilds ([#39011](https://github.com/angular/angular/issues/39011)) ([6e99427](https://github.com/angular/angular/commit/6e99427)), closes [#38979](https://github.com/angular/angular/issues/38979)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="11.0.0-next.3"></a>
|
||||||
|
# 11.0.0-next.3 (2020-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** add `params` and `reportProgress` options to `HttpClient.put()` overload ([#37873](https://github.com/angular/angular/issues/37873)) ([dd8d8c8](https://github.com/angular/angular/commit/dd8d8c8)), closes [#23600](https://github.com/angular/angular/issues/23600)
|
||||||
|
* **compiler-cli:** generate `let` statements in ES2015+ mode ([#38775](https://github.com/angular/angular/issues/38775)) ([123bff7](https://github.com/angular/angular/commit/123bff7))
|
||||||
|
* **core:** ensure TestBed is not instantiated before override provider ([#38717](https://github.com/angular/angular/issues/38717)) ([c8f056b](https://github.com/angular/angular/commit/c8f056b))
|
||||||
|
* **forms:** type NG_VALUE_ACCESSOR injection token as array ([#29723](https://github.com/angular/angular/issues/29723)) ([2b1b718](https://github.com/angular/angular/commit/2b1b718)), closes [#29351](https://github.com/angular/angular/issues/29351)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **common:** Add ISO week-numbering year formats support to formatDate ([#38828](https://github.com/angular/angular/issues/38828)) ([984ed39](https://github.com/angular/angular/commit/984ed39))
|
||||||
|
* **compiler:** Parse and recover on incomplete opening HTML tags ([#38681](https://github.com/angular/angular/issues/38681)) ([6ae3b68](https://github.com/angular/angular/commit/6ae3b68)), closes [#38596](https://github.com/angular/angular/issues/38596)
|
||||||
|
* **router:** add migration to update calls to navigateByUrl and createUrlTree with invalid parameters ([#38825](https://github.com/angular/angular/issues/38825)) ([7849fdd](https://github.com/angular/angular/commit/7849fdd)), closes [#38227](https://github.com/angular/angular/issues/38227)
|
||||||
|
* **service-worker:** add the option to prefer network for navigation requests ([#38565](https://github.com/angular/angular/issues/38565)) ([a206852](https://github.com/angular/angular/commit/a206852)), closes [#38194](https://github.com/angular/angular/issues/38194)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **core:** If you call `TestBed.overrideProvider` after TestBed initialization, provider overrides are not applied. This
|
||||||
|
behavior is consistent with other override methods (such as `TestBed.overrideDirective`, etc) but they
|
||||||
|
throw an error to indicate that, when the check was missing in the `TestBed.overrideProvider` function.
|
||||||
|
Now calling `TestBed.overrideProvider` after TestBed initialization also triggers an
|
||||||
|
error, thus there is a chance that some tests (where `TestBed.overrideProvider` is
|
||||||
|
called after TestBed initialization) will start to fail and require updates to move `TestBed.overrideProvider` calls
|
||||||
|
before TestBed initialization is completed.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.1.3"></a>
|
||||||
|
## 10.1.3 (2020-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **http:** Fix error message when we call jsonp without importing HttpClientJsonpModule ([#38756](https://github.com/angular/angular/issues/38756)) ([3902ec0](https://github.com/angular/angular/commit/3902ec0))
|
||||||
|
* **ngcc:** fix compilation of `ChangeDetectorRef` in pipe constructors ([#38892](https://github.com/angular/angular/issues/38892)) ([093c3a1](https://github.com/angular/angular/commit/093c3a1)), closes [#38666](https://github.com/angular/angular/issues/38666) [#38883](https://github.com/angular/angular/issues/38883)
|
||||||
|
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* feat(router): better warning message when a router outlet has not been instantiated ([#38920](https://github.com/angular/angular/issues/38920)) ([04d0aa6](https://github.com/angular/angular/commit/04d0aa6))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="11.0.0-next.2"></a>
|
||||||
|
# 11.0.0-next.2 (2020-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** do not round up fractions of a millisecond in `DatePipe` ([#38009](https://github.com/angular/angular/issues/38009)) ([26f2820](https://github.com/angular/angular/commit/26f2820)), closes [/www.ecma-international.org/ecma-262/5.1/#sec-15](https://github.com//www.ecma-international.org/ecma-262/5.1//issues/sec-15) [#37989](https://github.com/angular/angular/issues/37989)
|
||||||
|
* **common:** mark locale data arrays as readonly ([#30397](https://github.com/angular/angular/issues/30397)) ([6acea54](https://github.com/angular/angular/commit/6acea54)), closes [#27003](https://github.com/angular/angular/issues/27003)
|
||||||
|
* **compiler:** source span for microsyntax text att should be key span ([#38766](https://github.com/angular/angular/issues/38766)) ([8f349b2](https://github.com/angular/angular/commit/8f349b2))
|
||||||
|
* **router:** Fix arguments order for call to shouldReuseRoute ([#26949](https://github.com/angular/angular/issues/26949)) ([3817e5f](https://github.com/angular/angular/commit/3817e5f)), closes [#16192](https://github.com/angular/angular/issues/16192) [#16192](https://github.com/angular/angular/issues/16192)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compiler-cli:** `TemplateTypeChecker` operation to get `Symbol` from a template node ([#38618](https://github.com/angular/angular/issues/38618)) ([c4556db](https://github.com/angular/angular/commit/c4556db))
|
||||||
|
* **compiler-cli:** Add ability to get `Symbol` of `Template`s and `Element`s in component template ([#38618](https://github.com/angular/angular/issues/38618)) ([cf2e8b9](https://github.com/angular/angular/commit/cf2e8b9))
|
||||||
|
* **compiler-cli:** Add ability to get `Symbol` of AST expression in component template ([#38618](https://github.com/angular/angular/issues/38618)) ([f56ece4](https://github.com/angular/angular/commit/f56ece4))
|
||||||
|
* **compiler-cli:** add ability to get symbol of reference or variable ([#38618](https://github.com/angular/angular/issues/38618)) ([19598b4](https://github.com/angular/angular/commit/19598b4))
|
||||||
|
* **compiler-cli:** define interfaces to be used for TemplateTypeChecker ([#38618](https://github.com/angular/angular/issues/38618)) ([9e77bd3](https://github.com/angular/angular/commit/9e77bd3))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **compiler-cli:** only emit directive/pipe references that are used ([#38539](https://github.com/angular/angular/issues/38539)) ([077f516](https://github.com/angular/angular/commit/077f516))
|
||||||
|
* **compiler-cli:** optimize computation of type-check scope information ([#38539](https://github.com/angular/angular/issues/38539)) ([297c060](https://github.com/angular/angular/commit/297c060))
|
||||||
|
* **router:** use `ngDevMode` to tree-shake error messages in router ([#38674](https://github.com/angular/angular/issues/38674)) ([db21c4f](https://github.com/angular/angular/commit/db21c4f))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **router:** This change corrects the argument order when calling
|
||||||
|
RouteReuseStrategy#shouldReuseRoute. Previously, when evaluating child
|
||||||
|
routes, they would be called with the future and current arguments would
|
||||||
|
be swapped. If your RouteReuseStrategy relies specifically on only the future
|
||||||
|
or current snapshot state, you may need to update the shouldReuseRoute
|
||||||
|
implementation's use of "future" and "current" ActivateRouteSnapshots.
|
||||||
|
* **common:** The locale data API has been marked as returning readonly arrays, rather
|
||||||
|
than mutable arrays, since these arrays are shared across calls to the
|
||||||
|
API. If you were mutating them (e.g. calling `sort()`, `push()`, `splice()`, etc)
|
||||||
|
then your code will not longer compile. If you need to mutate the array, you
|
||||||
|
should now take a copy (e.g. by calling `slice()`) and mutate the copy.
|
||||||
|
* **common:** When passing a date-time formatted string to the `DatePipe` in a format that contains
|
||||||
|
fractions of a millisecond, the milliseconds will now always be rounded down rather than
|
||||||
|
to the nearest millisecond.
|
||||||
|
|
||||||
|
Most applications will not be affected by this change. If this is not the desired behaviour
|
||||||
|
then consider pre-processing the string to round the millisecond part before passing
|
||||||
|
it to the `DatePipe`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<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="11.0.0-next.1"></a>
|
||||||
|
# 11.0.0-next.1 (2020-09-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler-cli:** compute source-mappings for localized strings ([#38645](https://github.com/angular/angular/issues/38645)) ([7e0b3fd](https://github.com/angular/angular/commit/7e0b3fd)), closes [#38588](https://github.com/angular/angular/issues/38588)
|
||||||
|
* **core:** remove CollectionChangeRecord symbol ([#38668](https://github.com/angular/angular/issues/38668)) ([fdea180](https://github.com/angular/angular/commit/fdea180))
|
||||||
|
* **router:** support lazy loading for empty path named outlets ([#38379](https://github.com/angular/angular/issues/38379)) ([926ffcd](https://github.com/angular/angular/commit/926ffcd)), closes [#12842](https://github.com/angular/angular/issues/12842)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **core:** CollectionChangeRecord has been removed, use IterableChangeRecord
|
||||||
|
instead
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="10.1.1"></a>
|
<a name="10.1.1"></a>
|
||||||
## 10.1.1 (2020-09-09)
|
## 10.1.1 (2020-09-09)
|
||||||
|
|
||||||
@ -23,6 +258,31 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="11.0.0-next.0"></a>
|
||||||
|
# 11.0.0-next.0 (2020-09-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **forms:** ensure to emit `statusChanges` on subsequent value update/validations ([#38354](https://github.com/angular/angular/issues/38354)) ([d9fea85](https://github.com/angular/angular/commit/d9fea85)), closes [#20424](https://github.com/angular/angular/issues/20424) [#14542](https://github.com/angular/angular/issues/14542)
|
||||||
|
* **service-worker:** fix condition to check for a cache-busted request ([#36847](https://github.com/angular/angular/issues/36847)) ([5be4edf](https://github.com/angular/angular/commit/5be4edf))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **service-worker:** add `UnrecoverableStateError` ([#36847](https://github.com/angular/angular/issues/36847)) ([036a2fa](https://github.com/angular/angular/commit/036a2fa)), closes [#36539](https://github.com/angular/angular/issues/36539)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **forms:** Previously if FormControl, FormGroup and FormArray class instances had async validators
|
||||||
|
defined at initialization time, the status change event was not emitted once async validator
|
||||||
|
completed. After this change the status event is emitted into the `statusChanges` observable.
|
||||||
|
If your code relies on the old behavior, you can filter/ignore this additional status change
|
||||||
|
event.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="10.1.0"></a>
|
<a name="10.1.0"></a>
|
||||||
# 10.1.0 (2020-09-02)
|
# 10.1.0 (2020-09-02)
|
||||||
|
|
||||||
|
@ -209,9 +209,9 @@ Any line of the commit message cannot be longer than 100 characters.
|
|||||||
│ │
|
│ │
|
||||||
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
|
||||||
│ elements|forms|http|language-service|localize|platform-browser|
|
│ elements|forms|http|language-service|localize|platform-browser|
|
||||||
│ platform-browser-dynamic|platform-server|platform-webworker|
|
│ platform-browser-dynamic|platform-server|router|service-worker|
|
||||||
│ platform-webworker-dynamic|router|service-worker|upgrade|zone.js|
|
│ upgrade|zone.js|packaging|changelog|dev-infra|docs-infra|migrations|
|
||||||
│ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve
|
│ ngcc|ve
|
||||||
│
|
│
|
||||||
└─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
|
└─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
|
||||||
```
|
```
|
||||||
@ -253,8 +253,6 @@ The following is the list of supported scopes:
|
|||||||
* `platform-browser`
|
* `platform-browser`
|
||||||
* `platform-browser-dynamic`
|
* `platform-browser-dynamic`
|
||||||
* `platform-server`
|
* `platform-server`
|
||||||
* `platform-webworker`
|
|
||||||
* `platform-webworker-dynamic`
|
|
||||||
* `router`
|
* `router`
|
||||||
* `service-worker`
|
* `service-worker`
|
||||||
* `upgrade`
|
* `upgrade`
|
||||||
|
@ -6,6 +6,7 @@ Everything in this folder is part of the documentation project. This includes
|
|||||||
* the dgeni configuration for converting source files to rendered files that can be viewed in the web site.
|
* the dgeni configuration for converting source files to rendered files that can be viewed in the web site.
|
||||||
* the tooling for setting up examples for development; and generating live-example and zip files from the examples.
|
* the tooling for setting up examples for development; and generating live-example and zip files from the examples.
|
||||||
|
|
||||||
|
<a name="developer-tasks"></a>
|
||||||
## Developer tasks
|
## Developer tasks
|
||||||
|
|
||||||
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
|
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
|
||||||
|
@ -22,7 +22,7 @@ you don't need to specify values for those.
|
|||||||
The domain name of the server.
|
The domain name of the server.
|
||||||
|
|
||||||
- `AIO_GITHUB_ORGANIZATION`:
|
- `AIO_GITHUB_ORGANIZATION`:
|
||||||
The GitHub organization whose teams are whitelisted for accepting build artifacts.
|
The GitHub organization whose teams are trusted for accepting build artifacts.
|
||||||
See also `AIO_GITHUB_TEAM_SLUGS`.
|
See also `AIO_GITHUB_TEAM_SLUGS`.
|
||||||
|
|
||||||
- `AIO_GITHUB_REPO`:
|
- `AIO_GITHUB_REPO`:
|
||||||
|
@ -98,7 +98,7 @@ This section describes how each of the aforementioned sub-tasks is accomplished:
|
|||||||
Such a label can only have been added by a maintainer (with the necessary rights) and
|
Such a label can only have been added by a maintainer (with the necessary rights) and
|
||||||
designates that they have manually verified the PR contents.
|
designates that they have manually verified the PR contents.
|
||||||
2. We can verify (again using the GitHub API) the author's membership in one of the
|
2. We can verify (again using the GitHub API) the author's membership in one of the
|
||||||
whitelisted/trusted GitHub teams. For this operation, we need a Personal Access Token with the
|
trusted GitHub teams. For this operation, we need a Personal Access Token with the
|
||||||
`read:org` scope issued by a user that can "see" the specified GitHub organization.
|
`read:org` scope issued by a user that can "see" the specified GitHub organization.
|
||||||
Here too, we use the token by @mary-poppins.
|
Here too, we use the token by @mary-poppins.
|
||||||
|
|
||||||
|
2
aio/content/examples/.gitignore
vendored
2
aio/content/examples/.gitignore
vendored
@ -17,6 +17,7 @@
|
|||||||
**/e2e/tsconfig.e2e.json
|
**/e2e/tsconfig.e2e.json
|
||||||
**/src/karma.conf.js
|
**/src/karma.conf.js
|
||||||
**/.angular-cli.json
|
**/.angular-cli.json
|
||||||
|
**/.browserslistrc
|
||||||
**/.editorconfig
|
**/.editorconfig
|
||||||
**/.gitignore
|
**/.gitignore
|
||||||
**/angular.json
|
**/angular.json
|
||||||
@ -30,7 +31,6 @@
|
|||||||
**/tslint.json
|
**/tslint.json
|
||||||
**/karma-test-shim.js
|
**/karma-test-shim.js
|
||||||
**/browser-test-shim.js
|
**/browser-test-shim.js
|
||||||
**/browserslist
|
|
||||||
**/node_modules
|
**/node_modules
|
||||||
|
|
||||||
# built files
|
# built files
|
||||||
|
@ -21,11 +21,13 @@ import { ItemDirective } from './item.directive';
|
|||||||
ItemDirective
|
ItemDirective
|
||||||
],
|
],
|
||||||
// #enddocregion declarations
|
// #enddocregion declarations
|
||||||
|
// #docregion imports
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
HttpClientModule
|
HttpClientModule
|
||||||
],
|
],
|
||||||
|
// #enddocregion imports
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* This example project is special in that it is not a cli app. To run tests appropriate for this
|
||||||
|
* project, the test command is overwritten in `aio/content/examples/observables/example-config.json`.
|
||||||
|
*
|
||||||
|
* This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.js` runs
|
||||||
|
* tests for this project.
|
||||||
|
*
|
||||||
|
* TODO: Fix our infrastructure/tooling, so that this hack is not necessary.
|
||||||
|
*/
|
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": ["jasmine", "out-tsc/**/*.spec.js"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { docRegionChain, docRegionObservable, docRegionUnsubscribe } from './observables';
|
||||||
|
|
||||||
|
describe('observables', () => {
|
||||||
|
it('should print 2', (doneFn: DoneFn) => {
|
||||||
|
const consoleLogSpy = spyOn(console, 'log');
|
||||||
|
const observable = docRegionObservable(console);
|
||||||
|
observable.subscribe(() => {
|
||||||
|
expect(consoleLogSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(consoleLogSpy).toHaveBeenCalledWith(2);
|
||||||
|
doneFn();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close the subscription', () => {
|
||||||
|
const subscription = docRegionUnsubscribe();
|
||||||
|
expect(subscription.closed).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should chain an observable', (doneFn: DoneFn) => {
|
||||||
|
const observable = docRegionChain();
|
||||||
|
observable.subscribe(value => {
|
||||||
|
expect(value).toBe(4);
|
||||||
|
doneFn();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,20 +1,37 @@
|
|||||||
import { map } from 'rxjs/operators';
|
// #docplaster
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
export function docRegionObservable(console: Console) {
|
||||||
// #docregion observable
|
// #docregion observable
|
||||||
|
|
||||||
// declare a publishing operation
|
// declare a publishing operation
|
||||||
const observable = new Observable<number>(observer => {
|
const observable = new Observable<number>(observer => {
|
||||||
// Subscriber fn...
|
// Subscriber fn...
|
||||||
|
// #enddocregion observable
|
||||||
|
// The below code is used for unit testing only
|
||||||
|
observer.next(2);
|
||||||
|
// #docregion observable
|
||||||
});
|
});
|
||||||
|
|
||||||
// initiate execution
|
// initiate execution
|
||||||
observable.subscribe(() => {
|
observable.subscribe(value => {
|
||||||
// observer handles notifications
|
// observer handles notifications
|
||||||
|
// #enddocregion observable
|
||||||
|
// The below code is used for unit testing only
|
||||||
|
console.log(value);
|
||||||
|
// #docregion observable
|
||||||
});
|
});
|
||||||
|
|
||||||
// #enddocregion observable
|
// #enddocregion observable
|
||||||
|
return observable;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function docRegionUnsubscribe() {
|
||||||
|
const observable = new Observable<number>(() => {
|
||||||
|
// Subscriber fn...
|
||||||
|
});
|
||||||
// #docregion unsubscribe
|
// #docregion unsubscribe
|
||||||
|
|
||||||
const subscription = observable.subscribe(() => {
|
const subscription = observable.subscribe(() => {
|
||||||
@ -24,17 +41,32 @@ const subscription = observable.subscribe(() => {
|
|||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
|
|
||||||
// #enddocregion unsubscribe
|
// #enddocregion unsubscribe
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
// #docregion error
|
export function docRegionError() {
|
||||||
|
const observable = new Observable<number>(() => {
|
||||||
observable.subscribe(() => {
|
// Subscriber fn...
|
||||||
throw Error('my error');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// #docregion error
|
||||||
|
observable.subscribe(() => {
|
||||||
|
throw new Error('my error');
|
||||||
|
});
|
||||||
// #enddocregion error
|
// #enddocregion error
|
||||||
|
}
|
||||||
|
|
||||||
|
export function docRegionChain() {
|
||||||
|
let observable = new Observable<number>(observer => {
|
||||||
|
// Subscriber fn...
|
||||||
|
observer.next(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
observable =
|
||||||
// #docregion chain
|
// #docregion chain
|
||||||
|
|
||||||
observable.pipe(map(v => 2 * v));
|
observable.pipe(map(v => 2 * v));
|
||||||
|
|
||||||
// #enddocregion chain
|
// #enddocregion chain
|
||||||
|
return observable;
|
||||||
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
import { docRegionError, docRegionPromise } from './promises';
|
||||||
|
|
||||||
|
describe('promises', () => {
|
||||||
|
it('should print 2', (doneFn: DoneFn) => {
|
||||||
|
const consoleLogSpy = spyOn(console, 'log');
|
||||||
|
const pr = docRegionPromise(console, 2);
|
||||||
|
pr.then((value) => {
|
||||||
|
expect(consoleLogSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(consoleLogSpy).toHaveBeenCalledWith(2);
|
||||||
|
expect(value).toBe(4);
|
||||||
|
doneFn();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error', (doneFn: DoneFn) => {
|
||||||
|
const promise = docRegionError();
|
||||||
|
promise
|
||||||
|
.then(() => {
|
||||||
|
throw new Error('Promise should be rejected.');
|
||||||
|
},
|
||||||
|
() => doneFn());
|
||||||
|
});
|
||||||
|
});
|
@ -1,25 +1,44 @@
|
|||||||
|
// #docplaster
|
||||||
|
|
||||||
|
export function docRegionPromise(console: Console, inputValue: number) {
|
||||||
// #docregion promise
|
// #docregion promise
|
||||||
// initiate execution
|
// initiate execution
|
||||||
const promise = new Promise<number>((resolve, reject) => {
|
let promise = new Promise<number>((resolve, reject) => {
|
||||||
// Executer fn...
|
// Executer fn...
|
||||||
|
// #enddocregion promise
|
||||||
|
// The below is used in the unit tests.
|
||||||
|
resolve(inputValue);
|
||||||
|
// #docregion promise
|
||||||
});
|
});
|
||||||
|
// #enddocregion promise
|
||||||
|
promise =
|
||||||
|
// #docregion promise
|
||||||
promise.then(value => {
|
promise.then(value => {
|
||||||
// handle result here
|
// handle result here
|
||||||
});
|
|
||||||
|
|
||||||
// #enddocregion promise
|
// #enddocregion promise
|
||||||
|
// The below is used in the unit tests.
|
||||||
|
console.log(value);
|
||||||
|
return value;
|
||||||
|
// #docregion promise
|
||||||
|
});
|
||||||
|
// #enddocregion promise
|
||||||
|
promise =
|
||||||
// #docregion chain
|
// #docregion chain
|
||||||
|
|
||||||
promise.then(v => 2 * v);
|
promise.then(v => 2 * v);
|
||||||
|
|
||||||
// #enddocregion chain
|
// #enddocregion chain
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function docRegionError() {
|
||||||
|
let promise = Promise.resolve();
|
||||||
|
promise =
|
||||||
// #docregion error
|
// #docregion error
|
||||||
|
|
||||||
promise.then(() => {
|
promise.then(() => {
|
||||||
throw Error('my error');
|
throw new Error('my error');
|
||||||
});
|
});
|
||||||
|
|
||||||
// #enddocregion error
|
// #enddocregion error
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
import { browser, element, by } from 'protractor';
|
import { browser, by, element } from 'protractor';
|
||||||
|
|
||||||
describe('Component Communication Cookbook Tests', () => {
|
describe('Component Communication Cookbook Tests', () => {
|
||||||
|
|
||||||
// Note: '?e2e' which app can read to know it is running in protractor
|
beforeEach(() => browser.get(browser.baseUrl));
|
||||||
// e.g. `if (!/e2e/.test(location.search)) { ...`
|
|
||||||
beforeAll(() => {
|
|
||||||
browser.get('?e2e');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Parent-to-child communication', () => {
|
describe('Parent-to-child communication', () => {
|
||||||
// #docregion parent-to-child
|
// #docregion parent-to-child
|
||||||
@ -15,7 +11,7 @@ describe('Component Communication Cookbook Tests', () => {
|
|||||||
const masterName = 'Master';
|
const masterName = 'Master';
|
||||||
|
|
||||||
it('should pass properties to children properly', () => {
|
it('should pass properties to children properly', () => {
|
||||||
const parent = element.all(by.tagName('app-hero-parent')).get(0);
|
const parent = element(by.tagName('app-hero-parent'));
|
||||||
const heroes = parent.all(by.tagName('app-hero-child'));
|
const heroes = parent.all(by.tagName('app-hero-child'));
|
||||||
|
|
||||||
for (let i = 0; i < heroNames.length; i++) {
|
for (let i = 0; i < heroNames.length; i++) {
|
||||||
@ -35,7 +31,7 @@ describe('Component Communication Cookbook Tests', () => {
|
|||||||
it('should display trimmed, non-empty names', () => {
|
it('should display trimmed, non-empty names', () => {
|
||||||
const nonEmptyNameIndex = 0;
|
const nonEmptyNameIndex = 0;
|
||||||
const nonEmptyName = '"Dr IQ"';
|
const nonEmptyName = '"Dr IQ"';
|
||||||
const parent = element.all(by.tagName('app-name-parent')).get(0);
|
const parent = element(by.tagName('app-name-parent'));
|
||||||
const hero = parent.all(by.tagName('app-name-child')).get(nonEmptyNameIndex);
|
const hero = parent.all(by.tagName('app-name-child')).get(nonEmptyNameIndex);
|
||||||
|
|
||||||
const displayName = hero.element(by.tagName('h3')).getText();
|
const displayName = hero.element(by.tagName('h3')).getText();
|
||||||
@ -45,7 +41,7 @@ describe('Component Communication Cookbook Tests', () => {
|
|||||||
it('should replace empty name with default name', () => {
|
it('should replace empty name with default name', () => {
|
||||||
const emptyNameIndex = 1;
|
const emptyNameIndex = 1;
|
||||||
const defaultName = '"<no name set>"';
|
const defaultName = '"<no name set>"';
|
||||||
const parent = element.all(by.tagName('app-name-parent')).get(0);
|
const parent = element(by.tagName('app-name-parent'));
|
||||||
const hero = parent.all(by.tagName('app-name-child')).get(emptyNameIndex);
|
const hero = parent.all(by.tagName('app-name-child')).get(emptyNameIndex);
|
||||||
|
|
||||||
const displayName = hero.element(by.tagName('h3')).getText();
|
const displayName = hero.element(by.tagName('h3')).getText();
|
||||||
@ -70,12 +66,13 @@ describe('Component Communication Cookbook Tests', () => {
|
|||||||
expect(actual.logs.get(0).getText()).toBe(initialLog);
|
expect(actual.logs.get(0).getText()).toBe(initialLog);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set expected values after clicking \'Minor\' twice', () => {
|
it('should set expected values after clicking \'Minor\' twice', async () => {
|
||||||
const repoTag = element(by.tagName('app-version-parent'));
|
const repoTag = element(by.tagName('app-version-parent'));
|
||||||
const newMinorButton = repoTag.all(by.tagName('button')).get(0);
|
const newMinorButton = repoTag.all(by.tagName('button')).get(0);
|
||||||
|
|
||||||
newMinorButton.click().then(() => {
|
await newMinorButton.click();
|
||||||
newMinorButton.click().then(() => {
|
await newMinorButton.click();
|
||||||
|
|
||||||
const actual = getActual();
|
const actual = getActual();
|
||||||
|
|
||||||
const labelAfter2Minor = 'Version 1.25';
|
const labelAfter2Minor = 'Version 1.25';
|
||||||
@ -85,23 +82,20 @@ describe('Component Communication Cookbook Tests', () => {
|
|||||||
expect(actual.count).toBe(3);
|
expect(actual.count).toBe(3);
|
||||||
expect(actual.logs.get(2).getText()).toBe(logAfter2Minor);
|
expect(actual.logs.get(2).getText()).toBe(logAfter2Minor);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set expected values after clicking \'Major\' once', () => {
|
it('should set expected values after clicking \'Major\' once', async () => {
|
||||||
const repoTag = element(by.tagName('app-version-parent'));
|
const repoTag = element(by.tagName('app-version-parent'));
|
||||||
const newMajorButton = repoTag.all(by.tagName('button')).get(1);
|
const newMajorButton = repoTag.all(by.tagName('button')).get(1);
|
||||||
|
|
||||||
newMajorButton.click().then(() => {
|
await newMajorButton.click();
|
||||||
const actual = getActual();
|
const actual = getActual();
|
||||||
|
|
||||||
const labelAfterMajor = 'Version 2.0';
|
const labelAfterMajor = 'Version 2.0';
|
||||||
const logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
|
const logAfterMajor = 'major changed from 1 to 2, minor changed from 23 to 0';
|
||||||
|
|
||||||
expect(actual.label).toBe(labelAfterMajor);
|
expect(actual.label).toBe(labelAfterMajor);
|
||||||
expect(actual.count).toBe(4);
|
expect(actual.count).toBe(2);
|
||||||
expect(actual.logs.get(3).getText()).toBe(logAfterMajor);
|
expect(actual.logs.get(1).getText()).toBe(logAfterMajor);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function getActual() {
|
function getActual() {
|
||||||
@ -118,110 +112,125 @@ describe('Component Communication Cookbook Tests', () => {
|
|||||||
}
|
}
|
||||||
// ...
|
// ...
|
||||||
// #enddocregion parent-to-child-onchanges
|
// #enddocregion parent-to-child-onchanges
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Child-to-parent communication', () => {
|
describe('Child-to-parent communication', () => {
|
||||||
// #docregion child-to-parent
|
// #docregion child-to-parent
|
||||||
// ...
|
// ...
|
||||||
it('should not emit the event initially', () => {
|
it('should not emit the event initially', () => {
|
||||||
const voteLabel = element(by.tagName('app-vote-taker'))
|
const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
|
||||||
.element(by.tagName('h3')).getText();
|
expect(voteLabel.getText()).toBe('Agree: 0, Disagree: 0');
|
||||||
expect(voteLabel).toBe('Agree: 0, Disagree: 0');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should process Agree vote', () => {
|
it('should process Agree vote', async () => {
|
||||||
|
const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
|
||||||
const agreeButton1 = element.all(by.tagName('app-voter')).get(0)
|
const agreeButton1 = element.all(by.tagName('app-voter')).get(0)
|
||||||
.all(by.tagName('button')).get(0);
|
.all(by.tagName('button')).get(0);
|
||||||
agreeButton1.click().then(() => {
|
|
||||||
const voteLabel = element(by.tagName('app-vote-taker'))
|
await agreeButton1.click();
|
||||||
.element(by.tagName('h3')).getText();
|
|
||||||
expect(voteLabel).toBe('Agree: 1, Disagree: 0');
|
expect(voteLabel.getText()).toBe('Agree: 1, Disagree: 0');
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should process Disagree vote', () => {
|
it('should process Disagree vote', async () => {
|
||||||
|
const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
|
||||||
const agreeButton1 = element.all(by.tagName('app-voter')).get(1)
|
const agreeButton1 = element.all(by.tagName('app-voter')).get(1)
|
||||||
.all(by.tagName('button')).get(1);
|
.all(by.tagName('button')).get(1);
|
||||||
agreeButton1.click().then(() => {
|
|
||||||
const voteLabel = element(by.tagName('app-vote-taker'))
|
await agreeButton1.click();
|
||||||
.element(by.tagName('h3')).getText();
|
|
||||||
expect(voteLabel).toBe('Agree: 1, Disagree: 1');
|
expect(voteLabel.getText()).toBe('Agree: 0, Disagree: 1');
|
||||||
});
|
|
||||||
});
|
});
|
||||||
// ...
|
// ...
|
||||||
// #enddocregion child-to-parent
|
// #enddocregion child-to-parent
|
||||||
});
|
});
|
||||||
|
|
||||||
// Can't run timer tests in protractor because
|
describe('Parent calls child via local var', () => {
|
||||||
// interaction w/ zones causes all tests to freeze & timeout.
|
countDownTimerTests('app-countdown-parent-lv');
|
||||||
xdescribe('Parent calls child via local var', () => {
|
|
||||||
countDownTimerTests('countdown-parent-lv');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
xdescribe('Parent calls ViewChild', () => {
|
describe('Parent calls ViewChild', () => {
|
||||||
countDownTimerTests('countdown-parent-vc');
|
countDownTimerTests('app-countdown-parent-vc');
|
||||||
});
|
});
|
||||||
|
|
||||||
function countDownTimerTests(parentTag: string) {
|
function countDownTimerTests(parentTag: string) {
|
||||||
// #docregion countdown-timer-tests
|
// #docregion countdown-timer-tests
|
||||||
// ...
|
// ...
|
||||||
it('timer and parent seconds should match', () => {
|
// The tests trigger periodic asynchronous operations (via `setInterval()`), which will prevent
|
||||||
|
// the app from stabilizing. See https://angular.io/api/core/ApplicationRef#is-stable-examples
|
||||||
|
// for more details.
|
||||||
|
// To allow the tests to complete, we will disable automatically waiting for the Angular app to
|
||||||
|
// stabilize.
|
||||||
|
beforeEach(() => browser.waitForAngularEnabled(false));
|
||||||
|
afterEach(() => browser.waitForAngularEnabled(true));
|
||||||
|
|
||||||
|
it('timer and parent seconds should match', async () => {
|
||||||
const parent = element(by.tagName(parentTag));
|
const parent = element(by.tagName(parentTag));
|
||||||
const message = parent.element(by.tagName('app-countdown-timer')).getText();
|
const startButton = parent.element(by.buttonText('Start'));
|
||||||
browser.sleep(10); // give `seconds` a chance to catchup with `message`
|
const seconds = parent.element(by.className('seconds'));
|
||||||
const seconds = parent.element(by.className('seconds')).getText();
|
const timer = parent.element(by.tagName('app-countdown-timer'));
|
||||||
expect(message).toContain(seconds);
|
|
||||||
|
await startButton.click();
|
||||||
|
|
||||||
|
// Wait for `<app-countdown-timer>` to be populated with any text.
|
||||||
|
await browser.wait(() => timer.getText(), 2000);
|
||||||
|
|
||||||
|
expect(await timer.getText()).toContain(await seconds.getText());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should stop the countdown', () => {
|
it('should stop the countdown', async () => {
|
||||||
const parent = element(by.tagName(parentTag));
|
const parent = element(by.tagName(parentTag));
|
||||||
const stopButton = parent.all(by.tagName('button')).get(1);
|
const startButton = parent.element(by.buttonText('Start'));
|
||||||
|
const stopButton = parent.element(by.buttonText('Stop'));
|
||||||
|
const timer = parent.element(by.tagName('app-countdown-timer'));
|
||||||
|
|
||||||
stopButton.click().then(() => {
|
await startButton.click();
|
||||||
const message = parent.element(by.tagName('app-countdown-timer')).getText();
|
expect(await timer.getText()).not.toContain('Holding');
|
||||||
expect(message).toContain('Holding');
|
|
||||||
});
|
await stopButton.click();
|
||||||
|
expect(await timer.getText()).toContain('Holding');
|
||||||
});
|
});
|
||||||
// ...
|
// ...
|
||||||
// #enddocregion countdown-timer-tests
|
// #enddocregion countdown-timer-tests
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
describe('Parent and children communicate via a service', () => {
|
describe('Parent and children communicate via a service', () => {
|
||||||
// #docregion bidirectional-service
|
// #docregion bidirectional-service
|
||||||
// ...
|
// ...
|
||||||
it('should announce a mission', () => {
|
it('should announce a mission', async () => {
|
||||||
const missionControl = element(by.tagName('app-mission-control'));
|
const missionControl = element(by.tagName('app-mission-control'));
|
||||||
const announceButton = missionControl.all(by.tagName('button')).get(0);
|
const announceButton = missionControl.all(by.tagName('button')).get(0);
|
||||||
announceButton.click().then(() => {
|
|
||||||
const history = missionControl.all(by.tagName('li'));
|
const history = missionControl.all(by.tagName('li'));
|
||||||
|
|
||||||
|
await announceButton.click();
|
||||||
|
|
||||||
expect(history.count()).toBe(1);
|
expect(history.count()).toBe(1);
|
||||||
expect(history.get(0).getText()).toMatch(/Mission.* announced/);
|
expect(history.get(0).getText()).toMatch(/Mission.* announced/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should confirm the mission by Lovell', async () => {
|
||||||
|
await testConfirmMission(1, 'Lovell');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm the mission by Lovell', () => {
|
it('should confirm the mission by Haise', async () => {
|
||||||
testConfirmMission(1, 2, 'Lovell');
|
await testConfirmMission(3, 'Haise');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm the mission by Haise', () => {
|
it('should confirm the mission by Swigert', async () => {
|
||||||
testConfirmMission(3, 3, 'Haise');
|
await testConfirmMission(2, 'Swigert');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm the mission by Swigert', () => {
|
async function testConfirmMission(buttonIndex: number, astronaut: string) {
|
||||||
testConfirmMission(2, 4, 'Swigert');
|
|
||||||
});
|
|
||||||
|
|
||||||
function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
|
|
||||||
const confirmedLog = ' confirmed the mission';
|
|
||||||
const missionControl = element(by.tagName('app-mission-control'));
|
const missionControl = element(by.tagName('app-mission-control'));
|
||||||
|
const announceButton = missionControl.all(by.tagName('button')).get(0);
|
||||||
const confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
|
const confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
|
||||||
confirmButton.click().then(() => {
|
|
||||||
const history = missionControl.all(by.tagName('li'));
|
const history = missionControl.all(by.tagName('li'));
|
||||||
expect(history.count()).toBe(expectedLogCount);
|
|
||||||
expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + confirmedLog);
|
await announceButton.click();
|
||||||
});
|
await confirmButton.click();
|
||||||
|
|
||||||
|
expect(history.count()).toBe(2);
|
||||||
|
expect(history.get(1).getText()).toBe(`${astronaut} confirmed the mission`);
|
||||||
}
|
}
|
||||||
// ...
|
// ...
|
||||||
// #enddocregion bidirectional-service
|
// #enddocregion bidirectional-service
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"tests": [
|
|
||||||
{
|
|
||||||
"cmd": "yarn",
|
|
||||||
"args": [
|
|
||||||
"e2e",
|
|
||||||
"--protractor-config=e2e/protractor-puppeteer.conf.js",
|
|
||||||
"--no-webdriver-update",
|
|
||||||
"--port={PORT}"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
@ -30,22 +30,21 @@
|
|||||||
<app-vote-taker></app-vote-taker>
|
<app-vote-taker></app-vote-taker>
|
||||||
</div>
|
</div>
|
||||||
<a href="#top" class="to-top">Back to Top</a>
|
<a href="#top" class="to-top">Back to Top</a>
|
||||||
<hr>
|
|
||||||
|
|
||||||
|
<hr>
|
||||||
<div id="parent-to-child-local-var">
|
<div id="parent-to-child-local-var">
|
||||||
<app-countdown-parent-lv></app-countdown-parent-lv>
|
<app-countdown-parent-lv></app-countdown-parent-lv>
|
||||||
</div>
|
</div>
|
||||||
<a href="#top" class="to-top">Back to Top</a>
|
<a href="#top" class="to-top">Back to Top</a>
|
||||||
<hr>
|
|
||||||
|
|
||||||
|
<hr>
|
||||||
<div id="parent-to-view-child">
|
<div id="parent-to-view-child">
|
||||||
<app-countdown-parent-vc></app-countdown-parent-vc>
|
<app-countdown-parent-vc></app-countdown-parent-vc>
|
||||||
</div>
|
</div>
|
||||||
<a href="#top" class="to-top">Back to Top</a>
|
<a href="#top" class="to-top">Back to Top</a>
|
||||||
<hr>
|
|
||||||
|
|
||||||
|
<hr>
|
||||||
<div id="bidirectional-service">
|
<div id="bidirectional-service">
|
||||||
<app-mission-control></app-mission-control>
|
<app-mission-control></app-mission-control>
|
||||||
</div>
|
</div>
|
||||||
<a href="#top" class="to-top">Back to Top</a>
|
<a href="#top" class="to-top">Back to Top</a>
|
||||||
<hr>
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
@ -15,10 +15,17 @@ import { VersionParentComponent } from './version-parent.component';
|
|||||||
import { VoterComponent } from './voter.component';
|
import { VoterComponent } from './voter.component';
|
||||||
import { VoteTakerComponent } from './votetaker.component';
|
import { VoteTakerComponent } from './votetaker.component';
|
||||||
|
|
||||||
const directives: any[] = [
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
AstronautComponent,
|
AstronautComponent,
|
||||||
|
CountdownLocalVarParentComponent,
|
||||||
CountdownTimerComponent,
|
CountdownTimerComponent,
|
||||||
|
CountdownViewChildParentComponent,
|
||||||
HeroChildComponent,
|
HeroChildComponent,
|
||||||
HeroParentComponent,
|
HeroParentComponent,
|
||||||
MissionControlComponent,
|
MissionControlComponent,
|
||||||
@ -27,28 +34,8 @@ const directives: any[] = [
|
|||||||
VersionChildComponent,
|
VersionChildComponent,
|
||||||
VersionParentComponent,
|
VersionParentComponent,
|
||||||
VoterComponent,
|
VoterComponent,
|
||||||
VoteTakerComponent
|
VoteTakerComponent,
|
||||||
];
|
|
||||||
|
|
||||||
const schemas: any[] = [];
|
|
||||||
|
|
||||||
// Include Countdown examples
|
|
||||||
// unless in e2e tests which they break.
|
|
||||||
if (!/e2e/.test(location.search)) {
|
|
||||||
console.log('adding countdown timer examples');
|
|
||||||
directives.push(CountdownLocalVarParentComponent);
|
|
||||||
directives.push(CountdownViewChildParentComponent);
|
|
||||||
} else {
|
|
||||||
// In e2e test use CUSTOM_ELEMENTS_SCHEMA to suppress unknown element errors
|
|
||||||
schemas.push(CUSTOM_ELEMENTS_SCHEMA);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
BrowserModule
|
|
||||||
],
|
],
|
||||||
declarations: directives,
|
|
||||||
bootstrap: [ AppComponent ],
|
bootstrap: [ AppComponent ],
|
||||||
schemas
|
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-countdown-timer',
|
selector: 'app-countdown-timer',
|
||||||
template: '<p>{{message}}</p>'
|
template: '<p>{{message}}</p>'
|
||||||
})
|
})
|
||||||
export class CountdownTimerComponent implements OnInit, OnDestroy {
|
export class CountdownTimerComponent implements OnDestroy {
|
||||||
|
|
||||||
intervalId = 0;
|
intervalId = 0;
|
||||||
message = '';
|
message = '';
|
||||||
seconds = 11;
|
seconds = 11;
|
||||||
|
|
||||||
clearTimer() { clearInterval(this.intervalId); }
|
|
||||||
|
|
||||||
ngOnInit() { this.start(); }
|
|
||||||
ngOnDestroy() { this.clearTimer(); }
|
ngOnDestroy() { this.clearTimer(); }
|
||||||
|
|
||||||
start() { this.countDown(); }
|
start() { this.countDown(); }
|
||||||
@ -22,6 +19,8 @@ export class CountdownTimerComponent implements OnInit, OnDestroy {
|
|||||||
this.message = `Holding at T-${this.seconds} seconds`;
|
this.message = `Holding at T-${this.seconds} seconds`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private clearTimer() { clearInterval(this.intervalId); }
|
||||||
|
|
||||||
private countDown() {
|
private countDown() {
|
||||||
this.clearTimer();
|
this.clearTimer();
|
||||||
this.intervalId = window.setInterval(() => {
|
this.intervalId = window.setInterval(() => {
|
||||||
|
@ -24,7 +24,7 @@ export class UploaderService {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
upload(file: File) {
|
upload(file: File) {
|
||||||
if (!file) { return; }
|
if (!file) { return of<string>(); }
|
||||||
|
|
||||||
// COULD HAVE WRITTEN:
|
// COULD HAVE WRITTEN:
|
||||||
// return this.http.post('/upload/file', file, {
|
// return this.http.post('/upload/file', file, {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// TODO: Add unit tests for this file.
|
||||||
// tslint:disable: no-output-native
|
// tslint:disable: no-output-native
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core';
|
import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core';
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
"tests": [
|
"tests": [
|
||||||
{
|
{
|
||||||
"cmd": "yarn",
|
"cmd": "yarn",
|
||||||
"args": [ "tsc", "--project", "./tsconfig.app.json" ]
|
"args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": ["jasmine", "out-tsc/**/*.spec.js"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
55
aio/content/examples/observables/src/creating.spec.ts
Normal file
55
aio/content/examples/observables/src/creating.spec.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { docRegionFromEvent, docRegionSubscriber } from './creating';
|
||||||
|
|
||||||
|
describe('observables', () => {
|
||||||
|
it('should create an observable using the constructor', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
docRegionSubscriber(console);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(4);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
[1],
|
||||||
|
[2],
|
||||||
|
[3],
|
||||||
|
['Finished sequence'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should listen to input changes', () => {
|
||||||
|
let triggerInputChange;
|
||||||
|
const input = {
|
||||||
|
value: 'Test',
|
||||||
|
addEventListener: jasmine
|
||||||
|
.createSpy('addEvent')
|
||||||
|
.and.callFake((eventName: string, cb: (e) => void) => {
|
||||||
|
if (eventName === 'keydown') {
|
||||||
|
triggerInputChange = cb;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
removeEventListener: jasmine.createSpy('removeEventListener'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const document = { getElementById: () => input };
|
||||||
|
docRegionFromEvent(document);
|
||||||
|
triggerInputChange({keyCode: 65});
|
||||||
|
expect(input.value).toBe('Test');
|
||||||
|
|
||||||
|
triggerInputChange({keyCode: 27});
|
||||||
|
expect(input.value).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call removeEventListener when unsubscribing', (doneFn: DoneFn) => {
|
||||||
|
const input = {
|
||||||
|
addEventListener: jasmine.createSpy('addEvent'),
|
||||||
|
removeEventListener: jasmine
|
||||||
|
.createSpy('removeEvent')
|
||||||
|
.and.callFake((eventName: string, cb: (e) => void) => {
|
||||||
|
if (eventName === 'keydown') {
|
||||||
|
doneFn();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const document = { getElementById: () => input };
|
||||||
|
const subscription = docRegionFromEvent(document);
|
||||||
|
subscription.unsubscribe();
|
||||||
|
});
|
||||||
|
});
|
@ -1,8 +1,9 @@
|
|||||||
|
// #docplaster
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
export function docRegionSubscriber(console) {
|
||||||
// #docregion subscriber
|
// #docregion subscriber
|
||||||
|
|
||||||
// This function runs when subscribe() is called
|
// This function runs when subscribe() is called
|
||||||
function sequenceSubscriber(observer) {
|
function sequenceSubscriber(observer) {
|
||||||
// synchronously deliver 1, 2, and 3, then complete
|
// synchronously deliver 1, 2, and 3, then complete
|
||||||
@ -30,8 +31,8 @@ sequence.subscribe({
|
|||||||
// 2
|
// 2
|
||||||
// 3
|
// 3
|
||||||
// Finished sequence
|
// Finished sequence
|
||||||
|
|
||||||
// #enddocregion subscriber
|
// #enddocregion subscriber
|
||||||
|
}
|
||||||
|
|
||||||
// #docregion fromevent
|
// #docregion fromevent
|
||||||
|
|
||||||
@ -51,16 +52,18 @@ function fromEvent(target, eventName) {
|
|||||||
|
|
||||||
// #enddocregion fromevent
|
// #enddocregion fromevent
|
||||||
|
|
||||||
|
export function docRegionFromEvent(document) {
|
||||||
// #docregion fromevent_use
|
// #docregion fromevent_use
|
||||||
|
|
||||||
const ESC_KEY = 27;
|
const ESC_KEY = 27;
|
||||||
const nameInput = document.getElementById('name') as HTMLInputElement;
|
const nameInput = document.getElementById('name') as HTMLInputElement;
|
||||||
|
|
||||||
const subscription = fromEvent(nameInput, 'keydown')
|
const subscription = fromEvent(nameInput, 'keydown').subscribe((e: KeyboardEvent) => {
|
||||||
.subscribe((e: KeyboardEvent) => {
|
|
||||||
if (e.keyCode === ESC_KEY) {
|
if (e.keyCode === ESC_KEY) {
|
||||||
nameInput.value = '';
|
nameInput.value = '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// #enddocregion fromevent_use
|
// #enddocregion fromevent_use
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
// TODO: Add unit tests for this file.
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
// Create an Observable that will start listening to geolocation updates
|
// Create an Observable that will start listening to geolocation updates
|
||||||
|
48
aio/content/examples/observables/src/multicasting.spec.ts
Normal file
48
aio/content/examples/observables/src/multicasting.spec.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { docRegionDelaySequence, docRegionMulticastSequence } from './multicasting';
|
||||||
|
|
||||||
|
describe('multicasting', () => {
|
||||||
|
let console;
|
||||||
|
beforeEach(() => {
|
||||||
|
jasmine.clock().install();
|
||||||
|
console = {log: jasmine.createSpy('log')};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jasmine.clock().uninstall();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an observable and emit in sequence', () => {
|
||||||
|
docRegionDelaySequence(console);
|
||||||
|
jasmine.clock().tick(10000);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(12);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
[1],
|
||||||
|
['1st subscribe: 1'],
|
||||||
|
['2nd subscribe: 1'],
|
||||||
|
[2],
|
||||||
|
['1st subscribe: 2'],
|
||||||
|
['2nd subscribe: 2'],
|
||||||
|
[3],
|
||||||
|
['Finished sequence'],
|
||||||
|
['1st subscribe: 3'],
|
||||||
|
['1st sequence finished.'],
|
||||||
|
['2nd subscribe: 3'],
|
||||||
|
['2nd sequence finished.']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an observable and multicast the emissions', () => {
|
||||||
|
docRegionMulticastSequence(console);
|
||||||
|
jasmine.clock().tick(10000);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(7);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
['1st subscribe: 1'],
|
||||||
|
['1st subscribe: 2'],
|
||||||
|
['2nd subscribe: 2'],
|
||||||
|
['1st subscribe: 3'],
|
||||||
|
['2nd subscribe: 3'],
|
||||||
|
['1st sequence finished.'],
|
||||||
|
['2nd sequence finished.']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,8 +1,9 @@
|
|||||||
|
// #docplaster
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
export function docRegionDelaySequence(console) {
|
||||||
// #docregion delay_sequence
|
// #docregion delay_sequence
|
||||||
|
|
||||||
function sequenceSubscriber(observer) {
|
function sequenceSubscriber(observer) {
|
||||||
const seq = [1, 2, 3];
|
const seq = [1, 2, 3];
|
||||||
let timeoutId;
|
let timeoutId;
|
||||||
@ -23,9 +24,11 @@ function sequenceSubscriber(observer) {
|
|||||||
doInSequence(seq, 0);
|
doInSequence(seq, 0);
|
||||||
|
|
||||||
// Unsubscribe should clear the timeout to stop execution
|
// Unsubscribe should clear the timeout to stop execution
|
||||||
return {unsubscribe() {
|
return {
|
||||||
|
unsubscribe() {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
}};
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new Observable that will deliver the above sequence
|
// Create a new Observable that will deliver the above sequence
|
||||||
@ -71,9 +74,10 @@ setTimeout(() => {
|
|||||||
// (at 3.5 seconds): 2nd sequence finished
|
// (at 3.5 seconds): 2nd sequence finished
|
||||||
|
|
||||||
// #enddocregion subscribe_twice
|
// #enddocregion subscribe_twice
|
||||||
|
}
|
||||||
|
|
||||||
|
export function docRegionMulticastSequence(console) {
|
||||||
// #docregion multicast_sequence
|
// #docregion multicast_sequence
|
||||||
|
|
||||||
function multicastSequenceSubscriber() {
|
function multicastSequenceSubscriber() {
|
||||||
const seq = [1, 2, 3];
|
const seq = [1, 2, 3];
|
||||||
// Keep track of each observer (one for every active subscription)
|
// Keep track of each observer (one for every active subscription)
|
||||||
@ -84,7 +88,7 @@ function multicastSequenceSubscriber() {
|
|||||||
|
|
||||||
// Return the subscriber function (runs when subscribe()
|
// Return the subscriber function (runs when subscribe()
|
||||||
// function is invoked)
|
// function is invoked)
|
||||||
return (observer) => {
|
return observer => {
|
||||||
observers.push(observer);
|
observers.push(observer);
|
||||||
// When this is the first subscription, start the sequence
|
// When this is the first subscription, start the sequence
|
||||||
if (observers.length === 1) {
|
if (observers.length === 1) {
|
||||||
@ -153,3 +157,4 @@ setTimeout(() => {
|
|||||||
// (at 3 seconds): 2nd sequence finished
|
// (at 3 seconds): 2nd sequence finished
|
||||||
|
|
||||||
// #enddocregion multicast_sequence
|
// #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,6 +1,7 @@
|
|||||||
|
// #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
|
// Create simple observable that emits three values
|
||||||
@ -15,6 +16,7 @@ const myObserver = {
|
|||||||
|
|
||||||
// Execute with the observer object
|
// Execute with the observer object
|
||||||
myObservable.subscribe(myObserver);
|
myObservable.subscribe(myObserver);
|
||||||
|
|
||||||
// Logs:
|
// Logs:
|
||||||
// Observer got a next value: 1
|
// Observer got a next value: 1
|
||||||
// Observer got a next value: 2
|
// Observer got a next value: 2
|
||||||
@ -30,3 +32,4 @@ myObservable.subscribe(
|
|||||||
() => console.log('Observer got a complete notification')
|
() => console.log('Observer got a complete notification')
|
||||||
);
|
);
|
||||||
// #enddocregion sub_fn
|
// #enddocregion sub_fn
|
||||||
|
}
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
"tests": [
|
"tests": [
|
||||||
{
|
{
|
||||||
"cmd": "yarn",
|
"cmd": "yarn",
|
||||||
"args": [ "tsc", "--project", "./tsconfig.app.json" ]
|
"args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": ["jasmine", "out-tsc/**/*.spec.js"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
import { interval } from 'rxjs';
|
||||||
|
import { tap } from 'rxjs/operators';
|
||||||
|
import { backoff } from './backoff';
|
||||||
|
|
||||||
|
describe('backoff()', () => {
|
||||||
|
beforeEach(() => jasmine.clock().install());
|
||||||
|
afterEach(() => jasmine.clock().uninstall());
|
||||||
|
|
||||||
|
it('should retry in case of error', () => {
|
||||||
|
const mockConsole = {log: jasmine.createSpy('log')};
|
||||||
|
const source = interval(10).pipe(
|
||||||
|
tap(i => {
|
||||||
|
if (i > 0) {
|
||||||
|
throw new Error('Test error');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
backoff(3, 100),
|
||||||
|
);
|
||||||
|
source.subscribe({
|
||||||
|
next: v => mockConsole.log(`Emitted: ${v}`),
|
||||||
|
error: e => mockConsole.log(`Errored: ${e.message || e}`),
|
||||||
|
complete: () => mockConsole.log('Completed'),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial try:
|
||||||
|
// Errors on second emission and schedules retrying (with delay).
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
mockConsole.log.calls.reset();
|
||||||
|
|
||||||
|
// First re-attempt after 100ms:
|
||||||
|
// Errors again on second emission and schedules retrying (with larger delay).
|
||||||
|
jasmine.clock().tick(100);
|
||||||
|
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
mockConsole.log.calls.reset();
|
||||||
|
|
||||||
|
// Second re-attempt after 400ms:
|
||||||
|
// Errors again on second emission and schedules retrying (with even larger delay).
|
||||||
|
jasmine.clock().tick(400);
|
||||||
|
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
mockConsole.log.calls.reset();
|
||||||
|
|
||||||
|
// Third re-attempt after 900ms:
|
||||||
|
// Errors again on second emission and gives up (no retrying).
|
||||||
|
jasmine.clock().tick(900);
|
||||||
|
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||||
|
mockConsole.log.calls.reset();
|
||||||
|
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([['Errored: Test error']]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,23 +1,32 @@
|
|||||||
|
// #docplaster
|
||||||
import { pipe, range, timer, zip } from 'rxjs';
|
// #docregion
|
||||||
|
import { of, pipe, range, throwError, timer, zip } from 'rxjs';
|
||||||
import { ajax } from 'rxjs/ajax';
|
import { ajax } from 'rxjs/ajax';
|
||||||
import { retryWhen, map, mergeMap } from 'rxjs/operators';
|
import { map, mergeMap, retryWhen } from 'rxjs/operators';
|
||||||
|
|
||||||
function backoff(maxTries, ms) {
|
export function backoff(maxTries, delay) {
|
||||||
return pipe(
|
return pipe(
|
||||||
retryWhen(attempts => zip(range(1, maxTries), attempts)
|
retryWhen(attempts =>
|
||||||
.pipe(
|
zip(range(1, maxTries + 1), attempts).pipe(
|
||||||
map(([i]) => i * i),
|
mergeMap(([i, err]) => (i > maxTries) ? throwError(err) : of(i)),
|
||||||
mergeMap(i => timer(i * ms))
|
map(i => i * i),
|
||||||
)
|
mergeMap(v => timer(v * delay)),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
/*
|
||||||
|
This function declaration is necessary to ensure that it does not get called
|
||||||
|
when running the unit tests. It will not get rendered into the docs.
|
||||||
|
The indentation needs to start in the leftmost level position as well because of how
|
||||||
|
the docplaster combines the different regions together.
|
||||||
|
*/
|
||||||
|
function docRegionAjaxCall() {
|
||||||
|
// #docregion
|
||||||
ajax('/api/endpoint')
|
ajax('/api/endpoint')
|
||||||
.pipe(backoff(3, 250))
|
.pipe(backoff(3, 250))
|
||||||
.subscribe(data => handleData(data));
|
.subscribe(function handleData(data) { /* ... */ });
|
||||||
|
// #enddocregion
|
||||||
function handleData(data) {
|
|
||||||
// ...
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
import { of } from 'rxjs';
|
||||||
|
import { docRegionTypeahead } from './typeahead';
|
||||||
|
|
||||||
|
describe('typeahead', () => {
|
||||||
|
let document;
|
||||||
|
let ajax;
|
||||||
|
let triggertInputChange;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jasmine.clock().install();
|
||||||
|
const input = {
|
||||||
|
addEventListener: jasmine
|
||||||
|
.createSpy('addEvent')
|
||||||
|
.and.callFake((eventName: string, cb: (e) => void) => {
|
||||||
|
if (eventName === 'input') {
|
||||||
|
triggertInputChange = cb;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
removeEventListener: jasmine.createSpy('removeEvent'),
|
||||||
|
};
|
||||||
|
|
||||||
|
document = { getElementById: (id: string) => input };
|
||||||
|
ajax = jasmine.createSpy('ajax').and.callFake((url: string) => of('foo bar'));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jasmine.clock().uninstall();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should make an ajax call to the corrent endpoint', () => {
|
||||||
|
docRegionTypeahead(document, ajax);
|
||||||
|
triggertInputChange({ target: { value: 'foo' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).toHaveBeenCalledWith('/api/endpoint?search=foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not make an ajax call, when the input length < 3', () => {
|
||||||
|
docRegionTypeahead(document, ajax);
|
||||||
|
triggertInputChange({ target: { value: '' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).not.toHaveBeenCalled();
|
||||||
|
triggertInputChange({ target: { value: 'fo' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not make an ajax call for intermediate values when debouncing', () => {
|
||||||
|
docRegionTypeahead(document, ajax);
|
||||||
|
triggertInputChange({ target: { value: 'foo' } });
|
||||||
|
jasmine.clock().tick(9);
|
||||||
|
triggertInputChange({ target: { value: 'bar' } });
|
||||||
|
jasmine.clock().tick(9);
|
||||||
|
triggertInputChange({ target: { value: 'baz' } });
|
||||||
|
jasmine.clock().tick(9);
|
||||||
|
triggertInputChange({ target: { value: 'qux' } });
|
||||||
|
expect(ajax).not.toHaveBeenCalled();
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(ajax).toHaveBeenCalledTimes(1);
|
||||||
|
expect(ajax).toHaveBeenCalledWith('/api/endpoint?search=qux');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not make an ajax call, when the input value has not changed', () => {
|
||||||
|
docRegionTypeahead(document, ajax);
|
||||||
|
triggertInputChange({ target: { value: 'foo' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).toHaveBeenCalled();
|
||||||
|
ajax.calls.reset();
|
||||||
|
triggertInputChange({ target: { value: 'foo' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -1,8 +1,18 @@
|
|||||||
|
/*
|
||||||
|
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 { fromEvent } from 'rxjs';
|
||||||
import { ajax } from 'rxjs/ajax';
|
import { ajax } from 'rxjs/ajax';
|
||||||
import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';
|
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(
|
const typeahead = fromEvent(searchBox, 'input').pipe(
|
||||||
@ -10,9 +20,13 @@ const typeahead = fromEvent(searchBox, 'input').pipe(
|
|||||||
filter(text => text.length > 2),
|
filter(text => text.length > 2),
|
||||||
debounceTime(10),
|
debounceTime(10),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
switchMap(() => ajax('/api/endpoint'))
|
switchMap(searchTerm => ajax(`/api/endpoint?search=${searchTerm}`))
|
||||||
);
|
);
|
||||||
|
|
||||||
typeahead.subscribe(data => {
|
typeahead.subscribe(data => {
|
||||||
// Handle the data from the API
|
// Handle the data from the API
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
return typeahead;
|
||||||
|
}
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
"tests": [
|
"tests": [
|
||||||
{
|
{
|
||||||
"cmd": "yarn",
|
"cmd": "yarn",
|
||||||
"args": [ "tsc", "--project", "./tsconfig.app.json" ]
|
"args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": ["jasmine", "out-tsc/**/*.spec.js"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
46
aio/content/examples/rx-library/src/error-handling.spec.ts
Normal file
46
aio/content/examples/rx-library/src/error-handling.spec.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { Subject, throwError } from 'rxjs';
|
||||||
|
import { docRegionDefault } from './error-handling';
|
||||||
|
|
||||||
|
describe('error-handling', () => {
|
||||||
|
let mockConsole;
|
||||||
|
let ajaxSubject;
|
||||||
|
let ajax;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockConsole = {log: jasmine.createSpy('log')};
|
||||||
|
ajaxSubject = new Subject();
|
||||||
|
ajax = jasmine
|
||||||
|
.createSpy('ajax')
|
||||||
|
.and.callFake((url: string) => ajaxSubject);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => ajaxSubject.unsubscribe());
|
||||||
|
|
||||||
|
it('should return the response object', () => {
|
||||||
|
docRegionDefault(mockConsole, ajax);
|
||||||
|
|
||||||
|
ajaxSubject.next({response: {foo: 'bar'}});
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||||
|
['data: ', {foo: 'bar'}]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty array when using an object without a `response` property', () => {
|
||||||
|
docRegionDefault(mockConsole, ajax);
|
||||||
|
|
||||||
|
ajaxSubject.next({foo: 'bar'});
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||||
|
['data: ', []]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty array when the ajax observable errors', () => {
|
||||||
|
ajax.and.returnValue(throwError('Test Error'));
|
||||||
|
|
||||||
|
docRegionDefault(mockConsole, ajax);
|
||||||
|
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||||
|
['data: ', []]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,14 +1,23 @@
|
|||||||
|
// #docplaster
|
||||||
import { of } from 'rxjs';
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:no-shadowed-variable */
|
||||||
|
/* tslint:disable:align */
|
||||||
// #docregion
|
// #docregion
|
||||||
|
import { of } from 'rxjs';
|
||||||
import { ajax } from 'rxjs/ajax';
|
import { ajax } from 'rxjs/ajax';
|
||||||
import { map, catchError } from 'rxjs/operators';
|
import { map, catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
|
||||||
|
export function docRegionDefault(console, ajax) {
|
||||||
|
// #docregion
|
||||||
// Return "response" from the API. If an error happens,
|
// Return "response" from the API. If an error happens,
|
||||||
// return an empty array.
|
// return an empty array.
|
||||||
const apiData = ajax('/api/data').pipe(
|
const apiData = ajax('/api/data').pipe(
|
||||||
map(res => {
|
map((res: any) => {
|
||||||
if (!res.response) {
|
if (!res.response) {
|
||||||
throw new Error('Value expected!');
|
throw new Error('Value expected!');
|
||||||
}
|
}
|
||||||
@ -23,3 +32,5 @@ apiData.subscribe({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// #enddocregion
|
// #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,9 +1,17 @@
|
|||||||
import { of, pipe } from 'rxjs';
|
// #docplaster
|
||||||
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:align */
|
||||||
// #docregion
|
// #docregion
|
||||||
|
import { of, pipe } from 'rxjs';
|
||||||
import { filter, map } from 'rxjs/operators';
|
import { filter, map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
|
||||||
|
export function docRegionDefault(console) {
|
||||||
|
// #docregion
|
||||||
const nums = of(1, 2, 3, 4, 5);
|
const nums = of(1, 2, 3, 4, 5);
|
||||||
|
|
||||||
// Create a function that accepts an Observable.
|
// Create a function that accepts an Observable.
|
||||||
@ -19,5 +27,4 @@ const squareOdd = squareOddVals(nums);
|
|||||||
squareOdd.subscribe(x => console.log(x));
|
squareOdd.subscribe(x => console.log(x));
|
||||||
|
|
||||||
// #enddocregion
|
// #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,9 +1,17 @@
|
|||||||
import { Observable, of } from 'rxjs';
|
// #docplaster
|
||||||
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:align */
|
||||||
// #docregion
|
// #docregion
|
||||||
|
import { of } from 'rxjs';
|
||||||
import { filter, map } from 'rxjs/operators';
|
import { filter, map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
|
||||||
|
export function docRegionDefault(console) {
|
||||||
|
// #docregion
|
||||||
const squareOdd = of(1, 2, 3, 4, 5)
|
const squareOdd = of(1, 2, 3, 4, 5)
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(n => n % 2 !== 0),
|
filter(n => n % 2 !== 0),
|
||||||
@ -14,3 +22,4 @@ const squareOdd = of(1, 2, 3, 4, 5)
|
|||||||
squareOdd.subscribe(x => console.log(x));
|
squareOdd.subscribe(x => console.log(x));
|
||||||
|
|
||||||
// #enddocregion
|
// #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,10 +1,17 @@
|
|||||||
|
// #docplaster
|
||||||
import { Observable, of } from 'rxjs';
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:align */
|
||||||
// #docregion
|
// #docregion
|
||||||
|
import { of } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
|
||||||
|
export function docRegionDefault(console) {
|
||||||
|
// #docregion
|
||||||
const nums = of(1, 2, 3);
|
const nums = of(1, 2, 3);
|
||||||
|
|
||||||
const squareValues = map((val: number) => val * val);
|
const squareValues = map((val: number) => val * val);
|
||||||
@ -18,3 +25,4 @@ squaredNums.subscribe(x => console.log(x));
|
|||||||
// 9
|
// 9
|
||||||
|
|
||||||
// #enddocregion
|
// #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,20 +1,28 @@
|
|||||||
|
// #docplaster
|
||||||
import { Observable, of } from 'rxjs';
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:no-shadowed-variable */
|
||||||
|
/* tslint:disable:align */
|
||||||
// #docregion
|
// #docregion
|
||||||
|
import { of } from 'rxjs';
|
||||||
import { ajax } from 'rxjs/ajax';
|
import { ajax } from 'rxjs/ajax';
|
||||||
import { map, retry, catchError } from 'rxjs/operators';
|
import { map, retry, catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
|
||||||
|
export function docRegionDefault(console, ajax) {
|
||||||
|
// #docregion
|
||||||
const apiData = ajax('/api/data').pipe(
|
const apiData = ajax('/api/data').pipe(
|
||||||
retry(3), // Retry up to 3 times before failing
|
map((res: any) => {
|
||||||
map(res => {
|
|
||||||
if (!res.response) {
|
if (!res.response) {
|
||||||
|
console.log('Error occured.');
|
||||||
throw new Error('Value expected!');
|
throw new Error('Value expected!');
|
||||||
}
|
}
|
||||||
return res.response;
|
return res.response;
|
||||||
}),
|
}),
|
||||||
|
retry(3), // Retry up to 3 times before failing
|
||||||
catchError(err => of([]))
|
catchError(err => of([]))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -24,3 +32,4 @@ apiData.subscribe({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
import { of } from 'rxjs';
|
||||||
|
import { docRegionPromise } from './simple-creation.1';
|
||||||
|
|
||||||
|
describe('simple-creation.1', () => {
|
||||||
|
it('should create a promise from an observable and return an empty object', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
const fetch = () => of({foo: 42});
|
||||||
|
docRegionPromise(console, fetch);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
[{foo: 42}],
|
||||||
|
['Completed'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
24
aio/content/examples/rx-library/src/simple-creation.1.ts
Normal file
24
aio/content/examples/rx-library/src/simple-creation.1.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// #docplaster
|
||||||
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:align */
|
||||||
|
// #docregion promise
|
||||||
|
import { from } from 'rxjs';
|
||||||
|
|
||||||
|
// #enddocregion promise
|
||||||
|
|
||||||
|
export function docRegionPromise(console, fetch) {
|
||||||
|
// #docregion promise
|
||||||
|
// Create an Observable out of a promise
|
||||||
|
const data = from(fetch('/api/endpoint'));
|
||||||
|
// Subscribe to begin listening for async result
|
||||||
|
data.subscribe({
|
||||||
|
next(response) { console.log(response); },
|
||||||
|
error(err) { console.error('Error: ' + err); },
|
||||||
|
complete() { console.log('Completed'); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// #enddocregion promise
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import { docRegionInterval } from './simple-creation.2';
|
||||||
|
|
||||||
|
describe('simple-creation.2', () => {
|
||||||
|
beforeEach(() => jasmine.clock().install());
|
||||||
|
afterEach(() => jasmine.clock().uninstall());
|
||||||
|
|
||||||
|
it('should create an Observable that will publish a value on an interval', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
const subscription = docRegionInterval(console);
|
||||||
|
jasmine.clock().tick(1000);
|
||||||
|
expect(console.log).toHaveBeenCalledWith('It\'s been 1 seconds since subscribing!');
|
||||||
|
console.log.calls.reset();
|
||||||
|
|
||||||
|
jasmine.clock().tick(999);
|
||||||
|
expect(console.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
jasmine.clock().tick(1);
|
||||||
|
expect(console.log).toHaveBeenCalledWith('It\'s been 2 seconds since subscribing!');
|
||||||
|
subscription.unsubscribe();
|
||||||
|
});
|
||||||
|
});
|
22
aio/content/examples/rx-library/src/simple-creation.2.ts
Normal file
22
aio/content/examples/rx-library/src/simple-creation.2.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// #docplaster
|
||||||
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:align */
|
||||||
|
// #docregion interval
|
||||||
|
import { interval } from 'rxjs';
|
||||||
|
|
||||||
|
// #enddocregion interval
|
||||||
|
|
||||||
|
export function docRegionInterval(console) {
|
||||||
|
// #docregion interval
|
||||||
|
// Create an Observable that will publish a value on an interval
|
||||||
|
const secondsCounter = interval(1000);
|
||||||
|
// Subscribe to begin publishing values
|
||||||
|
const subscription = secondsCounter.subscribe(n =>
|
||||||
|
console.log(`It's been ${n + 1} seconds since subscribing!`));
|
||||||
|
|
||||||
|
// #enddocregion interval
|
||||||
|
return subscription;
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
import { docRegionEvent } from './simple-creation.3';
|
||||||
|
|
||||||
|
describe('simple-creation.3', () => {
|
||||||
|
let triggerMousemove;
|
||||||
|
let mockConsole;
|
||||||
|
let input;
|
||||||
|
let mockDocument;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockConsole = {log: jasmine.createSpy('log')};
|
||||||
|
input = {
|
||||||
|
addEventListener: jasmine
|
||||||
|
.createSpy('addEventListener')
|
||||||
|
.and.callFake((eventName, cb) => {
|
||||||
|
if (eventName === 'mousemove') {
|
||||||
|
triggerMousemove = cb;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
removeEventListener: jasmine.createSpy('removeEventListener'),
|
||||||
|
};
|
||||||
|
mockDocument = { getElementById: () => input };
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log coords when subscribing', () => {
|
||||||
|
docRegionEvent(mockConsole, mockDocument);
|
||||||
|
|
||||||
|
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
triggerMousemove({ clientX: 50, clientY: 50 });
|
||||||
|
triggerMousemove({ clientX: 30, clientY: 50 });
|
||||||
|
triggerMousemove({ clientX: 50, clientY: 30 });
|
||||||
|
expect(mockConsole.log).toHaveBeenCalledTimes(3);
|
||||||
|
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||||
|
['Coords: 50 X 50'],
|
||||||
|
['Coords: 30 X 50'],
|
||||||
|
['Coords: 50 X 30']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call unsubscribe when clientX and clientY are below < 40 ', () => {
|
||||||
|
docRegionEvent(mockConsole, mockDocument);
|
||||||
|
|
||||||
|
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Ensure that we have unsubscribed.
|
||||||
|
triggerMousemove({ clientX: 30, clientY: 30 });
|
||||||
|
expect(input.removeEventListener).toHaveBeenCalledWith('mousemove', triggerMousemove, undefined);
|
||||||
|
mockConsole.log.calls.reset();
|
||||||
|
|
||||||
|
triggerMousemove({ clientX: 50, clientY: 50 });
|
||||||
|
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
32
aio/content/examples/rx-library/src/simple-creation.3.ts
Normal file
32
aio/content/examples/rx-library/src/simple-creation.3.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// #docplaster
|
||||||
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
/* tslint:disable:align */
|
||||||
|
// #docregion event
|
||||||
|
import { fromEvent } from 'rxjs';
|
||||||
|
|
||||||
|
// #enddocregion event
|
||||||
|
|
||||||
|
export function docRegionEvent(console, document) {
|
||||||
|
// #docregion event
|
||||||
|
const el = document.getElementById('my-element');
|
||||||
|
|
||||||
|
// Create an Observable that will publish mouse movements
|
||||||
|
const mouseMoves = fromEvent(el, 'mousemove');
|
||||||
|
|
||||||
|
// Subscribe to start listening for mouse-move events
|
||||||
|
const subscription = mouseMoves.subscribe((evt: MouseEvent) => {
|
||||||
|
// Log coords of mouse movements
|
||||||
|
console.log(`Coords: ${evt.clientX} X ${evt.clientY}`);
|
||||||
|
|
||||||
|
// When the mouse is over the upper-left of the screen,
|
||||||
|
// unsubscribe to stop listening for mouse movements
|
||||||
|
if (evt.clientX < 40 && evt.clientY < 40) {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// #enddocregion event
|
||||||
|
}
|
14
aio/content/examples/rx-library/src/simple-creation.spec.ts
Normal file
14
aio/content/examples/rx-library/src/simple-creation.spec.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { of } from 'rxjs';
|
||||||
|
import { docRegionAjax } from './simple-creation';
|
||||||
|
|
||||||
|
describe('ajax', () => {
|
||||||
|
it('should make a request and console log the status and response', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
const ajax = jasmine.createSpy('ajax').and.callFake((url: string) => {
|
||||||
|
return of({status: 200, response: 'foo bar'});
|
||||||
|
});
|
||||||
|
|
||||||
|
docRegionAjax(console, ajax);
|
||||||
|
expect(console.log).toHaveBeenCalledWith(200, 'foo bar');
|
||||||
|
});
|
||||||
|
});
|
@ -1,65 +1,19 @@
|
|||||||
|
// #docplaster
|
||||||
// #docregion promise
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
import { from } from 'rxjs';
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
// Create an Observable out of a promise
|
/* tslint:disable:no-shadowed-variable */
|
||||||
const data = from(fetch('/api/endpoint'));
|
/* tslint:disable:align */
|
||||||
// Subscribe to begin listening for async result
|
|
||||||
data.subscribe({
|
|
||||||
next(response) { console.log(response); },
|
|
||||||
error(err) { console.error('Error: ' + err); },
|
|
||||||
complete() { console.log('Completed'); }
|
|
||||||
});
|
|
||||||
|
|
||||||
// #enddocregion promise
|
|
||||||
|
|
||||||
// #docregion interval
|
|
||||||
|
|
||||||
import { interval } from 'rxjs';
|
|
||||||
|
|
||||||
// Create an Observable that will publish a value on an interval
|
|
||||||
const secondsCounter = interval(1000);
|
|
||||||
// Subscribe to begin publishing values
|
|
||||||
secondsCounter.subscribe(n =>
|
|
||||||
console.log(`It's been ${n} seconds since subscribing!`));
|
|
||||||
|
|
||||||
// #enddocregion interval
|
|
||||||
|
|
||||||
|
|
||||||
// #docregion event
|
|
||||||
|
|
||||||
import { fromEvent } from 'rxjs';
|
|
||||||
|
|
||||||
const el = document.getElementById('my-element');
|
|
||||||
|
|
||||||
// Create an Observable that will publish mouse movements
|
|
||||||
const mouseMoves = fromEvent(el, 'mousemove');
|
|
||||||
|
|
||||||
// Subscribe to start listening for mouse-move events
|
|
||||||
const subscription = mouseMoves.subscribe((evt: MouseEvent) => {
|
|
||||||
// Log coords of mouse movements
|
|
||||||
console.log(`Coords: ${evt.clientX} X ${evt.clientY}`);
|
|
||||||
|
|
||||||
// When the mouse is over the upper-left of the screen,
|
|
||||||
// unsubscribe to stop listening for mouse movements
|
|
||||||
if (evt.clientX < 40 && evt.clientY < 40) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// #enddocregion event
|
|
||||||
|
|
||||||
|
|
||||||
// #docregion ajax
|
// #docregion ajax
|
||||||
|
|
||||||
import { ajax } from 'rxjs/ajax';
|
import { ajax } from 'rxjs/ajax';
|
||||||
|
|
||||||
// Create an Observable that will create an AJAX request
|
// Create an Observable that will create an AJAX request
|
||||||
|
// #enddocregion ajax
|
||||||
|
export function docRegionAjax(console, ajax) {
|
||||||
|
// #docregion ajax
|
||||||
const apiData = ajax('/api/data');
|
const apiData = ajax('/api/data');
|
||||||
// Subscribe to create the request
|
// Subscribe to create the request
|
||||||
apiData.subscribe(res => console.log(res.status, res.response));
|
apiData.subscribe(res => console.log(res.status, res.response));
|
||||||
|
|
||||||
// #enddocregion ajax
|
// #enddocregion ajax
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { SwUpdate } from '@angular/service-worker';
|
||||||
|
|
||||||
|
function notifyUser(message: string): void { }
|
||||||
|
|
||||||
|
// #docregion sw-unrecoverable-state
|
||||||
|
@Injectable()
|
||||||
|
export class HandleUnrecoverableStateService {
|
||||||
|
constructor(updates: SwUpdate) {
|
||||||
|
updates.unrecoverable.subscribe(event => {
|
||||||
|
notifyUser(
|
||||||
|
`An error occurred that we cannot recover from:\n${event.reason}\n\n` +
|
||||||
|
'Please reload the page.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #enddocregion sw-unrecoverable-state
|
@ -28,7 +28,7 @@ import {
|
|||||||
ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync
|
ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync
|
||||||
} from '@angular/core/testing';
|
} from '@angular/core/testing';
|
||||||
|
|
||||||
import { addMatchers, newEvent, click } from '../../testing';
|
import { addMatchers, click } from '../../testing';
|
||||||
|
|
||||||
export class NotProvided extends ValueService { /* example below */ }
|
export class NotProvided extends ValueService { /* example below */ }
|
||||||
beforeEach(addMatchers);
|
beforeEach(addMatchers);
|
||||||
@ -274,8 +274,10 @@ describe('demo (with TestBed):', () => {
|
|||||||
expect(comp.name).toBe(expectedOrigName,
|
expect(comp.name).toBe(expectedOrigName,
|
||||||
`comp.name should still be ${expectedOrigName} after value change, before binding happens`);
|
`comp.name should still be ${expectedOrigName} after value change, before binding happens`);
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular learns of input value change.
|
// Dispatch a DOM event so that Angular learns of input value change.
|
||||||
// then wait while ngModel pushes input.box value to comp.name
|
// then wait while ngModel pushes input.box value to comp.name
|
||||||
|
// 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'));
|
input.dispatchEvent(new Event('input'));
|
||||||
return fixture.whenStable();
|
return fixture.whenStable();
|
||||||
})
|
})
|
||||||
@ -312,8 +314,10 @@ describe('demo (with TestBed):', () => {
|
|||||||
expect(comp.name).toBe(expectedOrigName,
|
expect(comp.name).toBe(expectedOrigName,
|
||||||
`comp.name should still be ${expectedOrigName} after value change, before binding happens`);
|
`comp.name should still be ${expectedOrigName} after value change, before binding happens`);
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular learns of input value change.
|
// Dispatch a DOM event so that Angular learns of input value change.
|
||||||
// then wait a tick while ngModel pushes input.box value to comp.name
|
// then wait a tick while ngModel pushes input.box value to comp.name
|
||||||
|
// 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'));
|
input.dispatchEvent(new Event('input'));
|
||||||
tick();
|
tick();
|
||||||
expect(comp.name).toBe(expectedNewName,
|
expect(comp.name).toBe(expectedNewName,
|
||||||
@ -335,9 +339,11 @@ describe('demo (with TestBed):', () => {
|
|||||||
// simulate user entering new name in input
|
// simulate user entering new name in input
|
||||||
input.value = inputText;
|
input.value = inputText;
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular learns of input value change.
|
// Dispatch a DOM event so that Angular learns of input value change.
|
||||||
// then wait a tick while ngModel pushes input.box value to comp.text
|
// then wait a tick while ngModel pushes input.box value to comp.text
|
||||||
// and Angular updates the output span
|
// and Angular updates the output span
|
||||||
|
// 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'));
|
input.dispatchEvent(new Event('input'));
|
||||||
tick();
|
tick();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -3,7 +3,7 @@ import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ActivatedRoute, ActivatedRouteStub, asyncData, click, newEvent
|
ActivatedRoute, ActivatedRouteStub, asyncData, click
|
||||||
} from '../../testing';
|
} from '../../testing';
|
||||||
|
|
||||||
import { Hero } from '../model/hero';
|
import { Hero } from '../model/hero';
|
||||||
@ -99,6 +99,9 @@ function overrideSetup() {
|
|||||||
const newName = 'New Name';
|
const newName = 'New Name';
|
||||||
|
|
||||||
page.nameInput.value = newName;
|
page.nameInput.value = newName;
|
||||||
|
|
||||||
|
// 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
|
page.nameInput.dispatchEvent(new Event('input')); // tell Angular
|
||||||
|
|
||||||
expect(component.hero.name).toBe(newName, 'component hero has new name');
|
expect(component.hero.name).toBe(newName, 'component hero has new name');
|
||||||
@ -197,8 +200,9 @@ function heroModuleSetup() {
|
|||||||
// simulate user entering a new name into the input box
|
// simulate user entering a new name into the input box
|
||||||
nameInput.value = 'quick BROWN fOx';
|
nameInput.value = 'quick BROWN fOx';
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular learns of input value change.
|
// Dispatch a DOM event so that Angular learns of input value change.
|
||||||
// use newEvent utility function (not provided by Angular) for better browser compatibility
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
nameInput.dispatchEvent(new Event('input'));
|
nameInput.dispatchEvent(new Event('input'));
|
||||||
|
|
||||||
// Tell Angular to update the display binding through the title pipe
|
// Tell Angular to update the display binding through the title pipe
|
||||||
|
@ -6,7 +6,7 @@ import { DebugElement } from '@angular/core';
|
|||||||
|
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { addMatchers, newEvent } from '../../testing';
|
import { addMatchers } from '../../testing';
|
||||||
import { HeroService } from '../model/hero.service';
|
import { HeroService } from '../model/hero.service';
|
||||||
import { getTestHeroes, TestHeroService } from '../model/testing/test-hero.service';
|
import { getTestHeroes, TestHeroService } from '../model/testing/test-hero.service';
|
||||||
|
|
||||||
@ -53,6 +53,9 @@ describe('HeroListComponent', () => {
|
|||||||
it('should select hero on click', fakeAsync(() => {
|
it('should select hero on click', fakeAsync(() => {
|
||||||
const expectedHero = HEROES[1];
|
const expectedHero = HEROES[1];
|
||||||
const li = page.heroRows[1];
|
const li = page.heroRows[1];
|
||||||
|
|
||||||
|
// 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'));
|
li.dispatchEvent(new Event('click'));
|
||||||
tick();
|
tick();
|
||||||
// `.toEqual` because selectedHero is clone of expectedHero; see FakeHeroService
|
// `.toEqual` because selectedHero is clone of expectedHero; see FakeHeroService
|
||||||
@ -62,6 +65,9 @@ describe('HeroListComponent', () => {
|
|||||||
it('should navigate to selected hero detail on click', fakeAsync(() => {
|
it('should navigate to selected hero detail on click', fakeAsync(() => {
|
||||||
const expectedHero = HEROES[1];
|
const expectedHero = HEROES[1];
|
||||||
const li = page.heroRows[1];
|
const li = page.heroRows[1];
|
||||||
|
|
||||||
|
// 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'));
|
li.dispatchEvent(new Event('click'));
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { HighlightDirective } from './highlight.directive';
|
import { HighlightDirective } from './highlight.directive';
|
||||||
import { newEvent } from '../../testing';
|
|
||||||
|
|
||||||
// #docregion test-component
|
// #docregion test-component
|
||||||
@Component({
|
@Component({
|
||||||
@ -59,8 +58,11 @@ describe('HighlightDirective', () => {
|
|||||||
const input = des[2].nativeElement as HTMLInputElement;
|
const input = des[2].nativeElement as HTMLInputElement;
|
||||||
expect(input.style.backgroundColor).toBe('cyan', 'initial backgroundColor');
|
expect(input.style.backgroundColor).toBe('cyan', 'initial backgroundColor');
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular responds to the input value change.
|
|
||||||
input.value = 'green';
|
input.value = 'green';
|
||||||
|
|
||||||
|
// 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'));
|
input.dispatchEvent(new Event('input'));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
@ -14,18 +14,6 @@ export function advance(f: ComponentFixture<any>): void {
|
|||||||
f.detectChanges();
|
f.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create custom DOM event the old fashioned way
|
|
||||||
*
|
|
||||||
* https://developer.mozilla.org/en-US/docs/Web/API/Event/initEvent
|
|
||||||
* Although officially deprecated, some browsers (phantom) don't accept the preferred "new Event(eventName)"
|
|
||||||
*/
|
|
||||||
export function newEvent(eventName: string, bubbles = false, cancelable = false) {
|
|
||||||
const evt = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
|
|
||||||
evt.initCustomEvent(eventName, bubbles, cancelable, null);
|
|
||||||
return evt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
||||||
// #docregion click-event
|
// #docregion click-event
|
||||||
/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */
|
/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "tslint:recommended",
|
"extends": "tslint:recommended",
|
||||||
|
"rulesDirectory": [
|
||||||
|
"codelyzer"
|
||||||
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"align": {
|
"align": {
|
||||||
"options": [
|
"options": [
|
||||||
@ -13,22 +16,6 @@
|
|||||||
"deprecation": {
|
"deprecation": {
|
||||||
"severity": "warning"
|
"severity": "warning"
|
||||||
},
|
},
|
||||||
"component-class-suffix": true,
|
|
||||||
"component-selector": [
|
|
||||||
true,
|
|
||||||
"element",
|
|
||||||
// TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense).
|
|
||||||
"",
|
|
||||||
"kebab-case"
|
|
||||||
],
|
|
||||||
"contextual-lifecycle": true,
|
|
||||||
"directive-class-suffix": true,
|
|
||||||
"directive-selector": [
|
|
||||||
true,
|
|
||||||
"attribute",
|
|
||||||
["app", "toh"],
|
|
||||||
"camelCase"
|
|
||||||
],
|
|
||||||
"eofline": true,
|
"eofline": true,
|
||||||
"import-blacklist": [
|
"import-blacklist": [
|
||||||
true,
|
true,
|
||||||
@ -56,6 +43,8 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
// TODO(gkalpak): Fix the code and enable this.
|
||||||
|
// "no-any": true,
|
||||||
"no-console": [
|
"no-console": [
|
||||||
true,
|
true,
|
||||||
"debug",
|
"debug",
|
||||||
@ -95,6 +84,11 @@
|
|||||||
"named": "never"
|
"named": "never"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// TODO(gkalpak): Fix the code and enable this.
|
||||||
|
// "typedef": [
|
||||||
|
// true,
|
||||||
|
// "call-signature"
|
||||||
|
// ],
|
||||||
"typedef-whitespace": {
|
"typedef-whitespace": {
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
@ -130,6 +124,9 @@
|
|||||||
"check-typecast"
|
"check-typecast"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"component-class-suffix": true,
|
||||||
|
"contextual-lifecycle": true,
|
||||||
|
"directive-class-suffix": true,
|
||||||
"no-conflicting-lifecycle": true,
|
"no-conflicting-lifecycle": true,
|
||||||
"no-host-metadata-property": true,
|
"no-host-metadata-property": true,
|
||||||
"no-input-rename": true,
|
"no-input-rename": true,
|
||||||
@ -141,9 +138,19 @@
|
|||||||
"template-banana-in-box": true,
|
"template-banana-in-box": true,
|
||||||
"template-no-negated-async": true,
|
"template-no-negated-async": true,
|
||||||
"use-lifecycle-interface": true,
|
"use-lifecycle-interface": true,
|
||||||
"use-pipe-transform-interface": true
|
"use-pipe-transform-interface": true,
|
||||||
},
|
"directive-selector": [
|
||||||
"rulesDirectory": [
|
true,
|
||||||
"codelyzer"
|
"attribute",
|
||||||
|
["app", "toh"],
|
||||||
|
"camelCase"
|
||||||
|
],
|
||||||
|
"component-selector": [
|
||||||
|
true,
|
||||||
|
"element",
|
||||||
|
// TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense).
|
||||||
|
"",
|
||||||
|
"kebab-case"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import * as angular from 'angular';
|
import * as angular from 'angular';
|
||||||
import 'angular-route';
|
import 'angular-route';
|
||||||
|
|
||||||
const appName = 'myApp';
|
const appModule = angular.module('myApp', [
|
||||||
|
|
||||||
angular.module(appName, [
|
|
||||||
'ngRoute'
|
'ngRoute'
|
||||||
])
|
])
|
||||||
.config(['$routeProvider', '$locationProvider',
|
.config(['$routeProvider', '$locationProvider',
|
||||||
@ -25,5 +23,5 @@ angular.module(appName, [
|
|||||||
);
|
);
|
||||||
|
|
||||||
export function bootstrap(el: HTMLElement) {
|
export function bootstrap(el: HTMLElement) {
|
||||||
return angular.bootstrap(el, [appName]);
|
return angular.bootstrap(el, [appModule.name]);
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ or in the `@NgModule()` or `@Component()` metadata
|
|||||||
Registering the provider in the `@Injectable()` metadata also allows Angular to optimize an app
|
Registering the provider in the `@Injectable()` metadata also allows Angular to optimize an app
|
||||||
by removing the service from the compiled app if it isn't used.
|
by removing the service from the compiled app if it isn't used.
|
||||||
|
|
||||||
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator,
|
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator.
|
||||||
|
|
||||||
```
|
```
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -18,8 +18,6 @@ When you use the [Angular CLI](cli) command `ng new` to generate an app, the def
|
|||||||
/* JavaScript imports */
|
/* JavaScript imports */
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
@ -29,9 +27,7 @@ import { AppComponent } from './app.component';
|
|||||||
AppComponent
|
AppComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule
|
||||||
FormsModule,
|
|
||||||
HttpClientModule
|
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
@ -120,9 +116,6 @@ Now you could use your `ItemDirective` in a component. This example uses `AppMod
|
|||||||
|
|
||||||
Remember, components, directives, and pipes belong to one module only. You only need to declare them once in your app because you share them by importing the necessary modules. This saves you time and helps keep your app lean.
|
Remember, components, directives, and pipes belong to one module only. You only need to declare them once in your app because you share them by importing the necessary modules. This saves you time and helps keep your app lean.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a imports}
|
{@a imports}
|
||||||
|
|
||||||
## The `imports` array
|
## The `imports` array
|
||||||
@ -130,6 +123,12 @@ Remember, components, directives, and pipes belong to one module only. You only
|
|||||||
The module's `imports` array appears exclusively in the `@NgModule` metadata object.
|
The module's `imports` array appears exclusively in the `@NgModule` metadata object.
|
||||||
It tells Angular about other NgModules that this particular module needs to function properly.
|
It tells Angular about other NgModules that this particular module needs to function properly.
|
||||||
|
|
||||||
|
<code-example
|
||||||
|
path="bootstrapping/src/app/app.module.ts"
|
||||||
|
region="imports"
|
||||||
|
header="src/app/app.module.ts (excerpt)">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
This list of modules are those that export components, directives, or pipes
|
This list of modules are those that export components, directives, or pipes
|
||||||
that component templates in this module reference. In this case, the component is
|
that component templates in this module reference. In this case, the component is
|
||||||
`AppComponent`, which references components, directives, or pipes in `BrowserModule`,
|
`AppComponent`, which references components, directives, or pipes in `BrowserModule`,
|
||||||
@ -138,6 +137,8 @@ A component template can reference another component, directive,
|
|||||||
or pipe when the referenced class is declared in this module or
|
or pipe when the referenced class is declared in this module or
|
||||||
the class was imported from another module.
|
the class was imported from another module.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a bootstrap-array}
|
{@a bootstrap-array}
|
||||||
|
|
||||||
## The `providers` array
|
## The `providers` array
|
||||||
|
@ -53,18 +53,7 @@ Angular supports most recent browsers. This includes the following specific vers
|
|||||||
IE
|
IE
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div> 11, 10*, 9* ("compatibility view" mode not supported) </div>
|
<div>11</div>
|
||||||
<div>*deprecated in v10, see the {@link guide/deprecations#ie-9-10-and-mobile deprecations guide}.</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
IE Mobile*
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
11
|
|
||||||
<div>*deprecated in v10, see the {@link guide/deprecations#ie-9-10-and-mobile deprecations guide}.</div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -185,7 +174,7 @@ These are the polyfills required to run an Angular application on each supported
|
|||||||
|
|
||||||
<td>
|
<td>
|
||||||
Chrome, Firefox, Edge, <br>
|
Chrome, Firefox, Edge, <br>
|
||||||
Safari, Android, IE 10+
|
Safari, Android, IE 11
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
@ -196,20 +185,6 @@ These are the polyfills required to run an Angular application on each supported
|
|||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr style="vertical-align: top">
|
|
||||||
|
|
||||||
<td>
|
|
||||||
IE 9
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
ES2015<br>[classList](guide/browser-support#classlist)
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
@ -272,30 +247,7 @@ Some features of Angular may require additional polyfills.
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
IE 10, IE 11
|
IE 11
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr style="vertical-align: top">
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
[Http](guide/http) when sending and receiving binary data
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
[Typed Array](guide/browser-support#typedarray)<br>
|
|
||||||
|
|
||||||
[Blob](guide/browser-support#blob)<br>
|
|
||||||
|
|
||||||
[FormData](guide/browser-support#formdata)
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
IE 9
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
@ -437,60 +389,6 @@ The following polyfills are used to test the framework itself. They are a good s
|
|||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
<a id='typedarray' href="https://github.com/inexorabletash/polyfill/blob/master/typedarray.js">Typed Array</a>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
MIT
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
4KB
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
<a id='blob' href="https://github.com/eligrey/Blob.js">Blob</a>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
MIT
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
1.3KB
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
|
|
||||||
<a id='formdata' href="https://github.com/francois2metz/html5-formdata">FormData</a>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
MIT
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
0.4KB
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
@ -387,7 +387,7 @@ List the generated bundles in the `dist/` folder.
|
|||||||
|
|
||||||
<code-example language="none" class="code-shell">
|
<code-example language="none" class="code-shell">
|
||||||
|
|
||||||
ls dist/*.bundle.js
|
ls dist/*.js
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
@ -396,7 +396,7 @@ The following example displays the graph for the _main_ bundle.
|
|||||||
|
|
||||||
<code-example language="none" class="code-shell">
|
<code-example language="none" class="code-shell">
|
||||||
|
|
||||||
node_modules/.bin/source-map-explorer dist/main.*.bundle.js
|
node_modules/.bin/source-map-explorer dist/main*
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
@ -38,7 +38,6 @@ v9 - v12
|
|||||||
| `@angular/bazel` | [`Bazel builder and schematics`](#bazelbuilder) | v10 |
|
| `@angular/bazel` | [`Bazel builder and schematics`](#bazelbuilder) | v10 |
|
||||||
| `@angular/common` | [`ReflectiveInjector`](#reflectiveinjector) | <!--v8--> v11 |
|
| `@angular/common` | [`ReflectiveInjector`](#reflectiveinjector) | <!--v8--> v11 |
|
||||||
| `@angular/common` | [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation) | <!--v9--> v11 |
|
| `@angular/common` | [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation) | <!--v9--> v11 |
|
||||||
| `@angular/core` | [`CollectionChangeRecord`](#core) | <!--v7--> v11 |
|
|
||||||
| `@angular/core` | [`DefaultIterableDiffer`](#core) | <!--v7--> v11 |
|
| `@angular/core` | [`DefaultIterableDiffer`](#core) | <!--v7--> v11 |
|
||||||
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v11 |
|
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v11 |
|
||||||
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v11 |
|
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v11 |
|
||||||
@ -49,7 +48,6 @@ v9 - v12
|
|||||||
| `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | <!--v8--> v11 |
|
| `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | <!--v8--> v11 |
|
||||||
| `@angular/upgrade` | [`getAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
| `@angular/upgrade` | [`getAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
||||||
| `@angular/upgrade` | [`setAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
| `@angular/upgrade` | [`setAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
||||||
| `@angular/platform-webworker` | [All entry points](api/platform-webworker) | <!--v8--> v11 |
|
|
||||||
| template syntax | [`<template`>](#template-tag) | <!--v7--> v11 |
|
| template syntax | [`<template`>](#template-tag) | <!--v7--> v11 |
|
||||||
| polyfills | [reflect-metadata](#reflect-metadata) | <!--v8--> v11 |
|
| polyfills | [reflect-metadata](#reflect-metadata) | <!--v8--> v11 |
|
||||||
| npm package format | [`esm5` and `fesm5` entry-points in @angular/* npm packages](guide/deprecations#esm5-fesm5) | <!-- v9 --> v11 |
|
| npm package format | [`esm5` and `fesm5` entry-points in @angular/* npm packages](guide/deprecations#esm5-fesm5) | <!-- v9 --> v11 |
|
||||||
@ -61,7 +59,6 @@ v9 - v12
|
|||||||
| `@angular/core/testing` | [`async`](#testing) | <!--v9--> v12 |
|
| `@angular/core/testing` | [`async`](#testing) | <!--v9--> v12 |
|
||||||
| `@angular/router` | [`ActivatedRoute` params and `queryParams` properties](#activatedroute-props) | unspecified |
|
| `@angular/router` | [`ActivatedRoute` params and `queryParams` properties](#activatedroute-props) | unspecified |
|
||||||
| template syntax | [`/deep/`, `>>>`, and `::ng-deep`](#deep-component-style-selector) | <!--v7--> unspecified |
|
| template syntax | [`/deep/`, `>>>`, and `::ng-deep`](#deep-component-style-selector) | <!--v7--> unspecified |
|
||||||
| browser support | [`IE 9 and 10, IE mobile`](#ie-9-10-and-mobile) | <!--v10--> v11 |
|
|
||||||
|
|
||||||
For information about Angular CDK and Angular Material deprecations, see the [changelog](https://github.com/angular/components/blob/master/CHANGELOG.md).
|
For information about Angular CDK and Angular Material deprecations, see the [changelog](https://github.com/angular/components/blob/master/CHANGELOG.md).
|
||||||
|
|
||||||
@ -89,7 +86,6 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i
|
|||||||
|
|
||||||
| API | Replacement | Deprecation announced | Notes |
|
| API | Replacement | Deprecation announced | Notes |
|
||||||
| --- | ----------- | --------------------- | ----- |
|
| --- | ----------- | --------------------- | ----- |
|
||||||
| [`CollectionChangeRecord`](api/core/CollectionChangeRecord) | [`IterableChangeRecord`](api/core/IterableChangeRecord) | v4 | none |
|
|
||||||
| [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. |
|
| [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. |
|
||||||
| [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | See [`ReflectiveInjector`](#reflectiveinjector) |
|
| [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | See [`ReflectiveInjector`](#reflectiveinjector) |
|
||||||
| [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none |
|
| [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none |
|
||||||
@ -124,21 +120,7 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i
|
|||||||
|
|
||||||
| API | Replacement | Deprecation announced | Notes |
|
| API | Replacement | Deprecation announced | Notes |
|
||||||
| --- | ----------- | --------------------- | ----- |
|
| --- | ----------- | --------------------- | ----- |
|
||||||
| [`preserveQueryParams`](api/router/NavigationExtras#preserveQueryParams) | [`queryParamsHandling`](api/router/NavigationExtras#queryParamsHandling) | v4 | none |
|
| [`preserveQueryParams`](api/router/UrlCreationOptions#preserveQueryParams) | [`queryParamsHandling`](api/router/UrlCreationOptions#queryParamsHandling) | v4 | none |
|
||||||
|
|
||||||
{@a platform-webworker}
|
|
||||||
### @angular/platform-webworker
|
|
||||||
|
|
||||||
| API | Replacement | Deprecation announced | Notes |
|
|
||||||
| --- | ----------- | --------------------- | ----- |
|
|
||||||
| [All entry points](api/platform-webworker) | none | v8 | See [platform-webworker](#webworker-apps). |
|
|
||||||
|
|
||||||
{@a platform-webworker-dynamic}
|
|
||||||
### @angular/platform-webworker-dynamic
|
|
||||||
|
|
||||||
| API | Replacement | Deprecation announced | Notes |
|
|
||||||
| --- | ----------- | --------------------- | ----- |
|
|
||||||
| [All entry points](api/platform-webworker-dynamic) | none | v8 | See [platform-webworker](#webworker-apps). |
|
|
||||||
|
|
||||||
{@a upgrade}
|
{@a upgrade}
|
||||||
### @angular/upgrade
|
### @angular/upgrade
|
||||||
@ -394,28 +376,6 @@ These two properties have subtle differences, so switching to `textContent` unde
|
|||||||
|
|
||||||
All of the `wtf*` APIs are deprecated and will be removed in a future version.
|
All of the `wtf*` APIs are deprecated and will be removed in a future version.
|
||||||
|
|
||||||
{@a webworker-apps}
|
|
||||||
### Running Angular applications in platform-webworker
|
|
||||||
|
|
||||||
The `@angular/platform-*` packages enable Angular to be run in different contexts. For examples,
|
|
||||||
`@angular/platform-server` enables Angular to be run on the server, and `@angular/platform-browser`
|
|
||||||
enables Angular to be run in a web browser.
|
|
||||||
|
|
||||||
`@angular/platform-webworker` was introduced in Angular version 2 as an experiment in leveraging
|
|
||||||
Angular's rendering architecture to run an entire web application in a
|
|
||||||
[web worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API). We've learned a lot
|
|
||||||
from this experiment and have come to the conclusion that running the entire application in a web
|
|
||||||
worker is not the best strategy for most applications.
|
|
||||||
|
|
||||||
Going forward, we will focus our efforts related to web workers around their primary use case of
|
|
||||||
offloading CPU-intensive, non-critical work needed for initial rendering (such as in-memory search
|
|
||||||
and image processing). Learn more in the
|
|
||||||
[guide to Using Web Workers with the Angular CLI](guide/web-worker).
|
|
||||||
|
|
||||||
As of Angular version 8, all `platform-webworker` APIs are deprecated.
|
|
||||||
This includes both packages: `@angular/platform-webworker` and
|
|
||||||
`@angular/platform-webworker-dynamic`.
|
|
||||||
|
|
||||||
{@a entryComponents}
|
{@a entryComponents}
|
||||||
### `entryComponents` and `ANALYZE_FOR_ENTRY_COMPONENTS` no longer required
|
### `entryComponents` and `ANALYZE_FOR_ENTRY_COMPONENTS` no longer required
|
||||||
Previously, the `entryComponents` array in the `NgModule` definition was used to tell the compiler which components would be created and inserted dynamically. With Ivy, this isn't a requirement anymore and the `entryComponents` array can be removed from existing module declarations. The same applies to the `ANALYZE_FOR_ENTRY_COMPONENTS` injection token.
|
Previously, the `entryComponents` array in the `NgModule` definition was used to tell the compiler which components would be created and inserted dynamically. With Ivy, this isn't a requirement anymore and the `entryComponents` array can be removed from existing module declarations. The same applies to the `ANALYZE_FOR_ENTRY_COMPONENTS` injection token.
|
||||||
@ -464,20 +424,6 @@ export class MyModule {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
{@a ie-9-10-and-mobile}
|
|
||||||
### IE 9, 10, and IE mobile support
|
|
||||||
|
|
||||||
Support for IE 9 and 10 has been deprecated, as well as support for IE Mobile. These will be dropped in a future version.
|
|
||||||
Supporting outdated browsers like these increases bundle size, code complexity, and test load, and also requires time and effort that could be spent on improvements to the framework.
|
|
||||||
For example, fixing issues can be more difficult, as a straightforward fix for modern browsers could break old ones that have quirks due to not receiving updates from vendors.
|
|
||||||
|
|
||||||
The final decision was made on three key points:
|
|
||||||
* __Vendor support__: Microsoft dropped support of IE 9 and 10 on 1/12/16, meaning they no longer provide security updates or technical support. Additionally, Microsoft dropped support for Windows 10 Mobile in December 2019.
|
|
||||||
* __Usage statistics__: We looked at usage trends for IE 9 and 10 (as well as IE Mobile) from various sources and all indicated that usage percentages were extremely small (fractions of 1%).
|
|
||||||
* __Feedback from partners__: We also reached out to some of our Angular customers and none expressed concern about dropping IE 9, 10, nor IE Mobile support.
|
|
||||||
|
|
||||||
|
|
||||||
{@a wrapped-value}
|
{@a wrapped-value}
|
||||||
### `WrappedValue`
|
### `WrappedValue`
|
||||||
|
|
||||||
@ -603,18 +549,6 @@ In practical terms, the `package.json` of all `@angular` packages has changed in
|
|||||||
|
|
||||||
For more information about the npm package format, see the [Angular Package Format spec](https://goo.gl/jB3GVv).
|
For more information about the npm package format, see the [Angular Package Format spec](https://goo.gl/jB3GVv).
|
||||||
|
|
||||||
{@a removed}
|
|
||||||
## Removed APIs
|
|
||||||
|
|
||||||
The following APIs have been removed starting with version 10.0.0*:
|
|
||||||
|
|
||||||
| Package | API | Replacement | Notes |
|
|
||||||
| ---------------- | -------------- | ----------- | ----- |
|
|
||||||
| `@angular/core` | Undecorated base classes that use Angular features | Add Angular decorator | See [migration guide](guide/migration-undecorated-classes) for more info |
|
|
||||||
| `@angular/core` | `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [migration guide](guide/migration-module-with-providers) for more info |
|
|
||||||
|
|
||||||
*To see APIs removed in version 9, check out this guide on the [version 9 docs site](https://v9.angular.io/guide/deprecations#removed).
|
|
||||||
|
|
||||||
{@a style-sanitization}
|
{@a style-sanitization}
|
||||||
### Style Sanitization for `[style]` and `[style.prop]` bindings
|
### Style Sanitization for `[style]` and `[style.prop]` bindings
|
||||||
Angular used to sanitize `[style]` and `[style.prop]` bindings to prevent malicious code from being inserted through `javascript:` expressions in CSS `url()` entries. However, most modern browsers no longer support the usage of these expressions, so sanitization was only maintained for the sake of IE 6 and 7. Given that Angular does not support either IE 6 or 7 and sanitization has a performance cost, we will no longer sanitize style bindings as of version 10 of Angular.
|
Angular used to sanitize `[style]` and `[style.prop]` bindings to prevent malicious code from being inserted through `javascript:` expressions in CSS `url()` entries. However, most modern browsers no longer support the usage of these expressions, so sanitization was only maintained for the sake of IE 6 and 7. Given that Angular does not support either IE 6 or 7 and sanitization has a performance cost, we will no longer sanitize style bindings as of version 10 of Angular.
|
||||||
|
@ -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.
|
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 polyfills, see [polyfill documentation](https://www.webcomponents.org/polyfills).
|
||||||
|
|
||||||
- For more information about Angular browser support, see [Browser Support](guide/browser-support).
|
- For more information about Angular browser support, see [Browser Support](guide/browser-support).
|
||||||
|
@ -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).
|
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 O}
|
||||||
|
|
||||||
{@a observable}
|
{@a observable}
|
||||||
|
@ -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.
|
* 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.
|
The following table provides the status for Angular versions under support.
|
||||||
|
|
||||||
@ -107,6 +107,13 @@ Version | Status | Released | Active Ends | LTS Ends
|
|||||||
|
|
||||||
Angular versions ^4.0.0, ^5.0.0, ^6.0.0 and ^7.0.0 are no longer under support.
|
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}
|
{@a deprecation}
|
||||||
## Deprecation practices
|
## 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:
|
RxJS offers a number of functions that can be used to create new observables. These functions can simplify the process of creating observables from things such as events, timers, promises, and so on. For example:
|
||||||
|
|
||||||
|
|
||||||
<code-example path="rx-library/src/simple-creation.ts" region="promise" header="Create an observable from a promise"></code-example>
|
<code-example path="rx-library/src/simple-creation.1.ts" region="promise" header="Create an observable from a promise"></code-example>
|
||||||
|
|
||||||
<code-example path="rx-library/src/simple-creation.ts" region="interval" header="Create an observable from a counter"></code-example>
|
<code-example path="rx-library/src/simple-creation.2.ts" region="interval" header="Create an observable from a counter"></code-example>
|
||||||
|
|
||||||
<code-example path="rx-library/src/simple-creation.ts" region="event" header="Create an observable from an event"></code-example>
|
<code-example path="rx-library/src/simple-creation.3.ts" region="event" header="Create an observable from an event"></code-example>
|
||||||
|
|
||||||
<code-example path="rx-library/src/simple-creation.ts" region="ajax" header="Create an observable that creates an AJAX request"></code-example>
|
<code-example path="rx-library/src/simple-creation.ts" region="ajax" header="Create an observable that creates an AJAX request"></code-example>
|
||||||
|
|
||||||
|
@ -67,6 +67,33 @@ Therefore, it is recommended to reload the page once the promise returned by `ac
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
### Handling an unrecoverable state
|
||||||
|
|
||||||
|
In some cases, the version of the app used by the service worker to serve a client might be in a broken state that cannot be recovered from without a full page reload.
|
||||||
|
|
||||||
|
For example, imagine the following scenario:
|
||||||
|
- A user opens the app for the first time and the service worker caches the latest version of the app.
|
||||||
|
Let's assume the app's cached assets include `index.html`, `main.<main-hash-1>.js` and `lazy-chunk.<lazy-hash-1>.js`.
|
||||||
|
- The user closes the app and does not open it for a while.
|
||||||
|
- After some time, a new version of the app is deployed to the server.
|
||||||
|
This newer version includes the files `index.html`, `main.<main-hash-2>.js` and `lazy-chunk.<lazy-hash-2>.js` (note that the hashes are different now, because the content of the files has changed).
|
||||||
|
The old version is no longer available on the server.
|
||||||
|
- In the meantime, the user's browser decides to evict `lazy-chunk.<lazy-hash-1>.js` from its cache.
|
||||||
|
Browsers may decide to evict specific (or all) resources from a cache in order to reclaim disk space.
|
||||||
|
- The user opens the app again.
|
||||||
|
The service worker serves the latest version known to it at this point, namely the old version (`index.html` and `main.<main-hash-1>.js`).
|
||||||
|
- At some later point, the app requests the lazy bundle, `lazy-chunk.<lazy-hash-1>.js`.
|
||||||
|
- The service worker is unable to find the asset in the cache (remember that the browser evicted it).
|
||||||
|
Nor is it able to retrieve it from the server (since the server now only has `lazy-chunk.<lazy-hash-2>.js` from the newer version).
|
||||||
|
|
||||||
|
In the above scenario, the service worker is not able to serve an asset that would normally be cached.
|
||||||
|
That particular app version is broken and there is no way to fix the state of the client without reloading the page.
|
||||||
|
In such cases, the service worker notifies the client by sending an `UnrecoverableStateEvent` event.
|
||||||
|
You can subscribe to `SwUpdate#unrecoverable` to be notified and handle these errors.
|
||||||
|
|
||||||
|
<code-example path="service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts" header="handle-unrecoverable-state.service.ts" region="sw-unrecoverable-state"></code-example>
|
||||||
|
|
||||||
|
|
||||||
## More on Angular service workers
|
## More on Angular service workers
|
||||||
|
|
||||||
You may also be interested in the following:
|
You may also be interested in the following:
|
||||||
|
@ -267,6 +267,12 @@ By default, these criteria are:
|
|||||||
1. The URL must not contain a file extension (i.e. a `.`) in the last path segment.
|
1. The URL must not contain a file extension (i.e. a `.`) in the last path segment.
|
||||||
2. The URL must not contain `__`.
|
2. The URL must not contain `__`.
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
To configure whether navigation requests are sent through to the network or not, see the [navigationRequestStrategy](#navigation-request-strategy) section.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
### Matching navigation request URLs
|
### Matching navigation request URLs
|
||||||
|
|
||||||
While these default criteria are fine in most cases, it is sometimes desirable to configure different rules. For example, you may want to ignore specific routes (that are not part of the Angular app) and pass them through to the server.
|
While these default criteria are fine in most cases, it is sometimes desirable to configure different rules. For example, you may want to ignore specific routes (that are not part of the Angular app) and pass them through to the server.
|
||||||
@ -285,3 +291,32 @@ If the field is omitted, it defaults to:
|
|||||||
'!/**/*__*/**', // Exclude URLs containing `__` in any other segment.
|
'!/**/*__*/**', // Exclude URLs containing `__` in any other segment.
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
{@a navigation-request-strategy}
|
||||||
|
|
||||||
|
## `navigationRequestStrategy`
|
||||||
|
|
||||||
|
This optional property enables you to configure how the service worker handles navigation requests:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"navigationRequestStrategy": "freshness"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Possible values:
|
||||||
|
|
||||||
|
- `'performance'`: The default setting. Serves the specified [index file](#index-file), which is typically cached.
|
||||||
|
- `'freshness'`: Passes the requests through to the network and falls back to the `performance` behavior when offline.
|
||||||
|
This value is useful when the server redirects the navigation requests elsewhere using an HTTP redirect (3xx status code).
|
||||||
|
Reasons for using this value include:
|
||||||
|
- Redirecting to an authentication website when authentication is not handled by the application.
|
||||||
|
- Redirecting specific URLs to avoid breaking existing links/bookmarks after a website redesign.
|
||||||
|
- Redirecting to a different website, such as a server-status page, while a page is temporarily down.
|
||||||
|
|
||||||
|
<div class="alert is-important">
|
||||||
|
|
||||||
|
The `freshness` strategy usually results in more requests sent to the server, which can increase response latency.
|
||||||
|
It is recommended that you use the default performance strategy whenever possible.
|
||||||
|
|
||||||
|
</div>
|
@ -1,7 +1,7 @@
|
|||||||
# Template statements
|
# Template statements
|
||||||
|
|
||||||
A template **statement** responds to an **event** raised by a binding target
|
Template statements are methods or properties that you can use in your HTML to respond to user events.
|
||||||
such as an element, component, or directive.
|
With template statements, your application can engage users through actions such as displaying dynamic content or submitting forms.
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
@ -10,24 +10,30 @@ the syntax and code snippets in this guide.
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The following template statement appears in quotes to the right of the `=` symbol as in `(event)="statement"`.
|
In the following example, the template statement `deleteHero()` appears in quotes to the right of the `=` symbol as in `(event)="statement"`.
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
|
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
A template statement *has a side effect*.
|
When the user clicks the **Delete hero** button, Angular calls the `deleteHero()` function in the component class.
|
||||||
That's the whole point of an event.
|
|
||||||
It's how you update application state from user action.
|
|
||||||
|
|
||||||
Responding to events is the other side of Angular's "unidirectional data flow".
|
You can use template statements with elements, components, or directives in response to events.
|
||||||
You're free to change anything, anywhere, during this turn of the event loop.
|
|
||||||
|
|
||||||
Like template expressions, template *statements* use a language that looks like JavaScript.
|
<div class="alert is-helpful">
|
||||||
The template statement parser differs from the template expression parser and
|
|
||||||
specifically supports both basic assignment (`=`) and chaining expressions with <code>;</code>.
|
|
||||||
|
|
||||||
However, certain JavaScript and template expression syntax is not allowed:
|
Responding to events is an aspect of Angular's [unidirectional data flow](guide/glossary#unidirectional-data-flow).
|
||||||
|
You can change anything in your application during a single event loop.
|
||||||
|
|
||||||
* <code>new</code>
|
</div>
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
Like [template expressions](guide/interpolation), template statements use a language that looks like JavaScript.
|
||||||
|
However, the parser for template statements differs from the parser for template expressions.
|
||||||
|
In addition, the template statements parser specifically supports both basic assignment, `=`, and chaining expressions with semicolons, `;`.
|
||||||
|
|
||||||
|
The following JavaScript and template expression syntax is not allowed:
|
||||||
|
|
||||||
|
* `new`
|
||||||
* increment and decrement operators, `++` and `--`
|
* increment and decrement operators, `++` and `--`
|
||||||
* operator assignment, such as `+=` and `-=`
|
* operator assignment, such as `+=` and `-=`
|
||||||
* the bitwise operators, such as `|` and `&`
|
* the bitwise operators, such as `|` and `&`
|
||||||
@ -35,31 +41,32 @@ However, certain JavaScript and template expression syntax is not allowed:
|
|||||||
|
|
||||||
## Statement context
|
## Statement context
|
||||||
|
|
||||||
As with expressions, statements can refer only to what's in the statement context
|
Statements have a context—a particular part of the application to which the statement belongs.
|
||||||
such as an event handling method of the component instance.
|
|
||||||
|
|
||||||
The *statement context* is typically the component instance.
|
Statements can refer only to what's in the statement context, which is typically the component instance.
|
||||||
The *deleteHero* in `(click)="deleteHero()"` is a method of the data-bound component.
|
For example, `deleteHero()` of `(click)="deleteHero()"` is a method of the component in the following snippet.
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
|
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
The statement context may also refer to properties of the template's own context.
|
The statement context may also refer to properties of the template's own context.
|
||||||
In the following examples, the template `$event` object,
|
In the following example, the component's event handling method, `onSave()` takes the template's own `$event` object as an argument.
|
||||||
a [template input variable](guide/built-in-directives#template-input-variable) (`let hero`),
|
On the next two lines, the `deleteHero()` method takes a [template input variable](guide/built-in-directives#template-input-variable), `hero`, and `onSubmit()` takes a [template reference variable](guide/template-reference-variables), `#heroForm`.
|
||||||
and a [template reference variable](guide/template-reference-variables) (`#heroForm`)
|
|
||||||
are passed to an event handling method of the component.
|
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="context-var-statement" header="src/app/app.component.html"></code-example>
|
<code-example path="template-syntax/src/app/app.component.html" region="context-var-statement" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
|
In this example, the context of the `$event` object, `hero`, and `#heroForm` is the template.
|
||||||
|
|
||||||
Template context names take precedence over component context names.
|
Template context names take precedence over component context names.
|
||||||
In `deleteHero(hero)` above, the `hero` is the template input variable,
|
In the preceding `deleteHero(hero)`, the `hero` is the template input variable, not the component's `hero` property.
|
||||||
not the component's `hero` property.
|
|
||||||
|
|
||||||
## Statement guidelines
|
## Statement best practices
|
||||||
|
|
||||||
Template statements cannot refer to anything in the global namespace. They
|
* **Conciseness**
|
||||||
can't refer to `window` or `document`.
|
|
||||||
They can't call `console.log` or `Math.max`.
|
|
||||||
|
|
||||||
As with expressions, avoid writing complex template statements.
|
Keep template statements minimal by using method calls or basic property assignments.
|
||||||
A method call or simple property assignment should be the norm.
|
|
||||||
|
* **Work within the context**
|
||||||
|
|
||||||
|
The context of a template statement can be the component class instance or the template.
|
||||||
|
Because of this, template statements cannot refer to anything in the global namespace such as `window` or `document`.
|
||||||
|
For example, template statements can't call `console.log()` or `Math.max()`.
|
||||||
|
@ -38,9 +38,9 @@ If you're curious about the specific migrations being run by the CLI, see the [a
|
|||||||
### New Deprecations
|
### New Deprecations
|
||||||
|
|
||||||
| Area | API or Feature | May be removed in |
|
| Area | API or Feature | May be removed in |
|
||||||
| ----------------------------- | --------------------------------------------------------------------------- | ----------------- |
|
| ----------------------------- | -------------------------------------------------- | ----------------- |
|
||||||
| `@angular/core` | [`WrappedValue`](guide/deprecations#wrapped-value) | <!--v10--> v12 |
|
| `@angular/core` | [`WrappedValue`](guide/deprecations#wrapped-value) | <!--v10--> v12 |
|
||||||
| browser support | [`IE 9, 10, and IE Mobile`](guide/deprecations#ie-9-10-and-ie-mobile-support) | <!--v10--> v11 |
|
| browser support | IE 9, 10, and IE Mobile | <!--v10--> v11 |
|
||||||
|
|
||||||
|
|
||||||
{@a removals}
|
{@a removals}
|
||||||
|
@ -324,5 +324,5 @@ These techniques are useful for small-scale demonstrations, but they
|
|||||||
quickly become verbose and clumsy when handling large amounts of user input.
|
quickly become verbose and clumsy when handling large amounts of user input.
|
||||||
Two-way data binding is a more elegant and compact way to move
|
Two-way data binding is a more elegant and compact way to move
|
||||||
values between data entry fields and model properties.
|
values between data entry fields and model properties.
|
||||||
The next page, `Forms`, explains how to write
|
The [`Forms`](guide/forms-overview) page explains how to write
|
||||||
two-way bindings with `NgModel`.
|
two-way bindings with `NgModel`.
|
||||||
|
@ -32,7 +32,7 @@ To do this:
|
|||||||
|
|
||||||
1. Create a `typings.d.ts` file in your `src/` folder. This file is automatically included as global type definition.
|
1. Create a `typings.d.ts` file in your `src/` folder. This file is automatically included as global type definition.
|
||||||
|
|
||||||
2. Add the following code in `src/typings.d.ts`.
|
2. Add the following code in `src/typings.d.ts`:
|
||||||
|
|
||||||
```
|
```
|
||||||
declare module 'host' {
|
declare module 'host' {
|
||||||
@ -45,7 +45,7 @@ declare module 'host' {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. In the component or file that uses the library, add the following code.
|
3. In the component or file that uses the library, add the following code:
|
||||||
|
|
||||||
```
|
```
|
||||||
import * as host from 'host';
|
import * as host from 'host';
|
||||||
@ -129,7 +129,7 @@ interface JQuery {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If don't add the interface for the script-defined extension, your IDE shows an error:
|
If you don't add the interface for the script-defined extension, your IDE shows an error:
|
||||||
|
|
||||||
```
|
```
|
||||||
[TS][Error] Property 'myPlugin' does not exist on type 'JQuery'
|
[TS][Error] Property 'myPlugin' does not exist on type 'JQuery'
|
||||||
|
@ -12,7 +12,7 @@ include the following information:
|
|||||||
- Node and Angular CLI version (local version only).
|
- Node and Angular CLI version (local version only).
|
||||||
- How long each command took to initialize and execute.
|
- How long each command took to initialize and execute.
|
||||||
- Command name that was run.
|
- Command name that was run.
|
||||||
- For Schematics commands (add, generate, new and update), a list of whitelisted flags.
|
- For Schematics commands (add, generate, new and update), a list of selected flags.
|
||||||
- For build commands (build, serve), the number and size of bundles (initial and lazy),
|
- For build commands (build, serve), the number and size of bundles (initial and lazy),
|
||||||
compilation units, the time it took to build and rebuild, and basic Angular-specific
|
compilation units, the time it took to build and rebuild, and basic Angular-specific
|
||||||
API usage.
|
API usage.
|
||||||
|
@ -53,6 +53,9 @@
|
|||||||
},
|
},
|
||||||
"kyliau": {
|
"kyliau": {
|
||||||
"name": "Keen Yee Liau",
|
"name": "Keen Yee Liau",
|
||||||
|
"twitter": "liauky",
|
||||||
|
"website": "https://github.com/kyliau",
|
||||||
|
"bio": "Keen works on language service and CLI. He also maintains Karma and Protractor.",
|
||||||
"groups": ["Angular"],
|
"groups": ["Angular"],
|
||||||
"lead": "igorminar",
|
"lead": "igorminar",
|
||||||
"picture": "kyliau.jpg"
|
"picture": "kyliau.jpg"
|
||||||
|
@ -3,162 +3,5 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<article class="events-container">
|
<article class="events-container">
|
||||||
<p>Where we'll be presenting:</p>
|
<aio-events></aio-events>
|
||||||
<table class="is-full-width">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Event</th>
|
|
||||||
<th>Location</th>
|
|
||||||
<th>Date</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p>Where we already presented:</p>
|
|
||||||
<table class="is-full-width">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Event</th>
|
|
||||||
<th>Location</th>
|
|
||||||
<th>Date</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<!-- ng-vikings 2020 -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
|
|
||||||
<td>Oslo, Norway</td>
|
|
||||||
<td>May 25-26 conference, 27 workshops, 2020</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ng-conf 2020 -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ng-conf.org/" title="ng-conf">ng-conf</a></th>
|
|
||||||
<td>Salt Lake City, Utah</td>
|
|
||||||
<td>April 1-3, 2020</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngIndia 2020 -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://www.ng-ind.com/" title="ngIndia">ngIndia</a></th>
|
|
||||||
<td>Delhi, India</td>
|
|
||||||
<td>Feb 29, 2020</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ReactiveConf 2019 -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th>
|
|
||||||
<td>Prague, Czech Republic</td>
|
|
||||||
<td>October 30 - November 1, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- NG Rome 2019-->
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<a href="https://ngrome.io" title="NG Rome MMXIX - The Italian Angular Conference">NG Rome MMXIX</a>
|
|
||||||
</th>
|
|
||||||
<td>Rome, Italy</td>
|
|
||||||
<td>Oct 6th workshops, 7th conference, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- AngularConnect 2019-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://www.angularconnect.com/?utm_source=angular.io&utm_medium=referral"
|
|
||||||
title="AngularConnect">AngularConnect</a></th>
|
|
||||||
<td>London, UK</td>
|
|
||||||
<td>September 19-20, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- NG-DE 2019-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ng-de.org/" title="NG-DE">NG-DE</a></th>
|
|
||||||
<td>Berlin, Germany</td>
|
|
||||||
<td>August 29th workshops, 30-31 conference, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngJapan-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ngjapan.org" title="ng-japan">ng-japan</a></th>
|
|
||||||
<td>Tokyo, Japan</td>
|
|
||||||
<td>July 13, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngVikings 2019-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
|
|
||||||
<td>Copenhagen, Denmark</td>
|
|
||||||
<td>May 26 (workshops), 27-28 (conference), 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ng-conf 2019-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ng-conf.org/" title="ng-conf">ng-conf</a></th>
|
|
||||||
<td>Salt Lake City, Utah</td>
|
|
||||||
<td>May 1-3, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ng-India 2019-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://www.ng-ind.com/" title="ng-India">ng-India</a></th>
|
|
||||||
<td>Gurgaon, India</td>
|
|
||||||
<td>February 23, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngAtlanta 2019 -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ng-atl.org/" title="ngAtlanta">ngAtlanta</a></th>
|
|
||||||
<td>Atlanta, Georgia</td>
|
|
||||||
<td>January 9-12, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- AngularConnect-->
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<a href="https://past.angularconnect.com/2018" title="AngularConnect">AngularConnect</a>
|
|
||||||
</th>
|
|
||||||
<td>London, United Kingdom</td>
|
|
||||||
<td>November 5-7, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ReactiveConf -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th>
|
|
||||||
<td>Prague, Czech Republic</td>
|
|
||||||
<td>October 29-31, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- AngularMix -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://angularmix.com/" title="AngularMix">AngularMix</a></th>
|
|
||||||
<td>Orlando, Florida</td>
|
|
||||||
<td>October 10-12, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- Angular Conf Australia-->
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<a href="https://www.angularconf.com.au/" title="Angular Conf Australia">Angular Conf Australia</a>
|
|
||||||
</th>
|
|
||||||
<td>Melbourne, Australia</td>
|
|
||||||
<td>Jun 22, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngJapan-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ngjapan.org/en.html" title="ng-japan">ng-japan</a></th>
|
|
||||||
<td>Tokyo, Japan</td>
|
|
||||||
<td>Jun 16, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- WeRDevs-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://www.wearedevelopers.com/" title="WeAreDevs">WeAreDevelopers</a></th>
|
|
||||||
<td>Vienna, Austria</td>
|
|
||||||
<td>May 16-18, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngconf 2018-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://www.ng-conf.org/" title="ng-conf">ng-conf</a></th>
|
|
||||||
<td>Salt Lake City, Utah</td>
|
|
||||||
<td>April 18-20, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngVikings-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
|
|
||||||
<td>Helsinki, Finland</td>
|
|
||||||
<td>March 1-2, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngAtlanta-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="http://ng-atl.org/" title="ngAtlanta">ngAtlanta</a></th>
|
|
||||||
<td>Atlanta, Georgia</td>
|
|
||||||
<td>January 30, 2018</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</article>
|
</article>
|
||||||
|
245
aio/content/marketing/events.json
Normal file
245
aio/content/marketing/events.json
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "ng-china",
|
||||||
|
"location": "Online",
|
||||||
|
"linkUrl": "https://ng-china.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-11-21",
|
||||||
|
"end": "2020-11-22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "EnterpriseNG",
|
||||||
|
"location": "Online",
|
||||||
|
"linkUrl": "https://www.ng-conf.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-11-19",
|
||||||
|
"end": "2020-11-20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngrome",
|
||||||
|
"location": "Online",
|
||||||
|
"linkUrl": "https://ngrome.io/",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-10-20",
|
||||||
|
"end": "2020-10-20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DevReach",
|
||||||
|
"location": "Online",
|
||||||
|
"linkUrl": "https://www.telerik.com/devreach/online/agenda-thursday#sessions",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-10-19",
|
||||||
|
"end": "2020-10-23"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngVikings",
|
||||||
|
"location": "Oslo, Norway",
|
||||||
|
"linkUrl": "https://ngvikings.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-05-25",
|
||||||
|
"end": "2020-05-26"
|
||||||
|
},
|
||||||
|
"workshopsDate": {
|
||||||
|
"start": "2020-05-27",
|
||||||
|
"end": "2020-05-27"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng-conf",
|
||||||
|
"location": "Salt Lake City, Utah",
|
||||||
|
"linkUrl": "https://ng-conf.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-04-01",
|
||||||
|
"end": "2020-04-03"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngIndia",
|
||||||
|
"location": "Delhi, India",
|
||||||
|
"linkUrl": "https://www.ng-ind.com/",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-02-29",
|
||||||
|
"end": "2020-02-29"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ReactiveConf",
|
||||||
|
"location": "Prague, Czech Republic",
|
||||||
|
"linkUrl": "https://reactiveconf.com/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-10-30",
|
||||||
|
"end": "2019-11-01"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "NG Rome MMXIX",
|
||||||
|
"location": "Rome, Italy",
|
||||||
|
"linkUrl": "https://ngrome.io",
|
||||||
|
"tooltip": "NG Rome MMXIX - The Italian Angular Conference",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-10-07",
|
||||||
|
"end": "2019-10-07"
|
||||||
|
},
|
||||||
|
"workshopsDate": {
|
||||||
|
"start": "2019-10-06",
|
||||||
|
"end": "2019-10-06"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AngularConnect",
|
||||||
|
"location": "London, UK",
|
||||||
|
"linkUrl": "https://www.angularconnect.com/?utm_source=angular.io&utm_medium=referral",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-09-19",
|
||||||
|
"end": "2019-09-20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "NG-DE",
|
||||||
|
"location": "Berlin, Germany",
|
||||||
|
"linkUrl": "https://ng-de.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-08-30",
|
||||||
|
"end": "2019-08-31"
|
||||||
|
},
|
||||||
|
"workshopsDate": {
|
||||||
|
"start": "2019-08-29",
|
||||||
|
"end": "2019-08-29"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng-japan",
|
||||||
|
"location": "Tokyo, Japan",
|
||||||
|
"linkUrl": "https://ngjapan.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-07-13",
|
||||||
|
"end": "2019-07-13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngVikings",
|
||||||
|
"location": "Copenhagen, Denmark",
|
||||||
|
"linkUrl": "https://ngvikings.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-05-27",
|
||||||
|
"end": "2019-05-28"
|
||||||
|
},
|
||||||
|
"workshopsDate": {
|
||||||
|
"start": "2019-05-26",
|
||||||
|
"end": "2019-05-26"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng-conf",
|
||||||
|
"location": "Salt Lake City, Utah",
|
||||||
|
"linkUrl": "https://ng-conf.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-05-01",
|
||||||
|
"end": "2019-05-03"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng-India",
|
||||||
|
"location": "Gurgaon, India",
|
||||||
|
"linkUrl": "https://www.ng-ind.com/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-02-23",
|
||||||
|
"end": "2019-02-23"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngAtlanta",
|
||||||
|
"location": "Atlanta, Georgia",
|
||||||
|
"linkUrl": "https://ng-atl.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-01-09",
|
||||||
|
"end": "2019-01-12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AngularConnect",
|
||||||
|
"location": "London, United Kingdom",
|
||||||
|
"linkUrl": "https://past.angularconnect.com/2018",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-11-05",
|
||||||
|
"end": "2018-11-07"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ReactiveConf",
|
||||||
|
"location": "Prague, Czech Republic",
|
||||||
|
"linkUrl": "https://reactiveconf.com/",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-10-29",
|
||||||
|
"end": "2018-10-31"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AngularMix",
|
||||||
|
"location": "Orlando, Florida",
|
||||||
|
"linkUrl": "https://angularmix.com/",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-10-10",
|
||||||
|
"end": "2018-10-12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Angular Conf Australia",
|
||||||
|
"location": "Melbourne, Australia",
|
||||||
|
"linkUrl": "https://www.angularconf.com.au/",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-06-22",
|
||||||
|
"end": "2018-06-22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng-japan",
|
||||||
|
"location": "Tokyo, Japan",
|
||||||
|
"linkUrl": "https://ngjapan.org/en.html",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-06-16",
|
||||||
|
"end": "2018-06-16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WeAreDevelopers",
|
||||||
|
"location": "Vienna, Austria",
|
||||||
|
"linkUrl": "https://www.wearedevelopers.com/",
|
||||||
|
"tooltip": "WeAreDevs",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-05-16",
|
||||||
|
"end": "2018-05-18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng-conf",
|
||||||
|
"location": "Salt Lake City, Utah",
|
||||||
|
"linkUrl": "https://ng-conf.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-04-18",
|
||||||
|
"end": "2018-04-20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngVikings",
|
||||||
|
"location": "Helsinki, Finland",
|
||||||
|
"linkUrl": "https://ngvikings.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-03-01",
|
||||||
|
"end": "2018-03-02"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngAtlanta",
|
||||||
|
"location": "Atlanta, Georgia",
|
||||||
|
"linkUrl": "https://ng-atl.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-01-30",
|
||||||
|
"end": "2018-01-30"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
@ -61,27 +61,27 @@
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"url": "start",
|
"url": "start",
|
||||||
"title": "A Sample App",
|
"title": "Getting started",
|
||||||
"tooltip": "Take a look at Angular's component model, template syntax, and component communication."
|
"tooltip": "Take a look at Angular's component model, template syntax, and component communication."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "start/start-routing",
|
"url": "start/start-routing",
|
||||||
"title": "In-app Navigation",
|
"title": "Adding navigation",
|
||||||
"tooltip": "Navigate among different page views using the browser's URL."
|
"tooltip": "Navigate among different page views using the browser's URL."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "start/start-data",
|
"url": "start/start-data",
|
||||||
"title": "Manage Data",
|
"title": "Managing Data",
|
||||||
"tooltip": "Use services and access external data via HTTP."
|
"tooltip": "Use services and access external data via HTTP."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "start/start-forms",
|
"url": "start/start-forms",
|
||||||
"title": "Forms for User Input",
|
"title": "Using Forms for User Input",
|
||||||
"tooltip": "Learn about fetching and managing data from users with forms."
|
"tooltip": "Learn about fetching and managing data from users with forms."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "start/start-deployment",
|
"url": "start/start-deployment",
|
||||||
"title": "Deployment",
|
"title": "Deploying an application",
|
||||||
"tooltip": "Move to local development, or deploy your application to Firebase or your own server."
|
"tooltip": "Move to local development, or deploy your application to Firebase or your own server."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -101,11 +101,6 @@
|
|||||||
"title": "Components",
|
"title": "Components",
|
||||||
"tooltip": "Building dynamic views with data binding",
|
"tooltip": "Building dynamic views with data binding",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
|
||||||
"url": "guide/displaying-data",
|
|
||||||
"title": "Data binding",
|
|
||||||
"tooltip": "Property binding helps show app data in the UI."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "guide/user-input",
|
"url": "guide/user-input",
|
||||||
"title": "User Input",
|
"title": "User Input",
|
||||||
@ -542,27 +537,6 @@
|
|||||||
"title": "Tutorials",
|
"title": "Tutorials",
|
||||||
"tooltip": "End-to-end tutorials for learning Angular concepts and patterns.",
|
"tooltip": "End-to-end tutorials for learning Angular concepts and patterns.",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
|
||||||
"title": "Routing",
|
|
||||||
"tooltip": "End-to-end tutorials for learning about Angular's router.",
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"url": "guide/router-tutorial",
|
|
||||||
"title": "Using Angular Routes in a Single-page Application",
|
|
||||||
"tooltip": "A tutorial that covers many patterns associated with Angular routing."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "guide/router-tutorial-toh",
|
|
||||||
"title": "Router tutorial: tour of heroes",
|
|
||||||
"tooltip": "Explore how to use Angular's router. Based on the Tour of Heroes example."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "guide/forms",
|
|
||||||
"title": "Building a Template-driven Form",
|
|
||||||
"tooltip": "Create a template-driven form using directives and Angular template syntax."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "Tutorial: Tour of Heroes",
|
"title": "Tutorial: Tour of Heroes",
|
||||||
"tooltip": "The Tour of Heroes app is used as a reference point in many Angular examples.",
|
"tooltip": "The Tour of Heroes app is used as a reference point in many Angular examples.",
|
||||||
@ -609,6 +583,32 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Routing",
|
||||||
|
"tooltip": "End-to-end tutorials for learning about Angular's router.",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"url": "guide/router-tutorial",
|
||||||
|
"title": "Using Angular Routes in a Single-page Application",
|
||||||
|
"tooltip": "A tutorial that covers many patterns associated with Angular routing."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "guide/router-tutorial-toh",
|
||||||
|
"title": "Router tutorial: tour of heroes",
|
||||||
|
"tooltip": "Explore how to use Angular's router. Based on the Tour of Heroes example."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "guide/forms",
|
||||||
|
"title": "Building a Template-driven Form",
|
||||||
|
"tooltip": "Create a template-driven form using directives and Angular template syntax."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "guide/displaying-data",
|
||||||
|
"title": "Data binding",
|
||||||
|
"tooltip": "Property binding helps show app data in the UI."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "guide/web-worker",
|
"url": "guide/web-worker",
|
||||||
"title": "Web Workers",
|
"title": "Web Workers",
|
||||||
@ -954,6 +954,11 @@
|
|||||||
"url": "guide/styleguide",
|
"url": "guide/styleguide",
|
||||||
"title": "Coding Style Guide",
|
"title": "Coding Style Guide",
|
||||||
"tooltip": "Guidelines for writing Angular code."
|
"tooltip": "Guidelines for writing Angular code."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "guide/docs-style-guide",
|
||||||
|
"title": "Documentation Style Guide",
|
||||||
|
"tooltip": "Style guide for documentation authors."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Part 1: Getting started with a basic Angular app
|
# Getting started with a basic Angular app
|
||||||
|
|
||||||
Welcome to Angular!
|
Welcome to Angular!
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Try it: Manage data
|
# Managing data
|
||||||
|
|
||||||
At the end of [In-app Navigation](start/start-routing "Try it: In-app Navigation"), the online store application has a product catalog with two views: a product list and product details.
|
At the end of [In-app Navigation](start/start-routing "Try it: In-app Navigation"), the online store application has a product catalog with two views: a product list and product details.
|
||||||
Users can click on a product name from the list to see details in a new view, with a distinct URL, or route.
|
Users can click on a product name from the list to see details in a new view, with a distinct URL, or route.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Try it: Deployment
|
# Deploying an application
|
||||||
|
|
||||||
|
|
||||||
To deploy your application, you have to compile it, and then host the JavaScript, CSS, and HTML on a web server. Built Angular applications are very portable and can live in any environment or served by any technology, such as Node, Java, .NET, PHP, and many others.
|
To deploy your application, you have to compile it, and then host the JavaScript, CSS, and HTML on a web server. Built Angular applications are very portable and can live in any environment or served by any technology, such as Node, Java, .NET, PHP, and many others.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Try it: Use forms for user input
|
# Using forms for user input
|
||||||
|
|
||||||
At the end of [Managing Data](start/start-data "Try it: Managing Data"), the online store application has a product catalog and a shopping cart.
|
At the end of [Managing Data](start/start-data "Try it: Managing Data"), the online store application has a product catalog and a shopping cart.
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# In-app navigation
|
# Adding navigation
|
||||||
|
|
||||||
At the end of [part 1](start "Get started with a basic Angular app"), the online store application has a basic product catalog.
|
At the end of [part 1](start "Get started with a basic Angular app"), the online store application has a basic product catalog.
|
||||||
The app doesn't have any variable states or navigation.
|
The app doesn't have any variable states or navigation.
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
"build-local-with-viewengine": "yarn ~~build",
|
"build-local-with-viewengine": "yarn ~~build",
|
||||||
"prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci",
|
"prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci",
|
||||||
"build-local-with-viewengine-ci": "yarn ~~build --progress=false",
|
"build-local-with-viewengine-ci": "yarn ~~build --progress=false",
|
||||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js ef770f1cb",
|
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 568d34cdb",
|
||||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
||||||
"test": "yarn check-env && ng test",
|
"test": "yarn check-env && ng test",
|
||||||
"pree2e": "yarn check-env && yarn update-webdriver",
|
"pree2e": "yarn check-env && yarn update-webdriver",
|
||||||
@ -87,28 +87,27 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "10.0.2",
|
"@angular/animations": "10.1.3",
|
||||||
"@angular/cdk": "10.0.1",
|
"@angular/cdk": "10.2.2",
|
||||||
"@angular/common": "10.0.2",
|
"@angular/common": "10.1.3",
|
||||||
"@angular/compiler": "10.0.2",
|
"@angular/compiler": "10.1.3",
|
||||||
"@angular/core": "10.0.2",
|
"@angular/core": "10.1.3",
|
||||||
"@angular/elements": "10.0.2",
|
"@angular/elements": "10.1.3",
|
||||||
"@angular/forms": "10.0.2",
|
"@angular/forms": "10.1.3",
|
||||||
"@angular/material": "10.0.1",
|
"@angular/material": "10.2.2",
|
||||||
"@angular/platform-browser": "10.0.2",
|
"@angular/platform-browser": "10.1.3",
|
||||||
"@angular/platform-browser-dynamic": "10.0.2",
|
"@angular/platform-browser-dynamic": "10.1.3",
|
||||||
"@angular/router": "10.0.2",
|
"@angular/router": "10.1.3",
|
||||||
"@angular/service-worker": "10.0.2",
|
"@angular/service-worker": "10.1.3",
|
||||||
"@webcomponents/custom-elements": "1.2.1",
|
"@webcomponents/custom-elements": "1.2.1",
|
||||||
"rxjs": "^6.5.3",
|
"rxjs": "^6.5.3",
|
||||||
"tslib": "^1.10.0",
|
"tslib": "^2.0.0",
|
||||||
"zone.js": "~0.10.3"
|
"zone.js": "~0.10.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "0.1000.1",
|
"@angular-devkit/build-angular": "0.1001.3",
|
||||||
"@angular/cli": "10.0.1",
|
"@angular/cli": "10.1.3",
|
||||||
"@angular/compiler-cli": "10.0.2",
|
"@angular/compiler-cli": "10.1.3",
|
||||||
"@angular/language-service": "10.0.2",
|
|
||||||
"@types/jasmine": "^3.4.2",
|
"@types/jasmine": "^3.4.2",
|
||||||
"@types/jasminewd2": "^2.0.8",
|
"@types/jasminewd2": "^2.0.8",
|
||||||
"@types/lunr": "^2.3.2",
|
"@types/lunr": "^2.3.2",
|
||||||
@ -119,7 +118,7 @@
|
|||||||
"canonical-path": "1.0.0",
|
"canonical-path": "1.0.0",
|
||||||
"chalk": "^2.1.0",
|
"chalk": "^2.1.0",
|
||||||
"cjson": "^0.5.0",
|
"cjson": "^0.5.0",
|
||||||
"codelyzer": "^6.0.0-next.1",
|
"codelyzer": "^6.0.0",
|
||||||
"cross-spawn": "^5.1.0",
|
"cross-spawn": "^5.1.0",
|
||||||
"css-selector-parser": "^1.3.0",
|
"css-selector-parser": "^1.3.0",
|
||||||
"dgeni": "^0.4.11",
|
"dgeni": "^0.4.11",
|
||||||
@ -136,8 +135,8 @@
|
|||||||
"html": "^1.0.0",
|
"html": "^1.0.0",
|
||||||
"ignore": "^3.3.3",
|
"ignore": "^3.3.3",
|
||||||
"image-size": "^0.5.1",
|
"image-size": "^0.5.1",
|
||||||
"jasmine": "^3.4.0",
|
"jasmine": "~3.6.0",
|
||||||
"jasmine-core": "^3.4.0",
|
"jasmine-core": "~3.6.0",
|
||||||
"jasmine-spec-reporter": "~5.0.0",
|
"jasmine-spec-reporter": "~5.0.0",
|
||||||
"jsdom": "^9.12.0",
|
"jsdom": "^9.12.0",
|
||||||
"json-schema-traverse": "^0.4.1",
|
"json-schema-traverse": "^0.4.1",
|
||||||
@ -145,7 +144,7 @@
|
|||||||
"karma": "~5.0.0",
|
"karma": "~5.0.0",
|
||||||
"karma-chrome-launcher": "^3.1.0",
|
"karma-chrome-launcher": "^3.1.0",
|
||||||
"karma-coverage-istanbul-reporter": "^2.1.0",
|
"karma-coverage-istanbul-reporter": "^2.1.0",
|
||||||
"karma-jasmine": "^3.1.1",
|
"karma-jasmine": "~4.0.0",
|
||||||
"karma-jasmine-html-reporter": "^1.4.2",
|
"karma-jasmine-html-reporter": "^1.4.2",
|
||||||
"light-server": "^2.6.2",
|
"light-server": "^2.6.2",
|
||||||
"lighthouse": "6.1.0",
|
"lighthouse": "6.1.0",
|
||||||
@ -165,7 +164,7 @@
|
|||||||
"tree-kill": "^1.1.0",
|
"tree-kill": "^1.1.0",
|
||||||
"ts-node": "^8.4.1",
|
"ts-node": "^8.4.1",
|
||||||
"tslint": "~6.1.0",
|
"tslint": "~6.1.0",
|
||||||
"typescript": "~3.9.5",
|
"typescript": "~4.0.2",
|
||||||
"uglify-js": "^3.0.15",
|
"uglify-js": "^3.0.15",
|
||||||
"unist-util-filter": "^0.2.1",
|
"unist-util-filter": "^0.2.1",
|
||||||
"unist-util-source": "^1.0.1",
|
"unist-util-source": "^1.0.1",
|
||||||
|
@ -40,6 +40,10 @@ export const ELEMENT_MODULE_LOAD_CALLBACKS_AS_ROUTES = [
|
|||||||
{
|
{
|
||||||
selector: 'live-example',
|
selector: 'live-example',
|
||||||
loadChildren: () => import('./live-example/live-example.module').then(m => m.LiveExampleModule)
|
loadChildren: () => import('./live-example/live-example.module').then(m => m.LiveExampleModule)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'aio-events',
|
||||||
|
loadChildren: () => import('./events/events.module').then(m => m.EventsModule)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
43
aio/src/app/custom-elements/events/events.component.html
Normal file
43
aio/src/app/custom-elements/events/events.component.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<p>Where we'll be presenting:</p>
|
||||||
|
<table class="is-full-width">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Event</th>
|
||||||
|
<th>Location</th>
|
||||||
|
<th>Date</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody >
|
||||||
|
<tr *ngFor="let event of upcomingEvents">
|
||||||
|
<th><a href="{{event.linkUrl}}" title="{{event.tooltip}}">{{event.name}}</a></th>
|
||||||
|
<td>{{event.location}}</td>
|
||||||
|
<td>
|
||||||
|
<div>
|
||||||
|
{{getEventDates(event)}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>Where we already presented:</p>
|
||||||
|
<table class="is-full-width">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Event</th>
|
||||||
|
<th>Location</th>
|
||||||
|
<th>Date</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let event of pastEvents">
|
||||||
|
<th><a href="{{event.linkUrl}}" title="{{event.tooltip}}">{{event.name}}</a></th>
|
||||||
|
<td>{{event.location}}</td>
|
||||||
|
<td>
|
||||||
|
<div>
|
||||||
|
{{getEventDates(event)}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user