mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
172 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e914da145b | |||
| 6550c59df5 | |||
| 032276253f | |||
| d607d0b93c | |||
| c02e4d72bc | |||
| 4d2ef0f5df | |||
| 1a4b489692 | |||
| cbd23ee0de | |||
| 5d08390584 | |||
| 9b1d3d2fce | |||
| f71eb58036 | |||
| f7a2dd88aa | |||
| 031e23b06a | |||
| 5b0980f050 | |||
| 54d09a6bc0 | |||
| 782f678236 | |||
| 62cec95981 | |||
| 2ec696a50f | |||
| 119fbddb83 | |||
| e8e09d3455 | |||
| d7c20c8886 | |||
| 3e1fad406a | |||
| aeeb1b55d3 | |||
| 331383b39b | |||
| 0050b01b62 | |||
| e632fd1f0f | |||
| 128038e486 | |||
| 42c8533f42 | |||
| 2cc8fb0a08 | |||
| 4013db6fd2 | |||
| 95ee74a66c | |||
| a782be62e9 | |||
| dc4115d7ed | |||
| d8a18717da | |||
| dd177e2c18 | |||
| 0b8208690c | |||
| 7af1f0a265 | |||
| d0b88a6810 | |||
| fd860e3ef7 | |||
| a01ae487d7 | |||
| 3769de2caa | |||
| 835b326759 | |||
| 92cf9fbebe | |||
| 09f0254bdd | |||
| 7b9490ad64 | |||
| f0e82c8af4 | |||
| 7a47add255 | |||
| 7bc4dbd7ff | |||
| d40ac2056e | |||
| 1368f58b49 | |||
| b75e90f809 | |||
| 3919ee37cf | |||
| 37af6abb49 | |||
| 604a67f0a1 | |||
| baf0c7a11a | |||
| cd97d8793f | |||
| 3fae6637e7 | |||
| 30d1c34384 | |||
| 1384013a00 | |||
| 9327e97500 | |||
| 59a6ec4396 | |||
| 1b216297f4 | |||
| a89805c3ae | |||
| f092badb9c | |||
| d27015a43c | |||
| b9aee9070c | |||
| 5fdd8ae3ba | |||
| b2f474c4c9 | |||
| 38b042b6d0 | |||
| 48262bcb70 | |||
| fb3c0b2605 | |||
| fecd4f8eab | |||
| 9cce12cbf5 | |||
| b9aab0c87b | |||
| 123f42c744 | |||
| 64adfc9150 | |||
| 7924a2e497 | |||
| 98a1da9382 | |||
| 165aa4f164 | |||
| 4a5ad1793f | |||
| d65a42b183 | |||
| dee108b67c | |||
| 5a75721835 | |||
| ec49796807 | |||
| b4e4617807 | |||
| 812c1ba971 | |||
| 1cc1588c29 | |||
| b6d2212ce5 | |||
| a432ad65c7 | |||
| 96b3b774c3 | |||
| 365962948a | |||
| 577d97f757 | |||
| 36b16e667a | |||
| 2916ac9e1a | |||
| 4644886aaf | |||
| f5ce6cfa1e | |||
| 7ec482d9c2 | |||
| 98ba48eedf | |||
| 1aebbf8714 | |||
| 250dc40a46 | |||
| 8fb2e3b71f | |||
| 626980f3eb | |||
| 5400a3d88a | |||
| e07014f3d9 | |||
| 85b889e6fb | |||
| 881d4696a2 | |||
| 07185f4ed1 | |||
| 0778e6f7d7 | |||
| 25f8305195 | |||
| b51d111d72 | |||
| 72cdd1403b | |||
| c1fc425cd1 | |||
| e62c98f2aa | |||
| 47c02caa29 | |||
| 94c9e47dbe | |||
| 6b7fffcbeb | |||
| 641f27a9c9 | |||
| ab63ed3448 | |||
| b89ee0819b | |||
| 553c8d1366 | |||
| f398717502 | |||
| c9abf34d88 | |||
| 96d2c58647 | |||
| 49ca5bb1d8 | |||
| f6c2934f5b | |||
| 1169fae366 | |||
| 522b845bec | |||
| 2fa2f7d34a | |||
| af19c440e2 | |||
| 9f7157e560 | |||
| 30ddeabca6 | |||
| 4a91d9b0a0 | |||
| fcc6528159 | |||
| dc0cac235f | |||
| de1e35b944 | |||
| a38ae0aefa | |||
| fe3e4d6865 | |||
| 6cb7c3ebe3 | |||
| 966343bb92 | |||
| 7e93513425 | |||
| 7c7a1a1724 | |||
| 62217efb7e | |||
| 1fa14e0c84 | |||
| a71eb4c18e | |||
| ffd62b9f11 | |||
| e545dd8130 | |||
| 4883b7d9c7 | |||
| 8eb1f76341 | |||
| 37bbd97d6e | |||
| 9e9e82d7c7 | |||
| e12dfa535b | |||
| fa7c7afbe9 | |||
| a4aa9b3c28 | |||
| 40f27a31d2 | |||
| 94bfcdd9de | |||
| f0a8303abe | |||
| abd1bc8039 | |||
| 7316e72ec5 | |||
| ac2f262e9a | |||
| f2fa67eb0b | |||
| 9bee561956 | |||
| fce521e4d2 | |||
| ade305757b | |||
| ed67a074ce | |||
| 43eb934e6b | |||
| d2ae96f742 | |||
| 5626b34264 | |||
| 1671212a55 | |||
| 100091ebf0 | |||
| b7ff6f06de | |||
| 6b75263303 | |||
| caeb162513 |
@@ -36,7 +36,7 @@ var_4_win: &cache_key_win_fallback v6-angular-win-node-14-{{ checksum "month.txt
|
||||
|
||||
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
|
||||
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
|
||||
var_5: &components_repo_unit_tests_cache_key v1-angular-components-{{ checksum "month.txt" }}-a931de54a786597b34259e461c2cf3ab6edc590a
|
||||
var_5: &components_repo_unit_tests_cache_key v1-angular-components-{{ checksum "month.txt" }}-e0b76ed029939a7bb8246410b0170cfdbf35da25
|
||||
var_6: &components_repo_unit_tests_cache_key_fallback v1-angular-components-{{ checksum "month.txt" }}
|
||||
|
||||
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages`.
|
||||
@@ -786,10 +786,9 @@ workflows:
|
||||
# since the publishing script expects the legacy outputs layout.
|
||||
- build-npm-packages
|
||||
- legacy-unit-tests-saucelabs
|
||||
# TODO(devversion): re-enable once the components repo has accounted for the `ng_module` devmode target changes.
|
||||
#- components-repo-unit-tests:
|
||||
# requires:
|
||||
# - build-npm-packages
|
||||
- components-repo-unit-tests:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- test_zonejs:
|
||||
requires:
|
||||
- setup
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo"
|
||||
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
|
||||
setPublicVar COMPONENTS_REPO_BRANCH "master"
|
||||
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
|
||||
setPublicVar COMPONENTS_REPO_COMMIT "a931de54a786597b34259e461c2cf3ab6edc590a"
|
||||
setPublicVar COMPONENTS_REPO_COMMIT "e0b76ed029939a7bb8246410b0170cfdbf35da25"
|
||||
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -9,6 +9,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: angular/dev-infra/github-actions/commit-message-based-labels@744a864cfd77bfe280ac9337041a512cca2daaf9
|
||||
- uses: angular/dev-infra/github-actions/commit-message-based-labels@2c23378f310c42b482d246886ed2aab1550dc650
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -10,6 +10,6 @@ jobs:
|
||||
if: github.repository == 'angular/angular'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/feature-request@744a864cfd77bfe280ac9337041a512cca2daaf9
|
||||
- uses: angular/dev-infra/github-actions/feature-request@2c23378f310c42b482d246886ed2aab1550dc650
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -10,6 +10,6 @@ jobs:
|
||||
if: github.repository == 'angular/angular'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/lock-closed@744a864cfd77bfe280ac9337041a512cca2daaf9
|
||||
- uses: angular/dev-infra/github-actions/lock-closed@2c23378f310c42b482d246886ed2aab1550dc650
|
||||
with:
|
||||
lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
name: OpenSSF Scorecard
|
||||
on:
|
||||
branch_protection_rule:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0'
|
||||
push:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
name: Scorecards analysis
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload the results to code-scanning dashboard.
|
||||
security-events: write
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: 'Checkout code'
|
||||
uses: actions/checkout@230611dbd0eb52da1e1f4f7bc8bb0c3a339fc8b7
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Run analysis'
|
||||
uses: ossf/scorecard-action@3662744abc9b750123cb8965fef31e3802d52da5
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_results: true
|
||||
|
||||
# Upload the results as artifacts.
|
||||
- name: 'Upload artifact'
|
||||
uses: actions/upload-artifact@2244c8200304ec9588bf9399eac622d9fadc28c4
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: 'Upload to code-scanning'
|
||||
uses: github/codeql-action/upload-sarif@040feefecff4ae0fc15c110822a5f96516b0629e
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
+7
-1
@@ -30,7 +30,13 @@ export const release: ReleaseConfig = {
|
||||
return buildTargetPackages('dist/release-output', false, 'Release', /* isRelease */ true);
|
||||
},
|
||||
releaseNotes: {
|
||||
hiddenScopes: ['aio', 'dev-infra', 'docs-infra', 'zone.js'],
|
||||
hiddenScopes: [
|
||||
'aio',
|
||||
'dev-infra',
|
||||
'docs-infra',
|
||||
'zone.js',
|
||||
'devtools',
|
||||
],
|
||||
},
|
||||
releasePrLabels: ['comp: build & ci', 'action: merge', 'PullApprove: disable'],
|
||||
};
|
||||
|
||||
@@ -1271,6 +1271,7 @@ groups:
|
||||
'docs/NAMING.md',
|
||||
'aio/content/guide/angular-package-format.md',
|
||||
'aio/content/errors/*.md',
|
||||
'aio/content/extended-diagnostics/*.md',
|
||||
'aio/content/guide/glossary.md',
|
||||
'aio/content/guide/styleguide.md',
|
||||
'aio/content/examples/errors/**',
|
||||
|
||||
+137
-61
@@ -1,3 +1,140 @@
|
||||
<a name="13.2.4"></a>
|
||||
# 13.2.4 (2022-02-23)
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [1a4b489692](https://github.com/angular/angular/commit/1a4b489692aebd185a96af78996d13c47caf4478) | perf | improve algorithm to balance animation namespaces ([#45113](https://github.com/angular/angular/pull/45113)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [cbd23ee0de](https://github.com/angular/angular/commit/cbd23ee0def8e79b185d59d0713e5eeb97b20e1f) | perf | make `IterableDiffers` and `KeyValueDiffers` tree-shakable ([#45094](https://github.com/angular/angular/pull/45094)) ([#45115](https://github.com/angular/angular/pull/45115)) |
|
||||
## Special Thanks
|
||||
Alan Cohen, AlirezaEbrahimkhani, Andrew Kushnir, Daniele Maltese, David Wolf, JoostK, Paul Gschwendtner, dario-piotrowicz and manuelkroiss
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.2.3"></a>
|
||||
# 13.2.3 (2022-02-16)
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [0050b01b62](https://github.com/angular/angular/commit/0050b01b62adf8e0021c1878edb696c339c5e1bc) | perf | made errors in the animations package tree shakeable ([#45079](https://github.com/angular/angular/pull/45079)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [09f0254bdd](https://github.com/angular/angular/commit/09f0254bddbe10bb9e265c8aee7d3c911e479e0e) | perf | chain element start/end instructions ([#44994](https://github.com/angular/angular/pull/44994)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [92cf9fbebe](https://github.com/angular/angular/commit/92cf9fbebed75ddc0eb598e57556b4fbe8c24009) | fix | `ViewContainerRef.createComponent` should consult module injector when custom one is provided ([#44966](https://github.com/angular/angular/pull/44966)) |
|
||||
## Special Thanks
|
||||
AlirezaEbrahimkhani, Amer Yousuf, Andrew Kushnir, Aristeidis Bampakos, Dario Piotrowicz, Esteban Gehring, Jessica Janiuk, JiaLiPassion, Kristiyan Kostadinov, Mina Hosseini Moghadam, Patrick Cameron, Srdjan Milic, Yousaf Nawaz, dario-piotrowicz, markostanimirovic, mgechev and zuckjet
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.2.2"></a>
|
||||
# 13.2.2 (2022-02-08)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [37af6abb49](https://github.com/angular/angular/commit/37af6abb495a351052389cb095bfbea0260f46c5) | fix | allow banana-in-a-box bindings to end with non-null assertion ([#37809](https://github.com/angular/angular/pull/37809)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [b75e90f809](https://github.com/angular/angular/commit/b75e90f8096394eeef85fa7e667889b7b59a35ed) | fix | incorrectly keeping track of ngModel with ngFor inside a form ([#40459](https://github.com/angular/angular/pull/40459)) |
|
||||
### http
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [3fae6637e7](https://github.com/angular/angular/commit/3fae6637e7d379a9dd1862555ec7e586f80be4c9) | perf | remove IE special status handling ([#44354](https://github.com/angular/angular/pull/44354)) |
|
||||
### upgrade
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [b9aab0c87b](https://github.com/angular/angular/commit/b9aab0c87bcccb61167e92c1e910630afad67648) | fix | Do not trigger duplicate navigation events from Angular Router ([#43441](https://github.com/angular/angular/pull/43441)) |
|
||||
## Special Thanks
|
||||
Alan Agius, Alan Cohen, Andrew Kushnir, Andrew Scott, Daniel Díaz, Dario Piotrowicz, Doug Parker, Jayson Acosta, Joey Perrott, JoostK, Kristiyan Kostadinov, Olivier Capuozzo, Ramzan, Shai Reznik, TANMAY SRIVASTAVA, dario-piotrowicz, iRealNirmal, jhonyeduardo, mgechev and zuckjet
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.2.1"></a>
|
||||
# 13.2.1 (2022-02-02)
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4644886aaf](https://github.com/angular/angular/commit/4644886aaf96c9da513cc7a2c2568254a11c8a4b) | perf | remove no longer needed CssKeyframes classes ([#44903](https://github.com/angular/angular/pull/44903)) ([#44919](https://github.com/angular/angular/pull/44919)) |
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [b4e4617807](https://github.com/angular/angular/commit/b4e4617807a8ef97b6423e6debfe504f86aa506c) | fix | include query parameters for open HTTP requests in `verify` ([#44917](https://github.com/angular/angular/pull/44917)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [0778e6f7d7](https://github.com/angular/angular/commit/0778e6f7d79080124c240122acd9ff5ffffc74f5) | fix | accept nullish coalescing operator for any and unknown types ([#44862](https://github.com/angular/angular/pull/44862)) |
|
||||
| [07185f4ed1](https://github.com/angular/angular/commit/07185f4ed1ac7071af62a65d8a5391ca28b03205) | fix | enable nullish coalescing check only with `strictNullChecks` ([#44862](https://github.com/angular/angular/pull/44862)) |
|
||||
| [4a5ad1793f](https://github.com/angular/angular/commit/4a5ad1793f91ef5375146b4ead9929c8a939746e) | fix | ensure casing of logical paths is preserved ([#44798](https://github.com/angular/angular/pull/44798)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [7ec482d9c2](https://github.com/angular/angular/commit/7ec482d9c2292d6370f7a2da9addf2abb8bb21bb) | fix | Add back support for namespace URIs in createElement of dom renderer ([#44914](https://github.com/angular/angular/pull/44914)) |
|
||||
| [250dc40a46](https://github.com/angular/angular/commit/250dc40a46797446bf3328a58b9b1a4d8057b1d6) | fix | flush delayed scoping queue while setting up TestBed ([#44814](https://github.com/angular/angular/pull/44814)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [1aebbf8714](https://github.com/angular/angular/commit/1aebbf8714b9cd3c3b5844065c5b64240c05b37d) | fix | ensure OnPush ancestors are marked dirty when the promise resolves ([#44886](https://github.com/angular/angular/pull/44886)) |
|
||||
| [6b7fffcbeb](https://github.com/angular/angular/commit/6b7fffcbeb08ce77aef1d83de354ca7266600c6f) | fix | Update the typed forms migration schematic to find all files. ([#44881](https://github.com/angular/angular/pull/44881)) |
|
||||
## Special Thanks
|
||||
Alan, Andrew Kushnir, Andrew Scott, Aristeidis Bampakos, Arjen, Daniel Díaz, David Shevitz, Doug Parker, Dylan Hunn, Esteban Gehring, George Kalpakas, Jessica Janiuk, JoostK, Juri Strumpflohner, Lee Robinson, Maarten Tibau, Paul Gschwendtner, Theodore Brown, arturovt, dario-piotrowicz, fru2, markostanimirovic and mgechev
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.2.0"></a>
|
||||
# 13.2.0 (2022-01-26)
|
||||
## Deprecations
|
||||
###
|
||||
- The `CachedResourceLoader` and `RESOURCE_CACHE_PROVIDER` symbols were previously necessary in some cases to test AOT-compiled components with View Engine, but they are no longer needed since Ivy.
|
||||
|
||||
- The `ComponentFactory` and `ComponentFactoryResolver` classes are deprecated. Since Ivy, there is no need to resolve Component factories. Please use other APIs where you Component classes can be used directly (without resolving their factories).
|
||||
|
||||
- Since Ivy, the `CompilerOptions.useJit` and `CompilerOptions.missingTranslation` config options are unused, passing them has no effect.
|
||||
###
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [9c11183e74](https://github.com/angular/angular/commit/9c11183e74980b12c3c5712df174e90af6f19027) | docs | deprecate `CachedResourceLoader` and `RESOURCE_CACHE_PROVIDER` symbols ([#44749](https://github.com/angular/angular/pull/44749)) |
|
||||
| [9f12e7fea4](https://github.com/angular/angular/commit/9f12e7fea434c6d33e1155994e2c228d51520744) | docs | deprecate `ComponentFactory` and `ComponentFactoryResolver` symbols ([#44749](https://github.com/angular/angular/pull/44749)) |
|
||||
| [4e95a316ce](https://github.com/angular/angular/commit/4e95a316cef35a771cd8168e3a744eb6bd7f1615) | docs | deprecate unused config options from the `CompilerOptions` interface ([#44749](https://github.com/angular/angular/pull/44749)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a4ab6d6b72](https://github.com/angular/angular/commit/a4ab6d6b72fe891f232d2446f8b7c454a82a5ba0) | feat | add support for safe calls in templates ([#44580](https://github.com/angular/angular/pull/44580)) |
|
||||
| [abd1bc8039](https://github.com/angular/angular/commit/abd1bc8039ade4324f91fd616c82e52f05bdbb70) | fix | correct spans when parsing bindings with comments ([#44785](https://github.com/angular/angular/pull/44785)) |
|
||||
| [ed67a074ce](https://github.com/angular/angular/commit/ed67a074ceed6440bf7f638512a20bbd3be8cbd4) | fix | properly compile DI factories when coverage reporting is enabled ([#44732](https://github.com/angular/angular/pull/44732)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [fa835b5a29](https://github.com/angular/angular/commit/fa835b5a297087087eac2750b662096ed5492cb6) | feat | enable extended diagnostics by default ([#44712](https://github.com/angular/angular/pull/44712)) |
|
||||
| [73424def13](https://github.com/angular/angular/commit/73424def130ae1d72036142f6f2d7bce63858c9e) | feat | provide the animations for `DirectiveMeta` ([#44630](https://github.com/angular/angular/pull/44630)) |
|
||||
| [fe3e4d6865](https://github.com/angular/angular/commit/fe3e4d6865c1e760be0511cfd757b4574a67fa5b) | fix | Handle `ng-template` with structural directive in indexer ([#44788](https://github.com/angular/angular/pull/44788)) |
|
||||
| [7316e72ec5](https://github.com/angular/angular/commit/7316e72ec5ccc475e610bf26b5da451b8ffa50a2) | fix | properly index <svg> elements when on a template ([#44785](https://github.com/angular/angular/pull/44785)) |
|
||||
| [100091ebf0](https://github.com/angular/angular/commit/100091ebf0ad480e94b5db4f183f9bfcfe8b8274) | fix | remove leftover `_extendedTemplateDiagnostics` requirements ([#44777](https://github.com/angular/angular/pull/44777)) |
|
||||
| [d2ae96f742](https://github.com/angular/angular/commit/d2ae96f742c82daa6c4d4b8afd3b0959fb9b89d1) | fix | skip `ExtendedTemplateCheckerImpl` construction if there were configuration errors ([#44778](https://github.com/angular/angular/pull/44778)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5626b34264](https://github.com/angular/angular/commit/5626b34264d8893fece74d30db4920a685f3b17f) | fix | consistently use namespace short name rather than URI ([#44766](https://github.com/angular/angular/pull/44766)) |
|
||||
| [94bfcdd9de](https://github.com/angular/angular/commit/94bfcdd9de234f7189daaa06a6c179d9697bef8d) | fix | error if NgZone.isInAngularZone is called with a noop zone ([#44800](https://github.com/angular/angular/pull/44800)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [72092ebd26](https://github.com/angular/angular/commit/72092ebd26d6670a7dd02f6db5515d2afd27c4d6) | feat | Allow a FormControl to use initial value as default. ([#44434](https://github.com/angular/angular/pull/44434)) |
|
||||
| [f7aa937cac](https://github.com/angular/angular/commit/f7aa937cac0ad315e1012770aa1da97476aced54) | fix | Make some minor fixups for forward-compatibility with typed forms. ([#44540](https://github.com/angular/angular/pull/44540)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5a4ddfd4f5](https://github.com/angular/angular/commit/5a4ddfd4f5e17bb4209528cefc3f0674298ab6e8) | feat | Allow symbol keys for `Route` `data` and `resolve` properties ([#44519](https://github.com/angular/angular/pull/44519)) |
|
||||
## Special Thanks
|
||||
Alex Rickabaugh, Andrew Kushnir, Andrew Scott, Dario Piotrowicz, Derek Cormier, Doug Parker, Douglas Parker, Dylan Hunn, George Kalpakas, Jessica Janiuk, JoostK, Kristiyan Kostadinov, Martin Probst, Oleg Postoev, Stephanie Tuerk, Tim Bowersox, Wiley Marques, Yousaf Nawaz, dario-piotrowicz, iRealNirmal, ivanwonder and shejialuo
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.1.3"></a>
|
||||
# 13.1.3 (2022-01-19)
|
||||
### animations
|
||||
@@ -17,44 +154,6 @@ Alan Agius, Andrew Kushnir, AnkitSharma-007, Daniel Díaz, Dmytro Mezhenskyi, Je
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.2.0-next.2"></a>
|
||||
# 13.2.0-next.2 (2022-01-12)
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8ba43a1a1e](https://github.com/angular/angular/commit/8ba43a1a1ea811ad25d6af4384e1cce5a53b0baf) | fix | retain triggers values for moved tracked list items ([#44578](https://github.com/angular/angular/pull/44578)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a4ab6d6b72](https://github.com/angular/angular/commit/a4ab6d6b72fe891f232d2446f8b7c454a82a5ba0) | feat | add support for safe calls in templates ([#44580](https://github.com/angular/angular/pull/44580)) |
|
||||
| [b8ed03b5ad](https://github.com/angular/angular/commit/b8ed03b5ad91a2dd62d600240e9056a360b89b90) | fix | correct spans when parsing bindings with comments ([#44678](https://github.com/angular/angular/pull/44678)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [73424def13](https://github.com/angular/angular/commit/73424def130ae1d72036142f6f2d7bce63858c9e) | feat | provide the animations for `DirectiveMeta` ([#44630](https://github.com/angular/angular/pull/44630)) |
|
||||
| [1a9121826e](https://github.com/angular/angular/commit/1a9121826ea94d98d0603d65628d4b4b08f2f70b) | fix | enable narrowing of using type guard methods ([#44447](https://github.com/angular/angular/pull/44447)) |
|
||||
| [f8af49eb75](https://github.com/angular/angular/commit/f8af49eb751cabab76c6c7327f377a163f841dc4) | fix | fix crash during type-checking of library builds ([#44587](https://github.com/angular/angular/pull/44587)) |
|
||||
| [f83fb3a5f9](https://github.com/angular/angular/commit/f83fb3a5f9b146cb5025517950100e98895304ed) | fix | handle property reads of `ThisReceiver` in the indexer ([#44678](https://github.com/angular/angular/pull/44678)) |
|
||||
| [eeaabe7fbf](https://github.com/angular/angular/commit/eeaabe7fbf7a1cf057c6b740609886415e4037eb) | fix | incorrectly interpreting $any calls with a property read ([#44657](https://github.com/angular/angular/pull/44657)) |
|
||||
| [92b23f4851](https://github.com/angular/angular/commit/92b23f48519a1adb460a431c356bcd9917fb059d) | fix | properly index <svg> elements ([#44678](https://github.com/angular/angular/pull/44678)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5ae1030462](https://github.com/angular/angular/commit/5ae1030462a97bda8357415cbfcd34301dfa6499) | feat | Add an unused symbol `AnyForUntypedForms`. ([#44467](https://github.com/angular/angular/pull/44467)) |
|
||||
| [f7aa937cac](https://github.com/angular/angular/commit/f7aa937cac0ad315e1012770aa1da97476aced54) | fix | Make some minor fixups for forward-compatibility with typed forms. ([#44540](https://github.com/angular/angular/pull/44540)) |
|
||||
### language-service
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [af2a1317cb](https://github.com/angular/angular/commit/af2a1317cbfba985233f510bbe9e49016968307c) | feat | support completions for animation ([#44630](https://github.com/angular/angular/pull/44630)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5a4ddfd4f5](https://github.com/angular/angular/commit/5a4ddfd4f5e17bb4209528cefc3f0674298ab6e8) | feat | Allow symbol keys for `Route` `data` and `resolve` properties ([#44519](https://github.com/angular/angular/pull/44519)) |
|
||||
## Special Thanks
|
||||
Abdurrahman Abu-Hijleh, Adam Plumer, Alex Rickabaugh, AlirezaEbrahimkhani, Andrew Kushnir, Andrew Scott, Borja Paz Rodríguez, Chihab Otmani, Chris Mancini, Dario Piotrowicz, Doug Parker, Dylan Hunn, George Kalpakas, Joey Perrott, JoostK, Kristiyan Kostadinov, Kyoz, Patrick Prakash, Paul Gschwendtner, Serhey Dolgushev, Yousaf Nawaz, Yuchao Wu, alkavats1, dario-piotrowicz, huangqing, ivanwonder, shejialuo, twerske, wszgrcy and zuckjet
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.1.2"></a>
|
||||
# 13.1.2 (2022-01-12)
|
||||
### animations
|
||||
@@ -82,22 +181,6 @@ Abdurrahman Abu-Hijleh, Adam Plumer, Alex Rickabaugh, AlirezaEbrahimkhani, Andre
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.2.0-next.1"></a>
|
||||
# 13.2.0-next.1 (2021-12-15)
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [38ddae1c2e](https://github.com/angular/angular/commit/38ddae1c2e0de87d5672fda30d4fbe567dab4123) | fix | don't consume instructions for animateChild ([#44357](https://github.com/angular/angular/pull/44357)) |
|
||||
| [bab7ed3541](https://github.com/angular/angular/commit/bab7ed35418173714509e06ba8b092aff28fff89) | fix | should not invoke disabled child animations ([#37724](https://github.com/angular/angular/pull/37724)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [72092ebd26](https://github.com/angular/angular/commit/72092ebd26d6670a7dd02f6db5515d2afd27c4d6) | feat | Allow a FormControl to use initial value as default. ([#44434](https://github.com/angular/angular/pull/44434)) |
|
||||
## Special Thanks
|
||||
Alex Rickabaugh, Aristeidis Bampakos, Dylan Hunn, JoostK, Kristiyan Kostadinov, Paul Gschwendtner, Spej, Yousaf Nawaz, dario-piotrowicz, faso-dev, jaybell and zuckjet
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.1.1"></a>
|
||||
# 13.1.1 (2021-12-15)
|
||||
### animations
|
||||
@@ -115,13 +198,6 @@ Aristeidis Bampakos, Dylan Hunn, George Kalpakas, JoostK, Kristiyan Kostadinov,
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.2.0-next.0"></a>
|
||||
# 13.2.0-next.0 (2021-12-09)
|
||||
|
||||
There have been no changes since the previous `13.1.0` release.
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="13.1.0"></a>
|
||||
# 13.1.0 (2021-12-09)
|
||||
## Deprecations
|
||||
|
||||
@@ -16,10 +16,10 @@ http_archive(
|
||||
# rule in `@bazel_tools` is not Windows compatible and outdated.
|
||||
http_archive(
|
||||
name = "rules_pkg",
|
||||
sha256 = "a89e203d3cf264e564fcb96b6e06dd70bc0557356eb48400ce4b5d97c2c3720d",
|
||||
sha256 = "62eeb544ff1ef41d786e329e1536c1d541bb9bcad27ae984d57f18f314018e66",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.5.1/rules_pkg-0.5.1.tar.gz",
|
||||
"https://github.com/bazelbuild/rules_pkg/releases/download/0.5.1/rules_pkg-0.5.1.tar.gz",
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.6.0/rules_pkg-0.6.0.tar.gz",
|
||||
"https://github.com/bazelbuild/rules_pkg/releases/download/0.6.0/rules_pkg-0.6.0.tar.gz",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Angular documentation project (https://angular.io)
|
||||
|
||||
Everything in this folder is part of the documentation project. This includes
|
||||
Everything in this folder is part of the documentation project. This includes:
|
||||
|
||||
* the web site for displaying the documentation
|
||||
* the dgeni configuration for converting source files to rendered files that can be viewed in the web site.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Imports
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
import {assertNotMissingOrEmpty} from './utils';
|
||||
|
||||
// Constants
|
||||
@@ -22,8 +23,8 @@ export interface BuildInfo {
|
||||
username: string;
|
||||
build_num: number;
|
||||
has_artifacts: boolean;
|
||||
outcome: string; // e.g. 'success'
|
||||
vcs_revision: string; // HEAD SHA
|
||||
outcome: string; // e.g. 'success'
|
||||
vcs_revision: string; // HEAD SHA
|
||||
// there are other fields but they are not used in this code
|
||||
}
|
||||
|
||||
@@ -31,19 +32,19 @@ export interface BuildInfo {
|
||||
* A Helper that can interact with the CircleCI API.
|
||||
*/
|
||||
export class CircleCiApi {
|
||||
|
||||
private tokenParam = `circle-token=${this.circleCiToken}`;
|
||||
|
||||
/**
|
||||
* Construct a helper that can interact with the CircleCI REST API.
|
||||
* @param githubOrg The Github organisation whose repos we want to access in CircleCI (e.g. angular).
|
||||
* @param githubOrg The Github organisation whose repos we want to access in CircleCI (e.g.
|
||||
* angular).
|
||||
* @param githubRepo The Github repo whose builds we want to access in CircleCI (e.g. angular).
|
||||
* @param circleCiToken The CircleCI API access token (secret).
|
||||
*/
|
||||
constructor(
|
||||
private githubOrg: string,
|
||||
private githubRepo: string,
|
||||
private circleCiToken: string,
|
||||
private githubOrg: string,
|
||||
private githubRepo: string,
|
||||
private circleCiToken: string,
|
||||
) {
|
||||
assertNotMissingOrEmpty('githubOrg', githubOrg);
|
||||
assertNotMissingOrEmpty('githubRepo', githubRepo);
|
||||
@@ -64,7 +65,7 @@ export class CircleCiApi {
|
||||
}
|
||||
return response.json();
|
||||
} catch (error) {
|
||||
throw new Error(`CircleCI build info request failed (${error.message})`);
|
||||
throw new Error(`CircleCI build info request failed (${(error as Error).message})`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +85,7 @@ export class CircleCiApi {
|
||||
}
|
||||
return artifact.url;
|
||||
} catch (error) {
|
||||
throw new Error(`CircleCI artifact URL request failed (${error.message})`);
|
||||
throw new Error(`CircleCI artifact URL request failed (${(error as Error).message})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {basename, resolve as resolvePath} from 'path';
|
||||
|
||||
import {SHORT_SHA_LEN} from './constants';
|
||||
|
||||
/**
|
||||
@@ -17,7 +18,8 @@ export function computeShortSha(sha: string) {
|
||||
* @param artifactPath The path to the artifact on CircleCI.
|
||||
* @returns The fully resolved location for the specified downloaded artifact.
|
||||
*/
|
||||
export function computeArtifactDownloadPath(downloadsDir: string, pr: number, sha: string, artifactPath: string) {
|
||||
export function computeArtifactDownloadPath(
|
||||
downloadsDir: string, pr: number, sha: string, artifactPath: string) {
|
||||
return resolvePath(downloadsDir, `${pr}-${computeShortSha(sha)}-${basename(artifactPath)}`);
|
||||
}
|
||||
|
||||
@@ -48,7 +50,7 @@ export function assert(value: boolean, message: string) {
|
||||
* @param name The name of the parameter.
|
||||
* @param value The value of the parameter.
|
||||
*/
|
||||
export const assertNotMissingOrEmpty = (name: string, value: string | null | undefined) => {
|
||||
export const assertNotMissingOrEmpty = (name: string, value: string|null|undefined) => {
|
||||
assert(!!value, `Missing or empty required parameter '${name}'!`);
|
||||
};
|
||||
|
||||
@@ -64,9 +66,9 @@ export const getEnvVar = (name: string, isOptional = false): string => {
|
||||
|
||||
if (!isOptional && !value) {
|
||||
try {
|
||||
throw new Error(`ERROR: Missing required environment variable '${name}'!`);
|
||||
throw new Error(`ERROR: Missing required environment variable '${name}'!`);
|
||||
} catch (error) {
|
||||
console.error(error.stack);
|
||||
console.error((error as Error).stack);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -76,7 +78,8 @@ export const getEnvVar = (name: string, isOptional = false): string => {
|
||||
|
||||
/**
|
||||
* A basic logger implementation.
|
||||
* Delegates to `console`, but prepends each message with the current date and specified scope (i.e caller).
|
||||
* Delegates to `console`, but prepends each message with the current date and specified scope (i.e
|
||||
* caller).
|
||||
*/
|
||||
export class Logger {
|
||||
private padding = ' '.repeat(20 - this.scope.length);
|
||||
@@ -87,12 +90,20 @@ export class Logger {
|
||||
*/
|
||||
constructor(private scope: string) {}
|
||||
|
||||
public error(...args: any[]) { this.callMethod('error', args); }
|
||||
public info(...args: any[]) { this.callMethod('info', args); }
|
||||
public log(...args: any[]) { this.callMethod('log', args); }
|
||||
public warn(...args: any[]) { this.callMethod('warn', args); }
|
||||
public error(...args: any[]) {
|
||||
this.callMethod('error', args);
|
||||
}
|
||||
public info(...args: any[]) {
|
||||
this.callMethod('info', args);
|
||||
}
|
||||
public log(...args: any[]) {
|
||||
this.callMethod('log', args);
|
||||
}
|
||||
public warn(...args: any[]) {
|
||||
this.callMethod('warn', args);
|
||||
}
|
||||
|
||||
private callMethod(method: 'error' | 'info' | 'log' | 'warn', args: any[]) {
|
||||
private callMethod(method: 'error'|'info'|'log'|'warn', args: any[]) {
|
||||
console[method](`[${new Date()}]`, `${this.scope}:${this.padding}`, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ import fetch from 'node-fetch';
|
||||
import {dirname} from 'path';
|
||||
import {mkdir} from 'shelljs';
|
||||
import {promisify} from 'util';
|
||||
|
||||
import {CircleCiApi} from '../common/circle-ci-api';
|
||||
import {assert, assertNotMissingOrEmpty, computeArtifactDownloadPath, Logger} from '../common/utils';
|
||||
|
||||
import {PreviewServerError} from './preview-error';
|
||||
|
||||
export interface GithubInfo {
|
||||
@@ -20,8 +22,11 @@ export interface GithubInfo {
|
||||
*/
|
||||
export class BuildRetriever {
|
||||
private logger = new Logger('BuildRetriever');
|
||||
constructor(private api: CircleCiApi, private downloadSizeLimit: number, private downloadDir: string) {
|
||||
assert(downloadSizeLimit > 0, 'Invalid parameter "downloadSizeLimit" should be a number greater than 0.');
|
||||
constructor(
|
||||
private api: CircleCiApi, private downloadSizeLimit: number, private downloadDir: string) {
|
||||
assert(
|
||||
downloadSizeLimit > 0,
|
||||
'Invalid parameter "downloadSizeLimit" should be a number greater than 0.');
|
||||
assertNotMissingOrEmpty('downloadDir', downloadDir);
|
||||
}
|
||||
|
||||
@@ -50,25 +55,29 @@ export class BuildRetriever {
|
||||
* @param artifactPath the path on CircleCI where the artifact was stored.
|
||||
* @returns A promise to the file path where the downloaded file was stored.
|
||||
*/
|
||||
public async downloadBuildArtifact(buildNum: number, pr: number, sha: string, artifactPath: string): Promise<string> {
|
||||
public async downloadBuildArtifact(
|
||||
buildNum: number, pr: number, sha: string, artifactPath: string): Promise<string> {
|
||||
try {
|
||||
const outPath = computeArtifactDownloadPath(this.downloadDir, pr, sha, artifactPath);
|
||||
const downloadExists = await new Promise(resolve => fs.exists(outPath, exists => resolve(exists)));
|
||||
const downloadExists =
|
||||
await new Promise(resolve => fs.exists(outPath, exists => resolve(exists)));
|
||||
if (!downloadExists) {
|
||||
const url = await this.api.getBuildArtifactUrl(buildNum, artifactPath);
|
||||
const response = await fetch(url, {size: this.downloadSizeLimit});
|
||||
if (response.status !== 200) {
|
||||
throw new PreviewServerError(response.status, `Error ${response.status} - ${response.statusText}`);
|
||||
throw new PreviewServerError(
|
||||
response.status, `Error ${response.status} - ${response.statusText}`);
|
||||
}
|
||||
const buffer = await response.buffer();
|
||||
mkdir('-p', dirname(outPath));
|
||||
await promisify(fs.writeFile)(outPath, buffer);
|
||||
}
|
||||
return outPath;
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
this.logger.warn(error);
|
||||
const status = (error.type === 'max-size') ? 413 : 500;
|
||||
throw new PreviewServerError(status, `CircleCI artifact download failed (${error.message || error})`);
|
||||
throw new PreviewServerError(
|
||||
status, `CircleCI artifact download failed (${error.message || error})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as express from 'express';
|
||||
|
||||
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
||||
import {respondWithError, throwRequestError} from '../../lib/preview-server/utils';
|
||||
|
||||
@@ -37,11 +38,12 @@ describe('preview-server/utils', () => {
|
||||
originalUrl: 'some.domain.com/path',
|
||||
} as express.Request;
|
||||
throwRequestError(505, 'ERROR MESSAGE', request);
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
caught = true;
|
||||
expect(error).toBeInstanceOf(PreviewServerError);
|
||||
expect(error.status).toEqual(505);
|
||||
expect(error.message).toEqual(`ERROR MESSAGE in request: POST some.domain.com/path "The request body"`);
|
||||
expect(error.message)
|
||||
.toEqual(`ERROR MESSAGE in request: POST some.domain.com/path "The request body"`);
|
||||
}
|
||||
expect(caught).toEqual(true);
|
||||
});
|
||||
|
||||
@@ -1,40 +1,36 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
"lib": [
|
||||
"es2015",
|
||||
"es2016.array.include"
|
||||
], /* Specify library files to be included in the compilation: */
|
||||
], /* Specify library files to be included in the compilation: */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "dist", /* Redirect output structure to the directory. */
|
||||
"outDir": "dist", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
"importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
"importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
"noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
"noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
"noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
"noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
"useUnknownInCatchVariables": false, /* Avoid having to cast error objects inside `catch` clauses. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
@@ -42,29 +38,26 @@
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
], /* List of folders to include type definitions from. */
|
||||
], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
"inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
"inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Other */
|
||||
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
|
||||
"newLine": "LF", /* Use the specified end of line sequence to be used when emitting files: "crlf" (windows) or "lf" (unix). */
|
||||
"pretty": true, /* Stylize errors and messages using color and context. */
|
||||
"skipLibCheck": true /* Skip type checking of all declaration files (*.d.ts). */
|
||||
"newLine": "LF", /* Use the specified end of line sequence to be used when emitting files: "crlf" (windows) or "lf" (unix). */
|
||||
"pretty": true, /* Stylize errors and messages using color and context. */
|
||||
"skipLibCheck": true /* Skip type checking of all declaration files (*.d.ts). */
|
||||
},
|
||||
"include": [
|
||||
"lib/**/*",
|
||||
"test/**/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ rm -rf .angular/cache
|
||||
To clear the cache on Windows:
|
||||
|
||||
```bash
|
||||
rmdir /s /q .angular/cache
|
||||
rmdir /s /q .angular\cache
|
||||
```
|
||||
|
||||
For more information, see [rm command](https://man7.org/linux/man-pages/man1/rm.1.html) and [rmdir command](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/rmdir).
|
||||
@@ -66,10 +66,9 @@ A single workspace configuration file, `angular.json`, is created at the top lev
|
||||
This is where you can set per-project defaults for CLI command options, and specify configurations to use when the CLI builds a project for different targets.
|
||||
|
||||
The [ng config](cli/config) command lets you set and retrieve configuration values from the command line, or you can edit the `angular.json` file directly.
|
||||
Note that option names in the configuration file must use [camelCase](guide/glossary#case-types), while option names supplied to commands can use either camelCase or dash-case.
|
||||
Note that option names in the configuration file must use [camelCase](guide/glossary#case-types), while option names supplied to commands must be dash-case.
|
||||
|
||||
* See more about [Workspace Configuration](guide/workspace-config).
|
||||
* See the [complete schema](https://github.com/angular/angular-cli/wiki/angular-workspace) for `angular.json`.
|
||||
|
||||
## CLI command-language syntax
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
"plugin:@angular-eslint/template/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/template/accessibility-alt-text": "off", // TODO: Fix the code violating this rule and enable it.
|
||||
"@angular-eslint/template/accessibility-elements-content": "off", // TODO: Fix the code violating this rule and enable it.
|
||||
"@angular-eslint/template/accessibility-label-has-associated-control": "off", // TODO: Fix the code violating this rule and enable it.
|
||||
"@angular-eslint/template/accessibility-alt-text": "error",
|
||||
"@angular-eslint/template/accessibility-elements-content": "error",
|
||||
"@angular-eslint/template/accessibility-label-has-associated-control": "error",
|
||||
"@angular-eslint/template/accessibility-table-scope": "error",
|
||||
"@angular-eslint/template/accessibility-valid-aria": "error",
|
||||
"@angular-eslint/template/click-events-have-key-events": "off", // TODO: Fix the code violating this rule and enable it.
|
||||
"@angular-eslint/template/click-events-have-key-events": "error",
|
||||
"@angular-eslint/template/eqeqeq": "off",
|
||||
"@angular-eslint/template/mouse-events-have-key-events": "error",
|
||||
"@angular-eslint/template/no-autofocus": "error",
|
||||
|
||||
@@ -38,11 +38,11 @@
|
||||
<p></p>
|
||||
<div>
|
||||
<!-- #docregion event-binding -->
|
||||
<button (click)="toggleImage()">
|
||||
<button type="button" (click)="toggleImage()">
|
||||
<!-- #enddocregion event-binding -->
|
||||
Image Toggle #1</button>
|
||||
<!-- #docregion event-binding -->
|
||||
<button (click)="toggleImage($event)">
|
||||
<button type="button" (click)="toggleImage($event)">
|
||||
<!-- #enddocregion event-binding -->
|
||||
Image Toggle #2</button>
|
||||
<p>Image toggle event type was {{eventType}}</p>
|
||||
@@ -51,7 +51,7 @@
|
||||
<p></p>
|
||||
<div *ngIf="showImage">
|
||||
<!-- #docregion src -->
|
||||
<img [src]="movie.imageurl">
|
||||
<img [src]="movie.imageurl" [alt]="movie.title">
|
||||
<!-- #enddocregion src -->
|
||||
</div>
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<button (click)="toggleImage()">
|
||||
<button type="button" (click)="toggleImage()">
|
||||
{{showImage ? "Hide" : "Show"}} Poster
|
||||
</button>
|
||||
</th>
|
||||
@@ -61,6 +61,7 @@
|
||||
[style.height.px]="50"
|
||||
[style.margin.px]="2"
|
||||
[src]="movie.imageurl"
|
||||
[alt]="movie.title"
|
||||
[title]="movie.title">
|
||||
</td>
|
||||
<td>{{movie.title}}</td>
|
||||
|
||||
@@ -8,6 +8,7 @@ import * as auto from './auto.po';
|
||||
import * as filterStagger from './filter-stagger.po';
|
||||
import * as heroGroups from './hero-groups';
|
||||
import { getLinkById, sleepFor } from './util';
|
||||
import { getComponentSection, getToggleButton } from './querying.po';
|
||||
|
||||
describe('Animation Tests', () => {
|
||||
const openCloseHref = getLinkById('open-close');
|
||||
@@ -17,6 +18,7 @@ describe('Animation Tests', () => {
|
||||
const autoHref = getLinkById('auto');
|
||||
const filterHref = getLinkById('heroes');
|
||||
const heroGroupsHref = getLinkById('hero-groups');
|
||||
const queryingHref = getLinkById('querying');
|
||||
|
||||
beforeAll(() => browser.get(''));
|
||||
|
||||
@@ -167,7 +169,7 @@ describe('Animation Tests', () => {
|
||||
describe('Auto Calculation Component', () => {
|
||||
beforeAll(async () => {
|
||||
await autoHref.click();
|
||||
await sleepFor(0);
|
||||
await sleepFor(300);
|
||||
});
|
||||
|
||||
it('should attach a shrinkOut trigger to the list of items', async () => {
|
||||
@@ -218,7 +220,7 @@ describe('Animation Tests', () => {
|
||||
describe('Hero Groups Component', () => {
|
||||
beforeAll(async () => {
|
||||
await heroGroupsHref.click();
|
||||
await sleepFor(300);
|
||||
await sleepFor(400);
|
||||
});
|
||||
|
||||
it('should attach a flyInOut trigger to the list of items', async () => {
|
||||
@@ -242,4 +244,49 @@ describe('Animation Tests', () => {
|
||||
await browser.wait(async () => await heroesList.count() < total, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Querying Component', () => {
|
||||
const queryingAnimationDuration = 2500;
|
||||
|
||||
beforeAll(async () => {
|
||||
await queryingHref.click();
|
||||
await sleepFor(queryingAnimationDuration);
|
||||
});
|
||||
|
||||
it('should toggle the section', async () => {
|
||||
const toggleButton = getToggleButton();
|
||||
const section = getComponentSection();
|
||||
|
||||
expect(await section.isPresent()).toBe(true);
|
||||
|
||||
// toggling off
|
||||
await toggleButton.click();
|
||||
await sleepFor(queryingAnimationDuration);
|
||||
expect(await section.isPresent()).toBe(false);
|
||||
|
||||
// toggling on
|
||||
await toggleButton.click();
|
||||
await sleepFor(queryingAnimationDuration);
|
||||
expect(await section.isPresent()).toBe(true);
|
||||
await sleepFor(queryingAnimationDuration);
|
||||
});
|
||||
|
||||
it(`should disable the button for the animation's duration`, async () => {
|
||||
const toggleButton = getToggleButton();
|
||||
expect(await toggleButton.isEnabled()).toBe(true);
|
||||
|
||||
// toggling off
|
||||
await toggleButton.click();
|
||||
expect(await toggleButton.isEnabled()).toBe(false);
|
||||
await sleepFor(queryingAnimationDuration);
|
||||
expect(await toggleButton.isEnabled()).toBe(true);
|
||||
|
||||
// toggling on
|
||||
await toggleButton.click();
|
||||
expect(await toggleButton.isEnabled()).toBe(false);
|
||||
await sleepFor(queryingAnimationDuration);
|
||||
expect(await toggleButton.isEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { by } from 'protractor';
|
||||
import { locate } from './util';
|
||||
|
||||
export function getComponent() {
|
||||
return by.css('app-querying');
|
||||
}
|
||||
|
||||
export function getToggleButton() {
|
||||
const toggleButton = () => by.className('toggle');
|
||||
return locate(getComponent(), toggleButton());
|
||||
}
|
||||
|
||||
export function getComponentSection() {
|
||||
const findSection = () => by.css('section');
|
||||
return locate(getComponent(), findSection());
|
||||
}
|
||||
@@ -39,11 +39,10 @@ export const slideInAnimation =
|
||||
]),
|
||||
query(':enter', [
|
||||
animate('300ms ease-out', style({ left: '0%' }))
|
||||
])
|
||||
]),
|
||||
]),
|
||||
query(':enter', animateChild()),
|
||||
]),
|
||||
transition('* <=> FilterPage', [
|
||||
transition('* <=> *', [
|
||||
style({ position: 'relative' }),
|
||||
query(':enter, :leave', [
|
||||
style({
|
||||
@@ -59,13 +58,13 @@ export const slideInAnimation =
|
||||
query(':leave', animateChild()),
|
||||
group([
|
||||
query(':leave', [
|
||||
animate('200ms ease-out', style({ left: '100%' }))
|
||||
animate('200ms ease-out', style({ left: '100%', opacity: 0 }))
|
||||
]),
|
||||
query(':enter', [
|
||||
animate('300ms ease-out', style({ left: '0%' }))
|
||||
])
|
||||
]),
|
||||
query('@*', animateChild())
|
||||
]),
|
||||
query(':enter', animateChild()),
|
||||
])
|
||||
// #enddocregion query
|
||||
]);
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
nav a {
|
||||
padding: .7rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: .3rem;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<a id="heroes" routerLink="/heroes" routerLinkActive="active">Filter/Stagger</a>
|
||||
<a id="hero-groups" routerLink="/hero-groups" routerLinkActive="active">Hero Groups</a>
|
||||
<a id="insert-remove" routerLink="/insert-remove" routerLinkActive="active">Insert/Remove</a>
|
||||
|
||||
<a id="querying" routerLink="/querying" routerLinkActive="active">Querying</a>
|
||||
</nav>
|
||||
|
||||
<!-- #docregion route-animations-outlet -->
|
||||
|
||||
@@ -20,6 +20,7 @@ import { HeroListAutoComponent } from './hero-list-auto.component';
|
||||
import { HomeComponent } from './home.component';
|
||||
import { AboutComponent } from './about.component';
|
||||
import { InsertRemoveComponent } from './insert-remove.component';
|
||||
import { QueryingComponent } from './querying.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@@ -28,17 +29,61 @@ import { InsertRemoveComponent } from './insert-remove.component';
|
||||
BrowserAnimationsModule,
|
||||
RouterModule.forRoot([
|
||||
{ path: '', pathMatch: 'full', redirectTo: '/enter-leave' },
|
||||
{ path: 'open-close', component: OpenClosePageComponent },
|
||||
{ path: 'status', component: StatusSliderPageComponent },
|
||||
{ path: 'toggle', component: ToggleAnimationsPageComponent },
|
||||
{ path: 'heroes', component: HeroListPageComponent,
|
||||
data: { animation: 'FilterPage' } },
|
||||
{ path: 'hero-groups', component: HeroListGroupPageComponent },
|
||||
{ path: 'enter-leave', component: HeroListEnterLeavePageComponent },
|
||||
{ path: 'auto', component: HeroListAutoCalcPageComponent },
|
||||
{ path: 'insert-remove', component: InsertRemoveComponent},
|
||||
{ path: 'home', component: HomeComponent, data: { animation: 'HomePage' } },
|
||||
{ path: 'about', component: AboutComponent, data: { animation: 'AboutPage' } },
|
||||
{
|
||||
path: 'open-close',
|
||||
component: OpenClosePageComponent,
|
||||
data: { animation: 'openClosePage' }
|
||||
},
|
||||
{
|
||||
path: 'status',
|
||||
component: StatusSliderPageComponent,
|
||||
data: { animation: 'statusPage' }
|
||||
},
|
||||
{
|
||||
path: 'toggle',
|
||||
component: ToggleAnimationsPageComponent,
|
||||
data: { animation: 'togglePage' }
|
||||
},
|
||||
{
|
||||
path: 'heroes',
|
||||
component: HeroListPageComponent,
|
||||
data: { animation: 'filterPage' }
|
||||
},
|
||||
{
|
||||
path: 'hero-groups',
|
||||
component: HeroListGroupPageComponent,
|
||||
data: { animation: 'heroGroupPage' }
|
||||
},
|
||||
{
|
||||
path: 'enter-leave',
|
||||
component: HeroListEnterLeavePageComponent,
|
||||
data: { animation: 'enterLeavePage' }
|
||||
},
|
||||
{
|
||||
path: 'auto',
|
||||
component: HeroListAutoCalcPageComponent,
|
||||
data: { animation: 'autoPage' }
|
||||
},
|
||||
{
|
||||
path: 'insert-remove',
|
||||
component: InsertRemoveComponent,
|
||||
data: { animation: 'insertRemovePage' }
|
||||
},
|
||||
{
|
||||
path: 'querying',
|
||||
component: QueryingComponent,
|
||||
data: { animation: 'queryingPage' }
|
||||
},
|
||||
{
|
||||
path: 'home',
|
||||
component: HomeComponent,
|
||||
data: { animation: 'HomePage' }
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutComponent,
|
||||
data: { animation: 'AboutPage' }
|
||||
},
|
||||
])
|
||||
],
|
||||
// #enddocregion route-animation-data
|
||||
@@ -59,6 +104,7 @@ import { InsertRemoveComponent } from './insert-remove.component';
|
||||
HeroListAutoComponent,
|
||||
HomeComponent,
|
||||
InsertRemoveComponent,
|
||||
QueryingComponent,
|
||||
AboutComponent
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<ul class="heroes">
|
||||
<li *ngFor="let hero of heroes"
|
||||
[@shrinkOut]="'in'" (click)="removeHero(hero.id)">
|
||||
<div class="inner">
|
||||
[@shrinkOut]="'in'">
|
||||
<button class="inner" type="button" (click)="removeHero(hero.id)">
|
||||
<span class="badge">{{ hero.id }}</span>
|
||||
<span>{{ hero.name }}</span>
|
||||
</div>
|
||||
<span class="name">{{ hero.name }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
@@ -19,11 +19,11 @@ import { Hero } from './hero';
|
||||
template: `
|
||||
<ul class="heroes">
|
||||
<li *ngFor="let hero of heroes"
|
||||
[@flyInOut]="'in'" (click)="removeHero(hero.id)">
|
||||
<div class="inner">
|
||||
[@flyInOut]="'in'">
|
||||
<button class="inner" type="button" (click)="removeHero(hero.id)">
|
||||
<span class="badge">{{ hero.id }}</span>
|
||||
<span>{{ hero.name }}</span>
|
||||
</div>
|
||||
<span class="name">{{ hero.name }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
|
||||
@@ -19,12 +19,11 @@ import { Hero } from './hero';
|
||||
selector: 'app-hero-list-groups',
|
||||
template: `
|
||||
<ul class="heroes">
|
||||
<li *ngFor="let hero of heroes"
|
||||
[@flyInOut]="'in'" (click)="removeHero(hero.id)">
|
||||
<div class="inner">
|
||||
<li *ngFor="let hero of heroes" [@flyInOut]="'in'">
|
||||
<button class="inner" type="button" (click)="removeHero(hero.id)">
|
||||
<span class="badge">{{ hero.id }}</span>
|
||||
<span>{{ hero.name }}</span>
|
||||
</div>
|
||||
<span class="name">{{ hero.name }}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
`,
|
||||
@@ -33,7 +32,7 @@ import { Hero } from './hero';
|
||||
animations: [
|
||||
trigger('flyInOut', [
|
||||
state('in', style({
|
||||
width: 120,
|
||||
width: '*',
|
||||
transform: 'translateX(0)', opacity: 1
|
||||
})),
|
||||
transition(':enter', [
|
||||
@@ -41,7 +40,7 @@ import { Hero } from './hero';
|
||||
group([
|
||||
animate('0.3s 0.1s ease', style({
|
||||
transform: 'translateX(0)',
|
||||
width: 120
|
||||
width: '*'
|
||||
})),
|
||||
animate('0.3s ease', style({
|
||||
opacity: 1
|
||||
|
||||
@@ -1,25 +1,56 @@
|
||||
.heroes {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
width: 15em;
|
||||
}
|
||||
|
||||
.heroes li {
|
||||
overflow:hidden;
|
||||
margin: .5em 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.heroes li > .inner {
|
||||
cursor: pointer;
|
||||
.heroes .inner {
|
||||
flex: 1;
|
||||
background-color: #EEE;
|
||||
padding: .3rem 0;
|
||||
height: 1.6rem;
|
||||
margin: .5em;
|
||||
padding: 0;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.heroes li:hover > .inner {
|
||||
color: black;
|
||||
background-color: #DDD;
|
||||
transform: translateX(.1em);
|
||||
.heroes button.inner {
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.heroes button.inner:hover {
|
||||
color: #2c3a41;
|
||||
background-color: #e6e6e6;
|
||||
left: .1em;
|
||||
}
|
||||
|
||||
.heroes button.inner:active {
|
||||
background-color: #525252;
|
||||
color: #fafafa;
|
||||
}
|
||||
|
||||
.heroes button.inner.selected {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.heroes button.inner.selected:hover {
|
||||
background-color: #505050;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.heroes button.inner.selected:active {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.heroes .badge {
|
||||
@@ -27,17 +58,17 @@
|
||||
font-size: small;
|
||||
color: white;
|
||||
padding: 0.8em 0.7em 0 0.7em;
|
||||
background-color: #3d5157;
|
||||
position: relative;
|
||||
left: -1px;
|
||||
top: -4px;
|
||||
height: 1.8em;
|
||||
min-width: 16px;
|
||||
text-align: right;
|
||||
background-color: #405061;
|
||||
line-height: 1em;
|
||||
margin-right: .8em;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.heroes .name {
|
||||
min-width: max-content;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
padding-bottom: .5rem;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<li *ngFor="let hero of heroes" class="hero">
|
||||
<div class="inner">
|
||||
<span class="badge">{{ hero.id }}</span>
|
||||
<span>{{ hero.name }}</span>
|
||||
<span class="name">{{ hero.name }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<h2>Insert/Remove</h2>
|
||||
|
||||
<nav>
|
||||
<button (click)="toggle()">Toggle Insert/Remove</button>
|
||||
<button type="button" (click)="toggle()">Toggle Insert/Remove</button>
|
||||
</nav>
|
||||
|
||||
<!-- #docregion insert-remove-->
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- #docplaster -->
|
||||
<!-- #docregion trigger -->
|
||||
<nav>
|
||||
<button (click)="toggle()">Toggle Open/Close</button>
|
||||
<button type="button" (click)="toggle()">Toggle Open/Close</button>
|
||||
</nav>
|
||||
|
||||
<div [@openClose]="isOpen ? 'open' : 'closed'" class="open-close-container">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- #docplaster -->
|
||||
<nav>
|
||||
<button (click)="toggle()">Toggle Boolean/Close</button>
|
||||
<button type="button" (click)="toggle()">Toggle Boolean/Close</button>
|
||||
</nav>
|
||||
|
||||
<!-- #docregion trigger-boolean -->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- #docplaster -->
|
||||
<nav>
|
||||
<button (click)="toggle()">Toggle Open/Close</button>
|
||||
<button type="button" (click)="toggle()">Toggle Open/Close</button>
|
||||
</nav>
|
||||
|
||||
<!-- #docregion callbacks -->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<nav>
|
||||
<button (click)="toggleAnimations()">Toggle Animations</button>
|
||||
<button (click)="toggle()">Toggle Open/Closed</button>
|
||||
<button type="button" (click)="toggleAnimations()">Toggle Animations</button>
|
||||
<button type="button" (click)="toggle()">Toggle Open/Closed</button>
|
||||
</nav>
|
||||
<!-- #docregion toggle-animation -->
|
||||
<div [@.disabled]="isDisabled">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<button (click)="toggle()">Toggle Open/Close</button>
|
||||
<button type="button" (click)="toggle()">Toggle Open/Close</button>
|
||||
|
||||
<div [@openClose]="isOpen ? 'open' : 'closed'"
|
||||
(@openClose.start)="onAnimationEvent($event)"
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
section {
|
||||
border: 1px solid black;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
section > * {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.hero {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
color: black;
|
||||
background-color: #DDD;
|
||||
}
|
||||
|
||||
.hero .badge {
|
||||
display: inline-block;
|
||||
font-size: small;
|
||||
color: white;
|
||||
padding: 0.5rem;
|
||||
background-color: #3d5157;
|
||||
margin-right: .8em;
|
||||
border-radius: 4px 0 0 4px;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.hero .name {
|
||||
height: min-content;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
Component,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
trigger,
|
||||
style,
|
||||
animate,
|
||||
transition,
|
||||
group,
|
||||
query,
|
||||
animateChild,
|
||||
keyframes
|
||||
} from '@angular/animations';
|
||||
|
||||
import { HEROES } from './mock-heroes';
|
||||
|
||||
@Component({
|
||||
selector: 'app-querying',
|
||||
template: `
|
||||
<nav>
|
||||
<button class="toggle" (click)="show = !show" [disabled]="toggleDisabled">Toggle View</button>
|
||||
</nav>
|
||||
<section *ngIf="show" @query (@query.start)="toggleDisabled = true" (@query.done)="toggleDisabled = false">
|
||||
<p>I am a simple child element</p>
|
||||
<p *ngIf="show">I am a child element that enters and leaves with its parent</p>
|
||||
<p @animateMe>I am a child element with an animation trigger</p>
|
||||
<div class="hero">
|
||||
<span class="badge">{{ hero.id }}</span>
|
||||
<span class="name">{{ hero.name }} <small>(heroes are always animated!)</small></span>
|
||||
</div>
|
||||
</section>
|
||||
`,
|
||||
styleUrls: ['./querying.component.css'],
|
||||
animations: [
|
||||
trigger('query', [
|
||||
transition(':enter', [
|
||||
style({ height: 0 }),
|
||||
group([
|
||||
animate(500, style({ height: '*' })),
|
||||
query(':enter', [
|
||||
style({ opacity: 0, transform: 'scale(0)'}),
|
||||
animate(2000, style({ opacity: 1, transform: 'scale(1)' }))
|
||||
]),
|
||||
query('.hero', [
|
||||
style({ transform: 'translateX(-100%)'}),
|
||||
animate('.7s 500ms ease-in', style({ transform: 'translateX(0)' }))
|
||||
]),
|
||||
]),
|
||||
query('@animateMe', animateChild()),
|
||||
]),
|
||||
transition(':leave', [
|
||||
style({ height: '*' }),
|
||||
query('@animateMe', animateChild()),
|
||||
group([
|
||||
animate('500ms 500ms', style({ height: '0', padding: '0' })),
|
||||
query(':leave', [
|
||||
style({ opacity: 1, transform: 'scale(1)'}),
|
||||
animate('1s', style({ opacity: 0, transform: 'scale(0)' }))
|
||||
]),
|
||||
query('.hero', [
|
||||
style({ transform: 'translateX(0)'}),
|
||||
animate('.7s ease-out', style({ transform: 'translateX(-100%)' }))
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
trigger('animateMe', [
|
||||
transition('* <=> *', animate('500ms cubic-bezier(.68,-0.73,.26,1.65)', keyframes([
|
||||
style({ backgroundColor: "transparent", color: '*', offset: 0 }),
|
||||
style({ backgroundColor: "blue", color: 'white', offset: 0.2 }),
|
||||
style({ backgroundColor: "transparent", color: '*', offset: 1 })
|
||||
])))
|
||||
]),
|
||||
]
|
||||
})
|
||||
export class QueryingComponent {
|
||||
toggleDisabled = false;
|
||||
show = true;
|
||||
hero = HEROES[0];
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<nav>
|
||||
<button (click)="toggle()">Toggle Status</button>
|
||||
<button type="button" (click)="toggle()">Toggle Status</button>
|
||||
</nav>
|
||||
|
||||
<div [@slideStatus]="status" class="box">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<!--#docregion binding -->
|
||||
<li>{{hero.name}}</li>
|
||||
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
|
||||
<li (click)="selectHero(hero)"></li>
|
||||
<button type="button" (click)="selectHero(hero)">
|
||||
{{hero.name}}
|
||||
</button>
|
||||
<!--#enddocregion binding -->
|
||||
|
||||
<!--#docregion structural -->
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
|
||||
<p><em>Select a hero from the list to see details.</em></p>
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes" (click)="selectHero(hero)">
|
||||
{{hero.name}}
|
||||
<li *ngFor="let hero of heroes">
|
||||
<button type="button" (click)="selectHero(hero)">
|
||||
{{hero.name}}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -4,13 +4,21 @@ ul {
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
padding: 1rem;
|
||||
background-color: aliceblue;
|
||||
border: 1px solid #444;
|
||||
margin-bottom: .5rem;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
li button {
|
||||
background-color: aliceblue;
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
li button:hover, li button:active {
|
||||
background-color: #444;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<div>
|
||||
<!-- #docregion attrib-binding-aria -->
|
||||
<!-- create and set an aria attribute for assistive technology -->
|
||||
<button [attr.aria-label]="actionName">{{actionName}} with Aria</button>
|
||||
<button type="button" [attr.aria-label]="actionName">{{actionName}} with Aria</button>
|
||||
<!-- #enddocregion attrib-binding-aria -->
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<h2>Button disabled state bound to isUnchanged property</h2>
|
||||
<!-- #docregion disabled-button -->
|
||||
<!-- Bind button disabled state to `isUnchanged` property -->
|
||||
<button [disabled]="isUnchanged">Save</button>
|
||||
<button type="button" [disabled]="isUnchanged">Save</button>
|
||||
<!-- #enddocregion disabled-button -->
|
||||
</div>
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
<label>HTML Attribute Initializes to "Sarah":
|
||||
<input type="text" value="Sarah" #bindingInput></label>
|
||||
<div>
|
||||
<button (click)="getHTMLAttributeValue()">Get HTML attribute value</button> Won't change.
|
||||
<button type="button" (click)="getHTMLAttributeValue()">Get HTML attribute value</button> Won't change.
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button (click)="getDOMPropertyValue()">Get DOM property value</button> Changeable. Angular works with these.
|
||||
<button type="button" (click)="getDOMPropertyValue()">Get DOM property value</button> Changeable. Angular works with these.
|
||||
</div>
|
||||
|
||||
<p>2. Change the name in the input and click the buttons again.</p>
|
||||
@@ -36,10 +36,10 @@
|
||||
<h3>Disabled property vs. attribute</h3>
|
||||
<p>Use the inspector to see the Test Button work and its disabled property toggle.</p>
|
||||
<div>
|
||||
<button id="testButton" (click)="working()">Test Button</button>
|
||||
<button type="button" id="testButton" (click)="working()">Test Button</button>
|
||||
</div>
|
||||
<div>
|
||||
<button (click)="toggleDisabled()">Toggle disabled property for Test Button</button>
|
||||
<button type="button" (click)="toggleDisabled()">Toggle disabled property for Test Button</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<label for="special">special: <input type="checkbox" [(ngModel)]="isSpecial" id="special"></label>
|
||||
</li>
|
||||
</ul>
|
||||
<button (click)="setCurrentClasses()">Refresh currentClasses</button>
|
||||
<button type="button" (click)="setCurrentClasses()">Refresh currentClasses</button>
|
||||
|
||||
<div [ngClass]="currentClasses">
|
||||
This div should be {{ canSave ? "": "not"}} saveable,
|
||||
@@ -86,7 +86,7 @@
|
||||
<label>italic: <input type="checkbox" [(ngModel)]="canSave"></label> |
|
||||
<label>normal: <input type="checkbox" [(ngModel)]="isUnchanged"></label> |
|
||||
<label>xlarge: <input type="checkbox" [(ngModel)]="isSpecial"></label>
|
||||
<button (click)="setCurrentStyles()">Refresh currentStyles</button>
|
||||
<button type="button" (click)="setCurrentStyles()">Refresh currentStyles</button>
|
||||
<br><br>
|
||||
<div [ngStyle]="currentStyles">
|
||||
This div should be {{ canSave ? "italic": "plain"}},
|
||||
@@ -102,7 +102,7 @@
|
||||
<app-item-detail *ngIf="isActive" [item]="item"></app-item-detail>
|
||||
<!-- #enddocregion NgIf-1 -->
|
||||
|
||||
<button (click)="isActiveToggle()">Toggle app-item-detail</button>
|
||||
<button type="button" (click)="isActiveToggle()">Toggle app-item-detail</button>
|
||||
</div>
|
||||
<p>If currentCustomer isn't null, say hello to Laura:</p>
|
||||
<!-- #docregion NgIf-2 -->
|
||||
@@ -112,7 +112,7 @@
|
||||
<!-- #docregion NgIf-2b -->
|
||||
<div *ngIf="nullCustomer">Hello, <span>{{nullCustomer}}</span></div>
|
||||
<!-- #enddocregion NgIf-2b -->
|
||||
<button (click)="giveNullCustomerValue()">Give nullCustomer a value</button>
|
||||
<button type="button" (click)="giveNullCustomerValue()">Give nullCustomer a value</button>
|
||||
|
||||
|
||||
<h4>NgIf binding with template (no *)</h4>
|
||||
@@ -163,9 +163,9 @@
|
||||
</div>
|
||||
|
||||
<h4 id="ngFor-trackBy">*ngFor trackBy</h4>
|
||||
<button (click)="resetList()">Reset items</button>
|
||||
<button (click)="changeIds()">Change ids</button>
|
||||
<button (click)="clearTrackByCounts()">Clear counts</button>
|
||||
<button type="button" (click)="resetList()">Reset items</button>
|
||||
<button type="button" (click)="changeIds()">Change ids</button>
|
||||
<button type="button" (click)="clearTrackByCounts()">Clear counts</button>
|
||||
|
||||
<p><i>without</i> trackBy</p>
|
||||
<div class="box">
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Subscription } from 'rxjs';
|
||||
<p>
|
||||
{{astronaut}}: <strong>{{mission}}</strong>
|
||||
<button
|
||||
type="button"
|
||||
(click)="confirm()"
|
||||
[disabled]="!announced || confirmed">
|
||||
Confirm
|
||||
|
||||
@@ -14,8 +14,8 @@ import { CountdownTimerComponent } from './countdown-timer.component';
|
||||
selector: 'app-countdown-parent-lv',
|
||||
template: `
|
||||
<h3>Countdown to Liftoff (via local variable)</h3>
|
||||
<button (click)="timer.start()">Start</button>
|
||||
<button (click)="timer.stop()">Stop</button>
|
||||
<button type="button" (click)="timer.start()">Start</button>
|
||||
<button type="button" (click)="timer.stop()">Stop</button>
|
||||
<div class="seconds">{{timer.seconds}}</div>
|
||||
<app-countdown-timer #timer></app-countdown-timer>
|
||||
`,
|
||||
@@ -30,8 +30,8 @@ export class CountdownLocalVarParentComponent { }
|
||||
selector: 'app-countdown-parent-vc',
|
||||
template: `
|
||||
<h3>Countdown to Liftoff (via ViewChild)</h3>
|
||||
<button (click)="start()">Start</button>
|
||||
<button (click)="stop()">Stop</button>
|
||||
<button type="button" (click)="start()">Start</button>
|
||||
<button type="button" (click)="stop()">Stop</button>
|
||||
<div class="seconds">{{ seconds() }}</div>
|
||||
<app-countdown-timer></app-countdown-timer>
|
||||
`,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { MissionService } from './mission.service';
|
||||
selector: 'app-mission-control',
|
||||
template: `
|
||||
<h2>Mission Control</h2>
|
||||
<button (click)="announce()">Announce mission</button>
|
||||
<button type="button" (click)="announce()">Announce mission</button>
|
||||
|
||||
<app-astronaut
|
||||
*ngFor="let astronaut of astronauts"
|
||||
|
||||
@@ -5,8 +5,8 @@ import { Component } from '@angular/core';
|
||||
selector: 'app-version-parent',
|
||||
template: `
|
||||
<h2>Source code version</h2>
|
||||
<button (click)="newMinor()">New minor version</button>
|
||||
<button (click)="newMajor()">New major version</button>
|
||||
<button type="button" (click)="newMinor()">New minor version</button>
|
||||
<button type="button" (click)="newMajor()">New major version</button>
|
||||
<app-version-child [major]="major" [minor]="minor"></app-version-child>
|
||||
`
|
||||
})
|
||||
|
||||
@@ -5,8 +5,8 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
selector: 'app-voter',
|
||||
template: `
|
||||
<h4>{{name}}</h4>
|
||||
<button (click)="vote(true)" [disabled]="didVote">Agree</button>
|
||||
<button (click)="vote(false)" [disabled]="didVote">Disagree</button>
|
||||
<button type="button" (click)="vote(true)" [disabled]="didVote">Agree</button>
|
||||
<button type="button" (click)="vote(false)" [disabled]="didVote">Disagree</button>
|
||||
`
|
||||
})
|
||||
export class VoterComponent {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Hero } from './hero';
|
||||
}
|
||||
</style>
|
||||
<h3>Controls</h3>
|
||||
<button (click)="activate()">Activate</button>
|
||||
<button type="button" (click)="activate()">Activate</button>
|
||||
`
|
||||
})
|
||||
// #enddocregion inlinestyles
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<body>
|
||||
<h1 style="visibility: hidden;">External H1 Title for E2E test</h1>
|
||||
<app-root></app-root>
|
||||
<button style="visibility: hidden;">External button for E2E test</button>
|
||||
<button type="button" style="visibility: hidden;">External button for E2E test</button>
|
||||
<ul style="visibility: hidden;">
|
||||
<li>External list for E2E test</li>
|
||||
</ul>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<h2>Here's a zippy</h2>
|
||||
|
||||
<app-example-zippy>
|
||||
<button appExampleZippyToggle>Is content project cool?</button>
|
||||
<button type="button" appExampleZippyToggle>Is content project cool?</button>
|
||||
<!-- #docregion ng-template-->
|
||||
<ng-template appExampleZippyContent>
|
||||
It depends on what you do with it.
|
||||
|
||||
@@ -8,10 +8,10 @@ import { BROWSER_STORAGE, BrowserStorageService } from './storage.service';
|
||||
Open the inspector to see the local/session storage keys:
|
||||
|
||||
<h3>Session Storage</h3>
|
||||
<button (click)="setSession()">Set Session Storage</button>
|
||||
<button type="button" (click)="setSession()">Set Session Storage</button>
|
||||
|
||||
<h3>Local Storage</h3>
|
||||
<button (click)="setLocal()">Set Local Storage</button>
|
||||
<button type="button" (click)="setLocal()">Set Local Storage</button>
|
||||
`,
|
||||
providers: [
|
||||
BrowserStorageService,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { UserService } from './user.service';
|
||||
<h2>User</h2>
|
||||
<p id="user">
|
||||
{{userInfo}}
|
||||
<button (click)="nextUser()">Next User</button>
|
||||
<button type="button" (click)="nextUser()">Next User</button>
|
||||
<p>
|
||||
<app-heroes id="authorized" *ngIf="isAuthorized"></app-heroes>
|
||||
<app-heroes id="unauthorized" *ngIf="!isAuthorized"></app-heroes>
|
||||
|
||||
@@ -7,45 +7,67 @@
|
||||
}
|
||||
/* #enddocregion heroes */
|
||||
|
||||
.heroes {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
width: 15em;
|
||||
}
|
||||
.heroes li {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.heroes button {
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
left: 0;
|
||||
background-color: #EEE;
|
||||
margin: .5em;
|
||||
padding: .3em 0;
|
||||
height: 1.6em;
|
||||
padding: 0;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
height: 1.8em;
|
||||
}
|
||||
.heroes li.selected:hover {
|
||||
background-color: #BBD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
.heroes li:hover {
|
||||
color: #607D8B;
|
||||
background-color: #DDD;
|
||||
|
||||
.heroes button:hover {
|
||||
color: #2c3a41;
|
||||
background-color: #e6e6e6;
|
||||
left: .1em;
|
||||
}
|
||||
.heroes .text {
|
||||
position: relative;
|
||||
top: -3px;
|
||||
|
||||
.heroes button:active {
|
||||
background-color: #525252;
|
||||
color: #fafafa;
|
||||
}
|
||||
|
||||
.heroes button.selected {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.heroes button.selected:hover {
|
||||
background-color: #505050;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.heroes button.selected:active {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.heroes .badge {
|
||||
display: inline-block;
|
||||
font-size: small;
|
||||
color: white;
|
||||
padding: 0.8em 0.7em 0 0.7em;
|
||||
background-color: #607D8B;
|
||||
background-color: #405061;
|
||||
line-height: 1em;
|
||||
position: relative;
|
||||
left: -1px;
|
||||
top: -4px;
|
||||
height: 1.8em;
|
||||
margin-right: .8em;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.selected {
|
||||
background-color: #CFD8DC !important;
|
||||
color: white;
|
||||
.heroes .name {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
<h1>{{title}}</h1>
|
||||
<h2>My Heroes</h2>
|
||||
<ul class="heroes">
|
||||
<li *ngFor="let hero of heroes"
|
||||
[class.selected]="hero === selectedHero"
|
||||
(click)="onSelect(hero)">
|
||||
<span class="badge">{{hero.id}}</span> {{hero.name}}
|
||||
<li *ngFor="let hero of heroes">
|
||||
<button [class.selected]="hero === selectedHero" type="button" (click)="onSelect(hero)">
|
||||
<span class="badge">{{hero.id}}</span>
|
||||
<span class="name">{{hero.name}}</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div *ngIf="selectedHero">
|
||||
<h2>{{selectedHero.name}} details!</h2>
|
||||
<div><label>id: </label>{{selectedHero.id}}</div>
|
||||
<div>id: {{selectedHero.id}}</div>
|
||||
<div>
|
||||
<label>name: </label>
|
||||
<input [(ngModel)]="selectedHero.name" placeholder="name"/>
|
||||
<label>
|
||||
name: <input [(ngModel)]="selectedHero.name" placeholder="name"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// #docregion
|
||||
import { Component, Input, OnDestroy, AfterViewInit, ViewChild } from '@angular/core';
|
||||
import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
|
||||
import { AdDirective } from './ad.directive';
|
||||
import { AdItem } from './ad-item';
|
||||
@@ -17,7 +17,7 @@ import { AdComponent } from './ad.component';
|
||||
// #enddocregion ad-host
|
||||
})
|
||||
// #docregion class
|
||||
export class AdBannerComponent implements AfterViewInit, OnDestroy {
|
||||
export class AdBannerComponent implements OnInit, OnDestroy {
|
||||
@Input() ads: AdItem[] = [];
|
||||
|
||||
currentAdIndex = -1;
|
||||
@@ -25,7 +25,7 @@ export class AdBannerComponent implements AfterViewInit, OnDestroy {
|
||||
@ViewChild(AdDirective, {static: true}) adHost!: AdDirective;
|
||||
interval: number|undefined;
|
||||
|
||||
ngAfterViewInit() {
|
||||
ngOnInit(): void {
|
||||
this.loadComponent();
|
||||
this.getAds();
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import { PopupComponent } from './popup.component';
|
||||
selector: 'app-root',
|
||||
template: `
|
||||
<input #input value="Message">
|
||||
<button (click)="popup.showAsComponent(input.value)">Show as component</button>
|
||||
<button (click)="popup.showAsElement(input.value)">Show as element</button>
|
||||
<button type="button" (click)="popup.showAsComponent(input.value)">Show as component</button>
|
||||
<button type="button" (click)="popup.showAsElement(input.value)">Show as element</button>
|
||||
`,
|
||||
})
|
||||
export class AppComponent {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
|
||||
selector: 'my-popup',
|
||||
template: `
|
||||
<span>Popup: {{message}}</span>
|
||||
<button (click)="closed.next()">✖</button>
|
||||
<button type="button" (click)="closed.next()">✖</button>
|
||||
`,
|
||||
animations: [
|
||||
trigger('state', [
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
<div class="group">
|
||||
<h3>Target event</h3>
|
||||
<button (click)="onSave($event)">Save</button>
|
||||
<button type="button" (click)="onSave($event)">Save</button>
|
||||
|
||||
<button on-click="onSave($event)">on-click Save</button>
|
||||
<button type="button" on-enter="onSave()">on-click Save</button>
|
||||
|
||||
<!-- #docregion custom-directive -->
|
||||
<h4>myClick is an event on the custom ClickDirective:</h4>
|
||||
<button (myClick)="clickMessage=$event" clickable>click with myClick</button>
|
||||
<button type="button" (myClick)="clickMessage=$event" clickable>click with myClick</button>
|
||||
{{clickMessage}}
|
||||
<!-- #enddocregion custom-directive -->
|
||||
|
||||
@@ -34,16 +34,21 @@
|
||||
|
||||
|
||||
<h4>Click to see event target class:</h4>
|
||||
|
||||
<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->
|
||||
|
||||
<div class="parent-div" (click)="onClickMe($event)" clickable>Click me (parent)
|
||||
<div class="child-div">Click me too! (child) </div>
|
||||
</div>
|
||||
|
||||
<h3>Saves only once:</h3>
|
||||
<div (click)="onSave()" clickable>
|
||||
<button (click)="onSave($event)">Save, no propagation</button>
|
||||
<button type="button" (click)="onSave($event)">Save, no propagation</button>
|
||||
</div>
|
||||
|
||||
<h3>Saves twice:</h3>
|
||||
<div (click)="onSave()" clickable>
|
||||
<button (click)="onSave()">Save with propagation</button>
|
||||
<button type="button" (click)="onSave()">Save with propagation</button>
|
||||
</div>
|
||||
|
||||
<!-- eslint-enable @angular-eslint/template/click-events-have-key-events -->
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div class="detail">
|
||||
<p>This is the ItemDetailComponent</p>
|
||||
<!-- #docregion line-through -->
|
||||
<img src="{{itemImageUrl}}" [style.display]="displayNone">
|
||||
<img src="{{itemImageUrl}}" alt="{{item.name}}" [style.display]="displayNone">
|
||||
<span [style.text-decoration]="lineThrough">{{ item.name }}
|
||||
</span>
|
||||
<button (click)="delete()">Delete</button>
|
||||
<button type="button" (click)="delete()">Delete</button>
|
||||
<!-- #enddocregion line-through -->
|
||||
</div>
|
||||
|
||||
+11
-4
@@ -19,16 +19,21 @@ export class HeroFormReactiveComponent implements OnInit {
|
||||
heroForm: FormGroup;
|
||||
|
||||
ngOnInit(): void {
|
||||
// #docregion async-validator-usage
|
||||
const alterEgoControl = new FormControl('', {
|
||||
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
|
||||
updateOn: 'blur'
|
||||
});
|
||||
// #enddocregion async-validator-usage
|
||||
alterEgoControl.setValue(this.hero.alterEgo);
|
||||
|
||||
this.heroForm = new FormGroup({
|
||||
name: new FormControl(this.hero.name, [
|
||||
Validators.required,
|
||||
Validators.minLength(4),
|
||||
forbiddenNameValidator(/bob/i)
|
||||
]),
|
||||
alterEgo: new FormControl(this.hero.alterEgo, {
|
||||
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
|
||||
updateOn: 'blur'
|
||||
}),
|
||||
alterEgo: alterEgoControl,
|
||||
power: new FormControl(this.hero.power, Validators.required)
|
||||
});
|
||||
}
|
||||
@@ -39,5 +44,7 @@ export class HeroFormReactiveComponent implements OnInit {
|
||||
|
||||
get alterEgo() { return this.heroForm.get('alterEgo'); }
|
||||
|
||||
// #docregion async-validator-inject
|
||||
constructor(private alterEgoValidator: UniqueAlterEgoValidator) {}
|
||||
// #enddocregion async-validator-inject
|
||||
}
|
||||
|
||||
+1
-1
@@ -74,6 +74,6 @@
|
||||
|
||||
<div class="submitted-message" *ngIf="formDir.submitted">
|
||||
<p>You've submitted your hero, {{ heroForm.value.name }}!</p>
|
||||
<button (click)="formDir.resetForm({})">Add new hero</button>
|
||||
<button type="button" (click)="formDir.resetForm({})">Add new hero</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,9 +15,9 @@ export class UniqueAlterEgoValidator implements AsyncValidator {
|
||||
constructor(private heroesService: HeroesService) {}
|
||||
|
||||
validate(
|
||||
ctrl: AbstractControl
|
||||
): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
|
||||
return this.heroesService.isAlterEgoTaken(ctrl.value).pipe(
|
||||
control: AbstractControl
|
||||
): Observable<ValidationErrors | null> {
|
||||
return this.heroesService.isAlterEgoTaken(control.value).pipe(
|
||||
map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)),
|
||||
catchError(() => of(null))
|
||||
);
|
||||
@@ -25,20 +25,24 @@ export class UniqueAlterEgoValidator implements AsyncValidator {
|
||||
}
|
||||
// #enddocregion async-validator
|
||||
|
||||
// #docregion async-validator-directive
|
||||
@Directive({
|
||||
selector: '[appUniqueAlterEgo]',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_ASYNC_VALIDATORS,
|
||||
useExisting: forwardRef(() => UniqueAlterEgoValidator),
|
||||
useExisting: forwardRef(() => UniqueAlterEgoValidatorDirective),
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class UniqueAlterEgoValidatorDirective {
|
||||
export class UniqueAlterEgoValidatorDirective implements AsyncValidator {
|
||||
constructor(private validator: UniqueAlterEgoValidator) {}
|
||||
|
||||
validate(control: AbstractControl) {
|
||||
this.validator.validate(control);
|
||||
validate(
|
||||
control: AbstractControl
|
||||
): Observable<ValidationErrors | null> {
|
||||
return this.validator.validate(control);
|
||||
}
|
||||
}
|
||||
// #enddocregion async-validator-directive
|
||||
|
||||
+4
-3
@@ -35,14 +35,15 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input type="text"
|
||||
<!-- #docregion alterEgo-input -->
|
||||
<input type="text"
|
||||
id="alterEgo"
|
||||
name="alterEgo"
|
||||
#alterEgo="ngModel"
|
||||
[(ngModel)]="hero.alterEgo"
|
||||
[ngModelOptions]="{ updateOn: 'blur' }"
|
||||
appUniqueAlterEgo>
|
||||
|
||||
<!-- #enddocregion alterEgo-input -->
|
||||
<div *ngIf="alterEgo.pending">Validating...</div>
|
||||
<div *ngIf="alterEgo.invalid" class="alert alter-ego-errors">
|
||||
<div *ngIf="alterEgo.errors?.['uniqueAlterEgo']">
|
||||
@@ -81,7 +82,7 @@
|
||||
|
||||
<div class="submitted-message" *ngIf="heroForm.submitted">
|
||||
<p>You've submitted your hero, {{ heroForm.value.name }}!</p>
|
||||
<button (click)="heroForm.resetForm({})">Add new hero</button>
|
||||
<button type="button" (click)="heroForm.resetForm({})">Add new hero</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
<div class="col-xs-9">{{ model.power }}</div>
|
||||
</div>
|
||||
<br>
|
||||
<button class="btn btn-primary" (click)="submitted=false">Edit</button>
|
||||
<button type="button" class="btn btn-primary" (click)="submitted=false">Edit</button>
|
||||
</div>
|
||||
<!-- #enddocregion submitted -->
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
<p *ngIf="product && product.price > 700">
|
||||
<button>Notify Me</button>
|
||||
<button type="button">Notify Me</button>
|
||||
</p>
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
<p *ngIf="product && product.price > 700">
|
||||
<button (click)="notify.emit()">Notify Me</button>
|
||||
<button type="button" (click)="notify.emit()">Notify Me</button>
|
||||
</p>
|
||||
|
||||
+1
-1
@@ -7,6 +7,6 @@
|
||||
<h4>{{ product.price | currency }}</h4>
|
||||
<p>{{ product.description }}</p>
|
||||
<!-- #enddocregion details -->
|
||||
<button (click)="addToCart(product)">Buy</button>
|
||||
<button type="button" (click)="addToCart(product)">Buy</button>
|
||||
<!-- #docregion details -->
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
Description: {{ product.description }}
|
||||
</p>
|
||||
|
||||
<button (click)="share()">
|
||||
<button type="button" (click)="share()">
|
||||
Share
|
||||
</button>
|
||||
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
</p>
|
||||
|
||||
<!-- #docregion app-product-alerts -->
|
||||
<button (click)="share()">
|
||||
<button type="button" (click)="share()">
|
||||
Share
|
||||
</button>
|
||||
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
</p>
|
||||
|
||||
<!-- #docregion on-notify -->
|
||||
<button (click)="share()">
|
||||
<button type="button" (click)="share()">
|
||||
Share
|
||||
</button>
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
Description: {{ product.description }}
|
||||
</p>
|
||||
|
||||
<button (click)="share()">
|
||||
<button type="button" (click)="share()">
|
||||
Share
|
||||
</button>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('Hierarchical dependency injection', () => {
|
||||
income: '',
|
||||
|
||||
// queries
|
||||
heroEl: element.all(by.css('app-heroes-list li')).get(0), // first hero
|
||||
heroEl: element.all(by.css('app-heroes-list li button')).get(0), // first hero
|
||||
heroCardEl: element(by.css('app-heroes-list app-hero-tax-return')), // first hero tax-return
|
||||
taxReturnNameEl: element.all(by.css('app-heroes-list app-hero-tax-return #name')).get(0),
|
||||
incomeInputEl: element.all(by.css('app-heroes-list app-hero-tax-return input')).get(0),
|
||||
|
||||
+5
-5
@@ -2,7 +2,7 @@
|
||||
<div class="msg" [class.canceled]="message==='Canceled'">{{message}}</div>
|
||||
<fieldset>
|
||||
<span id=name>{{taxReturn.name}}</span>
|
||||
<label id=tid>TID: {{taxReturn.tid}}</label>
|
||||
<span id=tid>TID: {{taxReturn.tid}}</span>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<label>
|
||||
@@ -10,11 +10,11 @@
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<label>Tax: {{taxReturn.tax}}</label>
|
||||
<span>Tax: {{taxReturn.tax}}</span>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<button (click)="onSaved()">Save</button>
|
||||
<button (click)="onCanceled()">Cancel</button>
|
||||
<button (click)="onClose()">Close</button>
|
||||
<button type="button" (click)="onSaved()">Save</button>
|
||||
<button type="button" (click)="onCanceled()">Cancel</button>
|
||||
<button type="button" (click)="onClose()">Close</button>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
+9
-3
@@ -11,8 +11,8 @@ import { HeroesService } from './heroes.service';
|
||||
<div>
|
||||
<h3>Hero Tax Returns</h3>
|
||||
<ul>
|
||||
<li *ngFor="let hero of heroes | async"
|
||||
(click)="showTaxReturn(hero)">{{hero.name}}
|
||||
<li *ngFor="let hero of heroes | async">
|
||||
<button type="button" (click)="showTaxReturn(hero)">{{hero.name}}</button>
|
||||
</li>
|
||||
</ul>
|
||||
<app-hero-tax-return
|
||||
@@ -22,7 +22,13 @@ import { HeroesService } from './heroes.service';
|
||||
</app-hero-tax-return>
|
||||
</div>
|
||||
`,
|
||||
styles: [ 'li {cursor: pointer;}' ]
|
||||
styles: [`
|
||||
li button {
|
||||
font-size: inherit;
|
||||
margin: 0.3rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
`]
|
||||
})
|
||||
export class HeroesListComponent {
|
||||
heroes: Observable<Hero[]>;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<h3>Get configuration from JSON file</h3>
|
||||
<div>
|
||||
<button (click)="clear(); showConfig()">get</button>
|
||||
<button (click)="clear(); showConfigResponse()">getResponse</button>
|
||||
<button (click)="clear()">clear</button>
|
||||
<button (click)="clear(); makeError()">error</button>
|
||||
<button type="button" (click)="clear(); showConfig()">get</button>
|
||||
<button type="button" (click)="clear(); showConfigResponse()">getResponse</button>
|
||||
<button type="button" (click)="clear()">clear</button>
|
||||
<button type="button" (click)="clear(); makeError()">error</button>
|
||||
<span *ngIf="config">
|
||||
<p>Heroes API URL is "{{config.heroesUrl}}"</p>
|
||||
<p>Textfile URL is "{{config.textfile}}"</p>
|
||||
|
||||
@@ -28,10 +28,10 @@ export class ConfigComponent {
|
||||
showConfig() {
|
||||
this.configService.getConfig()
|
||||
// #enddocregion v1, v2
|
||||
.subscribe(
|
||||
(data: Config) => this.config = { ...data }, // success path
|
||||
error => this.error = error // error path
|
||||
);
|
||||
.subscribe({
|
||||
next: (data: Config) => this.config = { ...data }, // success path
|
||||
error: error => this.error = error, // error path
|
||||
});
|
||||
}
|
||||
|
||||
showConfig_v1() {
|
||||
@@ -69,7 +69,7 @@ export class ConfigComponent {
|
||||
}
|
||||
// #enddocregion showConfigResponse
|
||||
makeError() {
|
||||
this.configService.makeIntentionalError().subscribe(null, error => this.error = error );
|
||||
this.configService.makeIntentionalError().subscribe({ error: error => this.error = error.message });
|
||||
}
|
||||
|
||||
getType(val: any): string {
|
||||
|
||||
@@ -82,8 +82,7 @@ export class ConfigService {
|
||||
`Backend returned code ${error.status}, body was: `, error.error);
|
||||
}
|
||||
// Return an observable with a user-facing error message.
|
||||
return throwError(
|
||||
'Something bad happened; please try again later.');
|
||||
return throwError(() => new Error('Something bad happened; please try again later.'));
|
||||
}
|
||||
// #enddocregion handleError
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<h2>Download the text file</h2>
|
||||
<button (click)="download()">Download</button>
|
||||
<button (click)="clear()">clear</button>
|
||||
<button type="button" (click)="download()">Download</button>
|
||||
<button type="button" (click)="clear()">clear</button>
|
||||
<p *ngIf="contents">Contents: "{{contents}}"</p>
|
||||
|
||||
@@ -9,21 +9,20 @@
|
||||
}
|
||||
|
||||
.heroes li {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.heroes li:hover {
|
||||
left: .1em;
|
||||
}
|
||||
|
||||
.heroes a {
|
||||
.heroes button.delete {
|
||||
color: black;
|
||||
display: block;
|
||||
font-size: 1.2rem;
|
||||
background-color: #eee;
|
||||
margin: .5em 0;
|
||||
padding: .5em 0;
|
||||
border-radius: 4px;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.heroes a:hover {
|
||||
@@ -40,8 +39,18 @@
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.heroes button.edit {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
padding: 0;
|
||||
margin-right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
button.delete {
|
||||
position: absolute;
|
||||
right: -8px;
|
||||
top: 5px;
|
||||
background-color: gray;
|
||||
@@ -53,6 +62,4 @@ button.delete {
|
||||
.heroes input {
|
||||
max-width: 12rem;
|
||||
padding: .25rem;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
<input type="text" #heroName id="hero-name">
|
||||
|
||||
<!-- (click) passes input value to add() and then clears the input -->
|
||||
<button (click)="add(heroName.value); heroName.value=''">
|
||||
<button type="button" (click)="add(heroName.value); heroName.value=''">
|
||||
Add hero
|
||||
</button>
|
||||
<button (click)="search(heroName.value)">
|
||||
<button type="button" (click)="search(heroName.value)">
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul class="heroes">
|
||||
<li *ngFor="let hero of heroes">
|
||||
<a (click)="edit(hero)">
|
||||
<button type="button" class="edit" (click)="edit(hero)">
|
||||
<span class="badge">{{ hero.id || -1 }}</span>
|
||||
<span *ngIf="hero!==editHero">{{hero.name}}</span>
|
||||
<input type="text"
|
||||
@@ -22,8 +22,8 @@
|
||||
[(ngModel)]="hero.name"
|
||||
(blur)="update()"
|
||||
(keyup.enter)="update()">
|
||||
</a>
|
||||
<button class="delete" title="delete hero"
|
||||
</button>
|
||||
<button type="button" class="delete" title="delete hero"
|
||||
(click)="delete(hero)">x</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -53,10 +53,10 @@ describe('HeroesService', () => {
|
||||
|
||||
it('should return expected heroes (called once)', () => {
|
||||
|
||||
heroService.getHeroes().subscribe(
|
||||
heroes => expect(heroes).toEqual(expectedHeroes, 'should return expected heroes'),
|
||||
fail
|
||||
);
|
||||
heroService.getHeroes().subscribe({
|
||||
next: heroes => expect(heroes).toEqual(expectedHeroes, 'should return expected heroes'),
|
||||
error: fail,
|
||||
});
|
||||
|
||||
// HeroService should have made one request to GET heroes from expected URL
|
||||
const req = httpTestingController.expectOne(heroService.heroesUrl);
|
||||
@@ -68,10 +68,10 @@ describe('HeroesService', () => {
|
||||
|
||||
it('should be OK returning no heroes', () => {
|
||||
|
||||
heroService.getHeroes().subscribe(
|
||||
heroes => expect(heroes.length).toEqual(0, 'should have empty heroes array'),
|
||||
fail
|
||||
);
|
||||
heroService.getHeroes().subscribe({
|
||||
next: heroes => expect(heroes.length).toEqual(0, 'should have empty heroes array'),
|
||||
error: fail,
|
||||
});
|
||||
|
||||
const req = httpTestingController.expectOne(heroService.heroesUrl);
|
||||
req.flush([]); // Respond with no heroes
|
||||
@@ -80,10 +80,10 @@ describe('HeroesService', () => {
|
||||
// This service reports the error but finds a way to let the app keep going.
|
||||
it('should turn 404 into an empty heroes result', () => {
|
||||
|
||||
heroService.getHeroes().subscribe(
|
||||
heroes => expect(heroes.length).toEqual(0, 'should return empty heroes array'),
|
||||
fail
|
||||
);
|
||||
heroService.getHeroes().subscribe({
|
||||
next: heroes => expect(heroes.length).toEqual(0, 'should return empty heroes array'),
|
||||
error: fail,
|
||||
});
|
||||
|
||||
const req = httpTestingController.expectOne(heroService.heroesUrl);
|
||||
|
||||
@@ -96,10 +96,10 @@ describe('HeroesService', () => {
|
||||
|
||||
heroService.getHeroes().subscribe();
|
||||
heroService.getHeroes().subscribe();
|
||||
heroService.getHeroes().subscribe(
|
||||
heroes => expect(heroes).toEqual(expectedHeroes, 'should return expected heroes'),
|
||||
fail
|
||||
);
|
||||
heroService.getHeroes().subscribe({
|
||||
next: heroes => expect(heroes).toEqual(expectedHeroes, 'should return expected heroes'),
|
||||
error: fail,
|
||||
});
|
||||
|
||||
const requests = httpTestingController.match(heroService.heroesUrl);
|
||||
expect(requests.length).toEqual(3, 'calls to getHeroes()');
|
||||
@@ -119,10 +119,10 @@ describe('HeroesService', () => {
|
||||
|
||||
const updateHero: Hero = { id: 1, name: 'A' };
|
||||
|
||||
heroService.updateHero(updateHero).subscribe(
|
||||
data => expect(data).toEqual(updateHero, 'should return the hero'),
|
||||
fail
|
||||
);
|
||||
heroService.updateHero(updateHero).subscribe({
|
||||
next: data => expect(data).toEqual(updateHero, 'should return the hero'),
|
||||
error: fail,
|
||||
});
|
||||
|
||||
// HeroService should have made one request to PUT hero
|
||||
const req = httpTestingController.expectOne(heroService.heroesUrl);
|
||||
@@ -139,10 +139,10 @@ describe('HeroesService', () => {
|
||||
it('should turn 404 error into return of the update hero', () => {
|
||||
const updateHero: Hero = { id: 1, name: 'A' };
|
||||
|
||||
heroService.updateHero(updateHero).subscribe(
|
||||
data => expect(data).toEqual(updateHero, 'should return the update hero'),
|
||||
fail
|
||||
);
|
||||
heroService.updateHero(updateHero).subscribe({
|
||||
next: data => expect(data).toEqual(updateHero, 'should return the update hero'),
|
||||
error: fail,
|
||||
});
|
||||
|
||||
const req = httpTestingController.expectOne(heroService.heroesUrl);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div *ngIf="messageService.messages.length">
|
||||
<h2>Messages</h2>
|
||||
<button class="clear" (click)="messageService.clear()">clear</button>
|
||||
<button type="button" class="clear" (click)="messageService.clear()">clear</button>
|
||||
<br>
|
||||
<ol>
|
||||
<li *ngFor='let message of messageService.messages'> {{message}} </li>
|
||||
|
||||
@@ -97,13 +97,13 @@ describe('HttpClient testing', () => {
|
||||
|
||||
// Make three requests in a row
|
||||
httpClient.get<Data[]>(testUrl)
|
||||
.subscribe(d => expect(d.length).toEqual(0, 'should have no data'));
|
||||
.subscribe(d => expect(d.length).withContext('should have no data').toEqual(0));
|
||||
|
||||
httpClient.get<Data[]>(testUrl)
|
||||
.subscribe(d => expect(d).toEqual([testData[0]], 'should be one element array'));
|
||||
.subscribe(d => expect(d).withContext('should be one element array').toEqual([testData[0]]));
|
||||
|
||||
httpClient.get<Data[]>(testUrl)
|
||||
.subscribe(d => expect(d).toEqual(testData, 'should be expected data'));
|
||||
.subscribe(d => expect(d).withContext('should be expected data').toEqual(testData));
|
||||
|
||||
// #docregion multi-request
|
||||
// get all pending requests that match the given URL
|
||||
@@ -121,13 +121,13 @@ describe('HttpClient testing', () => {
|
||||
it('can test for 404 error', () => {
|
||||
const emsg = 'deliberate 404 error';
|
||||
|
||||
httpClient.get<Data[]>(testUrl).subscribe(
|
||||
data => fail('should have failed with the 404 error'),
|
||||
(error: HttpErrorResponse) => {
|
||||
expect(error.status).toEqual(404, 'status');
|
||||
expect(error.error).toEqual(emsg, 'message');
|
||||
}
|
||||
);
|
||||
httpClient.get<Data[]>(testUrl).subscribe({
|
||||
next: () => fail('should have failed with the 404 error'),
|
||||
error: (error: HttpErrorResponse) => {
|
||||
expect(error.status).withContext('status').toEqual(404);
|
||||
expect(error.error).withContext('message').toEqual(emsg);
|
||||
},
|
||||
});
|
||||
|
||||
const req = httpTestingController.expectOne(testUrl);
|
||||
|
||||
@@ -142,13 +142,13 @@ describe('HttpClient testing', () => {
|
||||
// at network level. e.g. Connection timeout, DNS error, offline, etc.
|
||||
const mockError = new ProgressEvent('error');
|
||||
|
||||
httpClient.get<Data[]>(testUrl).subscribe(
|
||||
data => fail('should have failed with the network error'),
|
||||
(error: HttpErrorResponse) => {
|
||||
httpClient.get<Data[]>(testUrl).subscribe({
|
||||
next: () => fail('should have failed with the network error'),
|
||||
error: (error: HttpErrorResponse) => {
|
||||
expect(error.error).toBe(mockError);
|
||||
done();
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const req = httpTestingController.expectOne(testUrl);
|
||||
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
"i18n": {
|
||||
"sourceLocale": "en-US",
|
||||
"locales": {
|
||||
"fr": "src/locale/messages.fr.xlf",
|
||||
// #enddocregion locale-config
|
||||
"baseHref": ""
|
||||
// #docregion locale-config
|
||||
"fr": {
|
||||
"translation": "src/locale/messages.fr.xlf",
|
||||
// #enddocregion locale-config
|
||||
"baseHref": ""
|
||||
// #docregion locale-config
|
||||
}
|
||||
}
|
||||
},
|
||||
// #docregion build-production-french
|
||||
|
||||
@@ -27,11 +27,11 @@
|
||||
<!--#enddocregion i18n-attribute-solo-id-->
|
||||
|
||||
<!--#docregion i18n-title-->
|
||||
<img [src]="logo" title="Angular logo">
|
||||
<img [src]="logo" title="Angular logo" alt="Angular logo">
|
||||
<!--#enddocregion i18n-title-->
|
||||
|
||||
<!--#docregion i18n-duplicate-custom-id-->
|
||||
<h3 i18n="@@myId">Hello</h3>
|
||||
<!-- ... -->
|
||||
<p i18n="@@myId">Good bye</p>
|
||||
<!--#enddocregion i18n-duplicate-custom-id-->
|
||||
<!--#enddocregion i18n-duplicate-custom-id-->
|
||||
|
||||
@@ -10,16 +10,16 @@
|
||||
<br />
|
||||
|
||||
<!--#docregion i18n-title-translate-->
|
||||
<img [src]="logo" i18n-title title="Angular logo" />
|
||||
<img [src]="logo" i18n-title title="Angular logo" alt="Angular logo"/>
|
||||
<!--#enddocregion i18n-title-translate-->
|
||||
<br>
|
||||
<button (click)="inc(1)">+</button> <button (click)="inc(-1)">-</button>
|
||||
<button type="button" (click)="inc(1)">+</button> <button type="button" (click)="inc(-1)">-</button>
|
||||
<!--#docregion i18n-plural-->
|
||||
<span i18n>Updated {minutes, plural, =0 {just now} =1 {one minute ago} other {{{minutes}} minutes ago}}</span>
|
||||
<!--#enddocregion i18n-plural-->
|
||||
({{minutes}})
|
||||
<br><br>
|
||||
<button (click)="male()">♂</button> <button (click)="female()">♀</button> <button (click)="other()">⚧</button>
|
||||
<button type="button" (click)="male()">♂</button> <button type="button" (click)="female()">♀</button> <button type="button" (click)="other()">⚧</button>
|
||||
<!--#docregion i18n-select-->
|
||||
<span i18n>The author is {gender, select, male {male} female {female} other {other}}</span>
|
||||
<!--#enddocregion i18n-select-->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<p>Save for later item: {{input1}}</p>
|
||||
<button (click)="saveIt()"> Save for later</button>
|
||||
<button type="button" (click)="saveIt()"> Save for later</button>
|
||||
|
||||
<p>Item for wishlist: {{input2}}</p>
|
||||
<button (click)="wishForIt()"> Add to wishlist</button>
|
||||
<button type="button" (click)="wishForIt()"> Add to wishlist</button>
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
<p>Latest clearance item: {{clearanceItem}}</p>
|
||||
|
||||
<button (click)="buyIt()"> Buy it with an Output!</button>
|
||||
<button type="button" (click)="buyIt()"> Buy it with an Output!</button>
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
<p [style.text-decoration]="lineThrough">Item: {{item}}</p>
|
||||
<button (click)="delete()">Delete item with an Output!</button>
|
||||
<button type="button" (click)="delete()">Delete item with an Output!</button>
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
<!-- #docregion child-output -->
|
||||
<label for="item-input">Add an item:</label>
|
||||
<input type="text" id="item-input" #newItem>
|
||||
<button (click)="addNewItem(newItem.value)">Add to parent's list</button>
|
||||
<button type="button" (click)="addNewItem(newItem.value)">Add to parent's list</button>
|
||||
<!-- #enddocregion child-output -->
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<!-- #docregion component-property -->
|
||||
<p>{{title}}</p>
|
||||
<div><img src="{{itemImageUrl}}"></div>
|
||||
<div><img alt="item" src="{{itemImageUrl}}"></div>
|
||||
<!-- #enddocregion component-property -->
|
||||
|
||||
<h3>Evaluating template expressions </h3>
|
||||
@@ -35,7 +35,7 @@
|
||||
<h3>Component context, properties of app.component.ts:</h3>
|
||||
<!-- #docregion component-context -->
|
||||
<h4>{{recommended}}</h4>
|
||||
<img [src]="itemImageUrl2">
|
||||
<img alt="item 2" [src]="itemImageUrl2">
|
||||
<!-- #enddocregion component-context -->
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
{{title}}
|
||||
</h1>
|
||||
|
||||
<button routerLink="/customers">Customers</button>
|
||||
<button routerLink="/orders">Orders</button>
|
||||
<button routerLink="">Home</button>
|
||||
<button type="button" routerLink="/customers">Customers</button>
|
||||
<button type="button" routerLink="/orders">Orders</button>
|
||||
<button type="button" routerLink="">Home</button>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import { LoggerService } from './logger.service';
|
||||
|
||||
<div class="info">
|
||||
<h3>AfterContent Logs</h3>
|
||||
<button (click)="reset()">Reset</button>
|
||||
<button type="button" (click)="reset()">Reset</button>
|
||||
<div *ngFor="let msg of logger.logs" class="log">{{msg}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ import { LoggerService } from './logger.service';
|
||||
|
||||
<div class="info">
|
||||
<h3>AfterView Logs</h3>
|
||||
<button (click)="reset()">Reset</button>
|
||||
<button type="button" (click)="reset()">Reset</button>
|
||||
<div *ngFor="let msg of logger.logs" class="log">{{msg}}</div>
|
||||
</div>
|
||||
`,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<a id="top"></a>
|
||||
<h1>Lifecycle Hooks</h1>
|
||||
<h1 id="top">Lifecycle Hooks</h1>
|
||||
<a href="#hooks">Peek-a-boo: (most) lifecycle hooks</a>
|
||||
<a href="#spy">Spy: directive with OnInit & OnDestroy</a>
|
||||
<a href="#onchanges">OnChanges</a>
|
||||
@@ -8,36 +7,29 @@
|
||||
<a href="#after-content">AfterContentInit & AfterContentChecked</a>
|
||||
<a href="#counter">Counter: OnChanges + Spy directive</a>
|
||||
|
||||
<a id="hooks"></a>
|
||||
<peek-a-boo-parent></peek-a-boo-parent>
|
||||
<peek-a-boo-parent id="hooks"></peek-a-boo-parent>
|
||||
<a href="#top">back to top</a>
|
||||
<hr />
|
||||
|
||||
<a id="spy"></a>
|
||||
<spy-parent></spy-parent>
|
||||
<spy-parent id="spy"></spy-parent>
|
||||
<a href="#top">back to top</a>
|
||||
<hr />
|
||||
|
||||
<a id="onchanges"></a>
|
||||
<on-changes-parent></on-changes-parent>
|
||||
<on-changes-parent id="onchanges"></on-changes-parent>
|
||||
<a href="#top">back to top</a>
|
||||
<hr />
|
||||
|
||||
<a id="docheck"></a>
|
||||
<do-check-parent></do-check-parent>
|
||||
<do-check-parent id="docheck"></do-check-parent>
|
||||
<a href="#top">back to top</a>
|
||||
<hr />
|
||||
|
||||
<a id="after-view"></a>
|
||||
<after-view-parent></after-view-parent>
|
||||
<after-view-parent id="after-view"></after-view-parent>
|
||||
<a href="#top">back to top</a>
|
||||
<hr />
|
||||
|
||||
<a id="after-content"></a>
|
||||
<after-content-parent></after-content-parent>
|
||||
<after-content-parent id="after-content"></after-content-parent>
|
||||
<a href="#top">back to top</a>
|
||||
<hr />
|
||||
|
||||
<a id="counter"></a>
|
||||
<counter-parent></counter-parent>
|
||||
<counter-parent id="counter"></counter-parent>
|
||||
<a href="#top">back to top</a>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user