mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
158 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 57cb448de5 | |||
| 0bd668c943 | |||
| 2c0bb7fb0e | |||
| 735cdf3761 | |||
| 948fb5f937 | |||
| 9f1a5fa5da | |||
| cab06bd125 | |||
| cdc5e39532 | |||
| ed8e7c727b | |||
| e08cc50f3c | |||
| ca7456e722 | |||
| b353ad5bac | |||
| bd6d18b977 | |||
| 3b218a72ce | |||
| 4367c44f7f | |||
| b69bf37841 | |||
| 05a05b628b | |||
| c21f41d6d4 | |||
| 413db303f4 | |||
| 752ebd62c9 | |||
| 7001b5d633 | |||
| fccf375df1 | |||
| 8d85de7fe1 | |||
| 4c2b0d94e2 | |||
| 57174bb60b | |||
| 7a4a5253ea | |||
| c5a0e909ac | |||
| d07ce6156e | |||
| e3b874b4fb | |||
| 1ac37f932f | |||
| 8650f0dee2 | |||
| 030d77010d | |||
| 96ca49d145 | |||
| 8264382a6b | |||
| f5a5215166 | |||
| 5cd00f167c | |||
| 22f89c0a2d | |||
| c442912532 | |||
| 9f9dd901ae | |||
| 30e42df518 | |||
| 566499af1d | |||
| 4a5f37d216 | |||
| 74567f1c8a | |||
| 64399b26c0 | |||
| 1b296dbb80 | |||
| cddc426e18 | |||
| 8109013b0f | |||
| f25d7e880f | |||
| f240c70971 | |||
| 0172477ff2 | |||
| 0702d4a560 | |||
| 798aee7a83 | |||
| c477e876e3 | |||
| ecd0daf8fd | |||
| 6eca767811 | |||
| d8d6478d0e | |||
| 0c4d7e061e | |||
| 293dc3b046 | |||
| 8dca5a11f5 | |||
| 45fca0bc95 | |||
| 85819b9cdb | |||
| 87378a773d | |||
| 96dd0c715e | |||
| ccddacf11d | |||
| b216e76838 | |||
| 0d31904a96 | |||
| aa3df2a7d4 | |||
| 50b7901169 | |||
| 1439f059a3 | |||
| ac211d46b0 | |||
| 356984d9ba | |||
| 7ac94dd664 | |||
| 49aff2726f | |||
| 6c14c9324f | |||
| c0c7216c5f | |||
| 25e4f04d9f | |||
| 259707e31c | |||
| d17ba9bc87 | |||
| a4923f1f17 | |||
| e603450592 | |||
| c6861eefa4 | |||
| 910742389f | |||
| 98d545fafa | |||
| 7786e35faa | |||
| 49101330f8 | |||
| 4507704a7e | |||
| 854b839531 | |||
| 89f7b00e59 | |||
| f12b01ec88 | |||
| 0c8aaf082e | |||
| a4f024fb07 | |||
| e2e0f08d99 | |||
| e194fbfbaa | |||
| 8ad1ee764b | |||
| f222bee8fa | |||
| 9a9a00e0f9 | |||
| 5f9cac037b | |||
| 9778326f10 | |||
| fac5a7cf4e | |||
| ae145d19c6 | |||
| 30b6d34a49 | |||
| 3e0b7c1bdc | |||
| a58a35dfe4 | |||
| a7c9fbb087 | |||
| 20811aad27 | |||
| df453c3549 | |||
| 1f6c96a1dc | |||
| 656549d88a | |||
| c0f0baac58 | |||
| d38aa4f103 | |||
| 7397b6648a | |||
| 0fe9331958 | |||
| 7620f506c0 | |||
| 62ad2f0c71 | |||
| 424a26b3c7 | |||
| 18bd1a5ccb | |||
| 8922a11693 | |||
| 4e0341050b | |||
| 0616dc2839 | |||
| dc228c6fc1 | |||
| b1a7249c24 | |||
| 9ff4893bc0 | |||
| 025f7af694 | |||
| 1c4a99a6d1 | |||
| 51d53fc728 | |||
| be882c3efb | |||
| 18fa6d7556 | |||
| bb8a5a4c70 | |||
| 9c00506a26 | |||
| 79e8539ce2 | |||
| 6f79507ea7 | |||
| 6616019735 | |||
| 4d4858fa28 | |||
| 538fe47d9d | |||
| b07c549802 | |||
| 6f6ad0279b | |||
| d08f91f861 | |||
| 28aa7b3188 | |||
| 6ac0a68d73 | |||
| ea19bff302 | |||
| 1112e9196a | |||
| 0d8a5c4665 | |||
| 76ceebad04 | |||
| 99c2d76651 | |||
| 246cb9c381 | |||
| d7c75c6edb | |||
| 0daca457bb | |||
| 12f9ed01cc | |||
| 69713413f7 | |||
| 43d6816f9e | |||
| 4c4fea1304 | |||
| c6df89c827 | |||
| 9cae9b4a8c | |||
| 29b959ce31 | |||
| a24b24e8a4 | |||
| f51c008744 | |||
| 3dedd6a07a | |||
| 881376eb42 |
@@ -4,7 +4,7 @@ description: 'Installs the dependencies using Yarn'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
|
||||
- uses: actions/cache@e12d46a63a90f2fae62d114769bbf2a179198b5c # v3
|
||||
with:
|
||||
path: |
|
||||
./node_modules/
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# This workflow builds the previews for pull requests when a certain label is applied.
|
||||
# The actual deployment happens as part of a dedicated second workflow to avoid security
|
||||
# issues where the building would otherwise occur in an authorized context where secrets
|
||||
# could be leaked. More details can be found here:
|
||||
|
||||
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/.
|
||||
|
||||
name: Build adev for preview deployment
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [synchronize, labeled]
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
adev-build:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'adev: preview') ||
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'adev: preview'))
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e52eb8237f2ed71195f87ce8046467a176568e58
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@e52eb8237f2ed71195f87ce8046467a176568e58
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@e52eb8237f2ed71195f87ce8046467a176568e58
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Build adev to ensure it continues to work
|
||||
run: yarn bazel build --config=aio_local_deps //adev:build
|
||||
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@73777547f819464aa5f48071b966eed34fd2c402
|
||||
with:
|
||||
workflow-artifact-name: 'adev-preview'
|
||||
pull-number: '${{github.event.pull_request.number}}'
|
||||
artifact-build-revision: '${{github.event.pull_request.head.sha}}'
|
||||
deploy-directory: './dist/bin/adev/build/browser'
|
||||
@@ -0,0 +1,50 @@
|
||||
# This workflow runs whenever the ADEV build workflow has completed. Deployment happens
|
||||
# as part of a dedicated second workflow to avoid security issues where the building would
|
||||
# otherwise occur in an authorized context where secrets could be leaked.
|
||||
#
|
||||
# More details can be found here:
|
||||
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/.
|
||||
|
||||
name: Deploying adev preview to Firebase
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['Build adev for preview deployment']
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
# Needed in order to be able to comment on the pull request.
|
||||
pull-requests: write
|
||||
# Needed in order to checkout the repository
|
||||
contents: read
|
||||
# Needed in order to retrieve the artifacts from the previous job
|
||||
actions: read
|
||||
|
||||
env:
|
||||
PREVIEW_PROJECT: ng-dev-previews
|
||||
PREVIEW_SITE: ng-dev-previews-fw
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
|
||||
with:
|
||||
token: '${{secrets.GITHUB_TOKEN}}'
|
||||
|
||||
- name: Configure Firebase deploy target
|
||||
working-directory: ./
|
||||
run: |
|
||||
# We can use `npx` as the Firebase deploy actions uses it too.
|
||||
npx -y firebase-tools@latest target:clear --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs
|
||||
npx -y firebase-tools@latest target:apply --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs ${{env.PREVIEW_SITE}}
|
||||
|
||||
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@73777547f819464aa5f48071b966eed34fd2c402
|
||||
with:
|
||||
github-token: '${{secrets.GITHUB_TOKEN}}'
|
||||
workflow-artifact-name: 'adev-preview'
|
||||
firebase-config-dir: './adev'
|
||||
firebase-public-dir: './adev/build/browser'
|
||||
firebase-project-id: '${{env.PREVIEW_PROJECT}}'
|
||||
firebase-service-key: '${{secrets.FIREBASE_PREVIEW_SERVICE_TOKEN}}'
|
||||
@@ -123,7 +123,7 @@ jobs:
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Build adev to ensure it continues to work
|
||||
run: yarn bazel build --config=aio_local_deps //adev:build
|
||||
run: yarn bazel test --config=aio_local_deps //adev:test
|
||||
|
||||
aio-local:
|
||||
runs-on:
|
||||
@@ -184,7 +184,7 @@ jobs:
|
||||
run: yarn --cwd aio deploy-production
|
||||
- name: Notify about failed deployment
|
||||
if: ${{ failure() }}
|
||||
uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v=v1.24.0
|
||||
uses: slackapi/slack-github-action@2a8087d4af6f83146a87539a70defe909fe6dbe6 # v=v1.24.0
|
||||
with:
|
||||
channel-id: 'C07DT5M6V,CKV1F72BG'
|
||||
slack-message: 'Deploy to aio job failed for ${{ github.base_ref }} branch failed on build ${{ github.event.after }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
@@ -235,9 +235,11 @@ jobs:
|
||||
//packages/zone.js/test/closure:closure_js \
|
||||
|
||||
- run: |
|
||||
rm -Rf packages/zone.js/build
|
||||
rm -Rf packages/zone.js/test/extra/*.umd.js
|
||||
|
||||
mkdir -p packages/zone.js/build/
|
||||
mkdir -p packages/zone.js/build/test/
|
||||
mkdir -p packages/zone.js/test/
|
||||
|
||||
cp dist/bin/packages/zone.js/bundles/zone.umd.js packages/zone.js/build/zone.umd.js
|
||||
cp dist/bin/packages/zone.js/npm_package/bundles/zone-mix.umd.js ./packages/zone.js/test/extra/
|
||||
|
||||
+43
-2
@@ -5,11 +5,35 @@ import {FormatConfig} from '@angular/ng-dev';
|
||||
*/
|
||||
export const format: FormatConfig = {
|
||||
'prettier': {
|
||||
'matchers': ['**/*.{yaml,yml}'],
|
||||
'matchers': [
|
||||
'**/*.{yaml,yml}',
|
||||
'devtools/**/*.{js,ts}',
|
||||
'tools/**/*.{js,ts}',
|
||||
'modules/**/*.{js,ts}',
|
||||
'scripts/**/*.{js,ts}',
|
||||
'packages/animations/**/*.{js,ts}',
|
||||
'packages/bazel/**/*.{js,ts}',
|
||||
'packages/benchpress/**/*.{js,ts}',
|
||||
'packages/common/**/*.{js,ts}',
|
||||
'packages/docs/**/*.{js,ts}',
|
||||
'packages/elements/**/*.{js,ts}',
|
||||
'packages/examples/**/*.{js,ts}',
|
||||
'packages/misc/**/*.{js,ts}',
|
||||
'packages/private/**/*.{js,ts}',
|
||||
'packages/service-worker/**/*.{js,ts}',
|
||||
'packages/upgrade/**/*.{js,ts}',
|
||||
|
||||
// Do not format d.ts files as they are generated
|
||||
'!**/*.d.ts',
|
||||
// Both third_party and .yarn are directories containing copied code which should
|
||||
// not be modified.
|
||||
'!third_party/**',
|
||||
'!.yarn/**',
|
||||
],
|
||||
},
|
||||
'clang-format': {
|
||||
'matchers': [
|
||||
'**/*.{js,ts}',
|
||||
//'**/*.{js,ts}',
|
||||
// TODO: burn down format failures and remove aio and integration exceptions.
|
||||
'!aio/**',
|
||||
'!integration/**',
|
||||
@@ -31,6 +55,23 @@ export const format: FormatConfig = {
|
||||
'!packages/common/src/i18n/currencies.ts',
|
||||
// Temporarily disable formatting for adev
|
||||
'!adev/**',
|
||||
|
||||
// Migrated to prettier
|
||||
'!devtools/**/*.{js,ts}',
|
||||
'!tools/**/*.{js,ts}',
|
||||
'!modules/**/*.{js,ts}',
|
||||
'!scripts/**/*.{js,ts}',
|
||||
'!packages/animations/**/*.{js,ts}',
|
||||
'!packages/bazel/**/*.{js,ts}',
|
||||
'!packages/benchpress/**/*.{js,ts}',
|
||||
'!packages/common/**/*.{js,ts}',
|
||||
'!packages/docs/**/*.{js,ts}',
|
||||
'!packages/elements/**/*.{js,ts}',
|
||||
'!packages/examples/**/*.{js,ts}',
|
||||
'!packages/misc/**/*.{js,ts}',
|
||||
'!packages/private/**/*.{js,ts}',
|
||||
'!packages/service-worker/**/*.{js,ts}',
|
||||
'!packages/upgrade/**/*.{js,ts}',
|
||||
],
|
||||
},
|
||||
'buildifier': true,
|
||||
|
||||
@@ -31,8 +31,6 @@
|
||||
"packages/http/**",
|
||||
"**/.gitignore",
|
||||
"**/.gitkeep",
|
||||
"**/yarn.lock",
|
||||
"**/package.json",
|
||||
"**/third_party/**",
|
||||
"**/tsconfig-build.json",
|
||||
"**/tsconfig-tsec.json",
|
||||
|
||||
+4
-1
@@ -1069,9 +1069,10 @@ groups:
|
||||
])
|
||||
reviewers:
|
||||
users:
|
||||
- twerske
|
||||
- josephperrott
|
||||
- jelbourn
|
||||
- bencodezen
|
||||
- jessicajaniuk
|
||||
|
||||
# =========================================================
|
||||
# Docs-infra
|
||||
@@ -1150,6 +1151,7 @@ groups:
|
||||
- josephperrott
|
||||
- mgechev
|
||||
- twerske
|
||||
- ~jeanmeche
|
||||
|
||||
# =========================================================
|
||||
# Dev-infra
|
||||
@@ -1394,6 +1396,7 @@ groups:
|
||||
- devversion # Paul Gschwendtner
|
||||
- dgp1130 # Doug Parker
|
||||
- dylhunn # Dylan Hunn
|
||||
- jeanmeche # Matthieu Riegler
|
||||
- jelbourn # Jeremy Elbourn
|
||||
- jessicajaniuk # Jessica Janiuk
|
||||
- JiaLiPassion # Jia Li
|
||||
|
||||
+82
-205
@@ -1,3 +1,79 @@
|
||||
<a name="17.1.2"></a>
|
||||
# 17.1.2 (2024-01-31)
|
||||
###
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [ccddacf11d](https://github.com/angular/angular/commit/ccddacf11deaebeda12e1bdb6e93ec401397d352) | fix | cta clickability issue in adev homepage. ([#52905](https://github.com/angular/angular/pull/52905)) |
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [98d545fafa](https://github.com/angular/angular/commit/98d545fafa7fc3b1fb3ae049ce655e33ef9bd423) | fix | cleanup DOM elements when root view is removed with async animations ([#53033](https://github.com/angular/angular/pull/53033)) |
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [cdc5e39532](https://github.com/angular/angular/commit/cdc5e3953237a192beafd6330f9d9e36ede34f2c) | fix | The date pipe should return ISO format for week and week-year as intended in the unit test. ([#53879](https://github.com/angular/angular/pull/53879)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [f12b01ec88](https://github.com/angular/angular/commit/f12b01ec88eaf18041c2e46335428627aa0d7744) | fix | Update type check block to fix control flow source mappings ([#53980](https://github.com/angular/angular/pull/53980)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [c477e876e3](https://github.com/angular/angular/commit/c477e876e39495b855b096440d53cf1dd1ad33c6) | fix | change defer block fixture default behavior to playthrough ([#54088](https://github.com/angular/angular/pull/54088)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8264382a6b](https://github.com/angular/angular/commit/8264382a6bf389fb3fca75fa2d6c0a2aa5a1e42f) | fix | error in standalone migration when non-array value is used as declarations in TestBed ([#54122](https://github.com/angular/angular/pull/54122)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.1.1"></a>
|
||||
# 17.1.1 (2024-01-24)
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [f222bee8fa](https://github.com/angular/angular/commit/f222bee8fa037f437761e5f7f127f22f280e9154) | fix | revert commit that replaced `last` helper with native `Array.at(-1)` ([#54021](https://github.com/angular/angular/pull/54021)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.1.0"></a>
|
||||
# 17.1.0 (2024-01-17)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [79ff91a813](https://github.com/angular/angular/commit/79ff91a813e544929cb5eb5f9aab762a9f3d0435) | fix | allow TS jsDocParsingMode host option to be programmatically set ([#53126](https://github.com/angular/angular/pull/53126)) |
|
||||
| [5613051a8b](https://github.com/angular/angular/commit/5613051a8bd2626ae347292807b2bf21085c4c02) | fix | allow TS jsDocParsingMode host option to be programmatically set again ([#53292](https://github.com/angular/angular/pull/53292)) |
|
||||
| [df8a825910](https://github.com/angular/angular/commit/df8a825910951bebf34a4eede42f3ce5cd3e6fb7) | fix | project empty block root node ([#53620](https://github.com/angular/angular/pull/53620)) |
|
||||
| [478d622265](https://github.com/angular/angular/commit/478d6222650884478314985e3d5132587c4f670c) | fix | project empty block root node in template pipeline ([#53620](https://github.com/angular/angular/pull/53620)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [abdc7e4578](https://github.com/angular/angular/commit/abdc7e45786667e4283912024a641975f1917d97) | feat | support type-checking for generic signal inputs ([#53521](https://github.com/angular/angular/pull/53521)) |
|
||||
| [e620b3a724](https://github.com/angular/angular/commit/e620b3a724cb615af24b7779c0ab492d24efb8cc) | fix | add compiler option to disable control flow content projection diagnostic ([#53311](https://github.com/angular/angular/pull/53311)) |
|
||||
| [4c1d69e288](https://github.com/angular/angular/commit/4c1d69e2880f22745c820eee630d10071e4fa86b) | fix | add diagnostic for control flow that prevents content projection ([#53190](https://github.com/angular/angular/pull/53190)) |
|
||||
| [76ceebad04](https://github.com/angular/angular/commit/76ceebad047f62972654a8c934c77d8d02d9fa14) | fix | do not throw fatal error if extended type check fails ([#53896](https://github.com/angular/angular/pull/53896)) |
|
||||
| [1a6eaa0fea](https://github.com/angular/angular/commit/1a6eaa0fea1024b919e17ac9d2e8c07df7916de8) | fix | input transform in local compilation mode ([#53645](https://github.com/angular/angular/pull/53645)) |
|
||||
| [56a76d73e0](https://github.com/angular/angular/commit/56a76d73e037aeea1975808d5c51608fd23d4fa6) | fix | modify `getConstructorDependencies` helper to work with reflection host after the previous change ([#52215](https://github.com/angular/angular/pull/52215)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [863be4b698](https://github.com/angular/angular/commit/863be4b6981dc60ca0610b0e61d2ba1f5759e2a3) | feat | expose new `input` API for signal-based inputs ([#53872](https://github.com/angular/angular/pull/53872)) |
|
||||
| [94096c6ede](https://github.com/angular/angular/commit/94096c6ede67436a349ae07901f2bb418bf9f461) | feat | support TypeScript 5.3 ([#52572](https://github.com/angular/angular/pull/52572)) |
|
||||
| [69b384c0d1](https://github.com/angular/angular/commit/69b384c0d16f631741339d8757c32ef08260cfce) | fix | `SignalNode` reactive node incorrectly exposing unset field ([#53571](https://github.com/angular/angular/pull/53571)) |
|
||||
| [6f79507ea7](https://github.com/angular/angular/commit/6f79507ea7f272d8d09250e222ca831f407867d8) | fix | Change defer block fixture default behavior to playthrough ([#53956](https://github.com/angular/angular/pull/53956)) |
|
||||
| [32f908ab70](https://github.com/angular/angular/commit/32f908ab70f1b9ed3f92df1cae05ddde68932404) | fix | do not accidentally inherit input transforms when overridden ([#53571](https://github.com/angular/angular/pull/53571)) |
|
||||
| [bdd61c768a](https://github.com/angular/angular/commit/bdd61c768a28b56c68634b99c036986499829f45) | fix | replace assertion with more intentional error ([#52234](https://github.com/angular/angular/pull/52234)) |
|
||||
| [0daca457bb](https://github.com/angular/angular/commit/0daca457bb5bb6ffe14b7037264f8497eb5b3daf) | fix | TestBed should still use the microtask queue to schedule effects ([#53843](https://github.com/angular/angular/pull/53843)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5c1d441029](https://github.com/angular/angular/commit/5c1d4410298e20cb03d7a1ddf7931538b6a181b4) | feat | Add info property to `NavigationExtras` ([#53303](https://github.com/angular/angular/pull/53303)) |
|
||||
| [50d7916278](https://github.com/angular/angular/commit/50d79162785bb8d3e158a7a4a3733f4c75d3b127) | feat | Add router configuration to resolve navigation promise on error ([#48910](https://github.com/angular/angular/pull/48910)) |
|
||||
| [a5a9b408e2](https://github.com/angular/angular/commit/a5a9b408e2eb64dcf1d3ca16da4897649dd2fc34) | feat | Add transient info to RouterLink input ([#53784](https://github.com/angular/angular/pull/53784)) |
|
||||
| [726530a9af](https://github.com/angular/angular/commit/726530a9af9c8daf7295cc3548f24e70f380d70e) | feat | Allow `onSameUrlNavigation: 'ignore'` in `navigateByUrl` ([#52265](https://github.com/angular/angular/pull/52265)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.0.9"></a>
|
||||
# 17.0.9 (2024-01-10)
|
||||
### common
|
||||
@@ -30,36 +106,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.1.0-next.5"></a>
|
||||
# 17.1.0-next.5 (2023-12-21)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [3a689c2050](https://github.com/angular/angular/commit/3a689c20509ed5972b3831988fc0014d293d0cc3) | fix | correctly intercept index in loop tracking function ([#53604](https://github.com/angular/angular/pull/53604)) |
|
||||
| [df8a825910](https://github.com/angular/angular/commit/df8a825910951bebf34a4eede42f3ce5cd3e6fb7) | fix | project empty block root node ([#53620](https://github.com/angular/angular/pull/53620)) |
|
||||
| [478d622265](https://github.com/angular/angular/commit/478d6222650884478314985e3d5132587c4f670c) | fix | project empty block root node in template pipeline ([#53620](https://github.com/angular/angular/pull/53620)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [aecb675fa5](https://github.com/angular/angular/commit/aecb675fa5059e510e0223e00fa114d92e799b04) | fix | avoid repeated work when parsing version ([#53598](https://github.com/angular/angular/pull/53598)) |
|
||||
| [872e7f25fe](https://github.com/angular/angular/commit/872e7f25fea8a01f980e4e406ad382a9e187ce5c) | fix | tree shake version class ([#53598](https://github.com/angular/angular/pull/53598)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [d49333edc3](https://github.com/angular/angular/commit/d49333edc3fe170c57d953aa2d8df58d0db379e0) | fix | cf migration - detect and error when result is invalid i18n nesting ([#53638](https://github.com/angular/angular/pull/53638)) ([#53639](https://github.com/angular/angular/pull/53639)) |
|
||||
| [8e2178792d](https://github.com/angular/angular/commit/8e2178792d474e84817c27851cd6b9600deb15b5) | fix | cf migration - ensure full check runs for all imports ([#53637](https://github.com/angular/angular/pull/53637)) |
|
||||
| [22b95de9bc](https://github.com/angular/angular/commit/22b95de9bcb92267b8f848a1aafa71af70d5577b) | fix | cf migration - fix bug in attribute formatting ([#53636](https://github.com/angular/angular/pull/53636)) |
|
||||
| [b40bb22a66](https://github.com/angular/angular/commit/b40bb22a66a3c2ae478778f3ef0be0ce971a1b2c) | fix | cf migration - improve import declaration handling ([#53622](https://github.com/angular/angular/pull/53622)) |
|
||||
| [8bf752539f](https://github.com/angular/angular/commit/8bf752539f6b4b5955e3d61a76423910af55181b) | fix | cf migration - preserve indentation on attribute strings ([#53625](https://github.com/angular/angular/pull/53625)) |
|
||||
| [7bb312fcf6](https://github.com/angular/angular/commit/7bb312fcf612c57d6606ae14f21707f10d8ff9ce) | fix | cf migration - stop removing empty newlines from i18n blocks ([#53578](https://github.com/angular/angular/pull/53578)) |
|
||||
| [db6b4a6bc4](https://github.com/angular/angular/commit/db6b4a6bc479b0fac0a4a99e1a202c38d1ea8944) | fix | Fix cf migration bug with parsing for loop conditions properly ([#53558](https://github.com/angular/angular/pull/53558)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [502f300757](https://github.com/angular/angular/commit/502f3007575a5445c2876c92548415cb003582b1) | fix | Should not freeze original object used for route data ([#53635](https://github.com/angular/angular/pull/53635)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.0.8"></a>
|
||||
# 17.0.8 (2023-12-21)
|
||||
### compiler
|
||||
@@ -89,45 +135,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.1.0-next.4"></a>
|
||||
# 17.1.0-next.4 (2023-12-13)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [b98d8f79ed](https://github.com/angular/angular/commit/b98d8f79ed4979fb91661dbdfb6266a917b41474) | fix | handle ambient types in input transform function ([#51474](https://github.com/angular/angular/pull/51474)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [9e5456912a](https://github.com/angular/angular/commit/9e5456912a699315f1f5e8458d35df946c01dc12) | fix | generate less type checking code in for loops ([#53515](https://github.com/angular/angular/pull/53515)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [2565121851](https://github.com/angular/angular/commit/25651218512becb3a491b1c94d643a5066a6d3cb) | fix | Avoid refreshing a host view twice when using transplanted views ([#53021](https://github.com/angular/angular/pull/53021)) |
|
||||
| [629343f247](https://github.com/angular/angular/commit/629343f24741760e76d98ea1005debaf4775bfc7) | fix | Multiple subscribers to ApplicationRef.isStable should all see values ([#53541](https://github.com/angular/angular/pull/53541)) |
|
||||
| [42f4f70e97](https://github.com/angular/angular/commit/42f4f70e97b47f7ffcf15e8af2c1a3fa3f393903) | fix | remove signal equality check short-circuit ([#53446](https://github.com/angular/angular/pull/53446)) |
|
||||
| [f35adcb9b2](https://github.com/angular/angular/commit/f35adcb9b255741515d54bbce694f4cd34aac249) | fix | update feature usage marker ([#53542](https://github.com/angular/angular/pull/53542)) |
|
||||
| [1fc5442947](https://github.com/angular/angular/commit/1fc5442947b07be7c11fe7c79b9e69f9458c8108) | perf | avoid changes Observable creation on QueryList ([#53498](https://github.com/angular/angular/pull/53498)) |
|
||||
| [e3a6bf9b6c](https://github.com/angular/angular/commit/e3a6bf9b6c3bef03df9bfc8f05b817bc875cbad6) | perf | optimize memory allocation when reconcilling lists ([#52245](https://github.com/angular/angular/pull/52245)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a02767956a](https://github.com/angular/angular/commit/a02767956a00e0b44b690411f01584f6660c83ea) | fix | CF Migration - ensure bound ngIfElse cases ignore line breaks ([#53435](https://github.com/angular/angular/pull/53435)) |
|
||||
| [6aa1bb78e8](https://github.com/angular/angular/commit/6aa1bb78e8b9ca866b3c2d84f4005a0e67f2b6bc) | fix | cf migration - undo changes when html fails to parse post migration ([#53530](https://github.com/angular/angular/pull/53530)) |
|
||||
| [cc02852ac4](https://github.com/angular/angular/commit/cc02852ac44bfea13e8b9d5768694a094fc2c4b5) | fix | CF migration only remove newlines of changed template content ([#53508](https://github.com/angular/angular/pull/53508)) |
|
||||
| [ce1076785c](https://github.com/angular/angular/commit/ce1076785c98ecc9660637cafa4a62a63be8d315) | fix | cf migration validate structure of ngswitch before migrating ([#53530](https://github.com/angular/angular/pull/53530)) |
|
||||
| [1f5c8bf116](https://github.com/angular/angular/commit/1f5c8bf116783c25234ab7fa301a0be842e58007) | fix | ensure we do not overwrite prior template replacements in migration ([#53393](https://github.com/angular/angular/pull/53393)) |
|
||||
| [79f791543b](https://github.com/angular/angular/commit/79f791543b607eea1bb6a28b9c744760ba4fd028) | fix | fix cf migration import removal when errors occur ([#53502](https://github.com/angular/angular/pull/53502)) |
|
||||
### platform-browser
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [fdb9cb7a5b](https://github.com/angular/angular/commit/fdb9cb7a5be4695abaa60efc89ecd50ddb9e9e6e) | fix | Get correct base path when using "." as base href when serving from the file:// protocol. ([#53547](https://github.com/angular/angular/pull/53547)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [48c5041687](https://github.com/angular/angular/commit/48c50416872313f23d2759232c990cf8d2de6996) | fix | provide more actionable error message when route is not matched in production mode ([#53523](https://github.com/angular/angular/pull/53523)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.0.7"></a>
|
||||
# 17.0.7 (2023-12-13)
|
||||
### compiler
|
||||
@@ -167,47 +174,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.1.0-next.3"></a>
|
||||
# 17.1.0-next.3 (2023-12-06)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5613051a8b](https://github.com/angular/angular/commit/5613051a8bd2626ae347292807b2bf21085c4c02) | fix | allow TS jsDocParsingMode host option to be programmatically set again ([#53292](https://github.com/angular/angular/pull/53292)) |
|
||||
| [77ac4cd324](https://github.com/angular/angular/commit/77ac4cd32491d0c994cb4ea50372601c955cec3d) | fix | generate proper code for nullish coalescing in styling host bindings ([#53305](https://github.com/angular/angular/pull/53305)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e620b3a724](https://github.com/angular/angular/commit/e620b3a724cb615af24b7779c0ab492d24efb8cc) | fix | add compiler option to disable control flow content projection diagnostic ([#53311](https://github.com/angular/angular/pull/53311)) |
|
||||
| [d7a83f9521](https://github.com/angular/angular/commit/d7a83f95213cdd5d3ceefbc95fa1190856b0198c) | fix | avoid conflicts with built-in global variables in for loop blocks ([#53319](https://github.com/angular/angular/pull/53319)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [77939a3bd3](https://github.com/angular/angular/commit/77939a3bd39073d07e24797a4632ec2d2a6b92e0) | fix | cleanup signal consumers for all views ([#53351](https://github.com/angular/angular/pull/53351)) |
|
||||
| [899f6c4a12](https://github.com/angular/angular/commit/899f6c4a12127f87aeedf47ee128ce949ebe717c) | fix | handle hydration of multiple nodes projected in a single slot ([#53270](https://github.com/angular/angular/pull/53270)) |
|
||||
| [4b23221b4e](https://github.com/angular/angular/commit/4b23221b4e5f8be7bcffc8ace255143653550d3d) | fix | support hydration for cases when content is re-projected using ng-template ([#53304](https://github.com/angular/angular/pull/53304)) |
|
||||
| [82609d471c](https://github.com/angular/angular/commit/82609d471c9802b847a5654918eca1ba3ebb29b3) | fix | support swapping hydrated views in `@for` loops ([#53274](https://github.com/angular/angular/pull/53274)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [1c1e8c477b](https://github.com/angular/angular/commit/1c1e8c477b8d5c8deecf05744bd1d5f7c86d2e14) | fix | CF migration - ensure NgIfElse attributes are properly removed ([#53298](https://github.com/angular/angular/pull/53298)) |
|
||||
| [2998d482dd](https://github.com/angular/angular/commit/2998d482dd3f1f5ff7f08260c3947ded74dac126) | fix | CF Migration - Fix case of aliases on i18n ng-templates preventing removal ([#53299](https://github.com/angular/angular/pull/53299)) |
|
||||
| [aad5e5bd0e](https://github.com/angular/angular/commit/aad5e5bd0e7f6581f51fc3a23c98a7f11219a8f8) | fix | CF Migration add support for ngIf with just a then ([#53297](https://github.com/angular/angular/pull/53297)) |
|
||||
| [6f75471307](https://github.com/angular/angular/commit/6f75471307a9458d0cffd0ee2bbf4190640c3c3a) | fix | CF Migration fix missing alias for bound ngifs ([#53296](https://github.com/angular/angular/pull/53296)) |
|
||||
| [2a5a8f6f05](https://github.com/angular/angular/commit/2a5a8f6f052961c010a68a05d868f50220f2fcf2) | fix | Change CF Migration ng-template placeholder generation and handling ([#53394](https://github.com/angular/angular/pull/53394)) |
|
||||
| [03e2f1bb25](https://github.com/angular/angular/commit/03e2f1bb25693d1a5f4e53fc4f4cd1937cf6bda1) | fix | fix regexp for else and then in cf migration ([#53257](https://github.com/angular/angular/pull/53257)) |
|
||||
| [f4a96a9160](https://github.com/angular/angular/commit/f4a96a9160927903c38e172f6375c4bc5f8e0905) | fix | handle aliases on bound ngIf migrations ([#53261](https://github.com/angular/angular/pull/53261)) |
|
||||
| [5a0ed28c9d](https://github.com/angular/angular/commit/5a0ed28c9d3f1c7507feee482e37c3346d52b033) | fix | handle nested ng-template replacement safely in CF migration ([#53368](https://github.com/angular/angular/pull/53368)) |
|
||||
| [01b18a4248](https://github.com/angular/angular/commit/01b18a4248f068df33c0ca8a2d62ef2fc69f941c) | fix | handle templates outside of component in cf migration ([#53368](https://github.com/angular/angular/pull/53368)) |
|
||||
| [9834fd2738](https://github.com/angular/angular/commit/9834fd27387ed5edc65bed56aa003fc45d250420) | fix | remove setting that removes comments in CF migration ([#53350](https://github.com/angular/angular/pull/53350)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5c1d441029](https://github.com/angular/angular/commit/5c1d4410298e20cb03d7a1ddf7931538b6a181b4) | feat | Add info property to `NavigationExtras` ([#53303](https://github.com/angular/angular/pull/53303)) |
|
||||
| [50d7916278](https://github.com/angular/angular/commit/50d79162785bb8d3e158a7a4a3733f4c75d3b127) | feat | Add router configuration to resolve navigation promise on error ([#48910](https://github.com/angular/angular/pull/48910)) |
|
||||
| [1940280d27](https://github.com/angular/angular/commit/1940280d27bb3ece585d6345dbd742d208f46912) | fix | Ensure canMatch guards run on wildcard routes ([#53239](https://github.com/angular/angular/pull/53239)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.0.6"></a>
|
||||
# 17.0.6 (2023-12-06)
|
||||
### compiler
|
||||
@@ -247,43 +213,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.1.0-next.2"></a>
|
||||
# 17.1.0-next.2 (2023-11-29)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [79ff91a813](https://github.com/angular/angular/commit/79ff91a813e544929cb5eb5f9aab762a9f3d0435) | fix | allow TS jsDocParsingMode host option to be programmatically set ([#53126](https://github.com/angular/angular/pull/53126)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4c1d69e288](https://github.com/angular/angular/commit/4c1d69e2880f22745c820eee630d10071e4fa86b) | fix | add diagnostic for control flow that prevents content projection ([#53190](https://github.com/angular/angular/pull/53190)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [58cf389d80](https://github.com/angular/angular/commit/58cf389d8095dd522a998b6b9a7d7b8da1656644) | fix | avoid stale provider info when TestBed.overrideProvider is used ([#52918](https://github.com/angular/angular/pull/52918)) |
|
||||
| [c7c7ea9813](https://github.com/angular/angular/commit/c7c7ea9813f6dcf91c096bb37d36bfe0c715a04f) | fix | inherit host directives ([#52992](https://github.com/angular/angular/pull/52992)) |
|
||||
| [b35c6731e5](https://github.com/angular/angular/commit/b35c6731e51de9c33707010fc780cbaa559be6c3) | fix | Reattached views that are dirty from a signal update should refresh ([#53001](https://github.com/angular/angular/pull/53001)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [dbca1c9d61](https://github.com/angular/angular/commit/dbca1c9d618be4d195d779ef80606c6d22f7c977) | fix | Add ngForTemplate support to control flow migration ([#53076](https://github.com/angular/angular/pull/53076)) |
|
||||
| [53912fdf74](https://github.com/angular/angular/commit/53912fdf74e1224f95edc3dd550c51451e613c44) | fix | allows colons in ngIf else cases to migrate ([#53076](https://github.com/angular/angular/pull/53076)) |
|
||||
| [fadfee4324](https://github.com/angular/angular/commit/fadfee43247acec7e450d5cc929a0a6780a54bd1) | fix | cf migration fix migrating empty switch default ([#53237](https://github.com/angular/angular/pull/53237)) |
|
||||
| [b2aeaf5d97](https://github.com/angular/angular/commit/b2aeaf5d97327842a5bb657e9f02bb4f1358304b) | fix | CF migration log warning when collection aliasing detected in `@for` ([#53238](https://github.com/angular/angular/pull/53238)) |
|
||||
| [c6326289f8](https://github.com/angular/angular/commit/c6326289f87089b3415ba0f3e371206b8a396b66) | fix | cf migration removes unnecessary bound ngifelse attribute ([#53236](https://github.com/angular/angular/pull/53236)) |
|
||||
| [e6f10e81d2](https://github.com/angular/angular/commit/e6f10e81d2486b5cc1f468777b7e1057d3f09dc5) | fix | control flow migration formatting fixes ([#53076](https://github.com/angular/angular/pull/53076)) |
|
||||
| [6291c8db09](https://github.com/angular/angular/commit/6291c8db092c0ab67f6789cc03c19dfa0bdc251d) | fix | fix off by one issue with template removal in CF migration ([#53255](https://github.com/angular/angular/pull/53255)) |
|
||||
| [f1b7d40057](https://github.com/angular/angular/commit/f1b7d400575a6a02a2fc2b642c2b8c15cca9b3c4) | fix | fixes CF migration i18n ng-template offsets ([#53212](https://github.com/angular/angular/pull/53212)) |
|
||||
| [a738b48717](https://github.com/angular/angular/commit/a738b48717e16613b34c3e50480cd49f2a31858f) | fix | fixes control flow migration common module removal ([#53076](https://github.com/angular/angular/pull/53076)) |
|
||||
| [a3599515bb](https://github.com/angular/angular/commit/a3599515bb2513fbcb2899284c7f6b4bc021ddaf) | fix | properly handle ngIfThen cases in CF migration ([#53256](https://github.com/angular/angular/pull/53256)) |
|
||||
| [8a52674faa](https://github.com/angular/angular/commit/8a52674faacefd4042726383fdf0aed59a04fb7e) | fix | Update CF migration to skip templates with duplicate ng-template names ([#53204](https://github.com/angular/angular/pull/53204)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [29e0834c4d](https://github.com/angular/angular/commit/29e0834c4deecfa8bf384b5e4359796c8123afcd) | fix | Resolvers in different parts of the route tree should be able to execute together ([#52934](https://github.com/angular/angular/pull/52934)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.0.5"></a>
|
||||
# 17.0.5 (2023-11-29)
|
||||
### core
|
||||
@@ -313,39 +242,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.1.0-next.1"></a>
|
||||
# 17.1.0-next.1 (2023-11-20)
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [29c5416d14](https://github.com/angular/angular/commit/29c5416d14638a05a894269aa5dbe67e98754418) | fix | remove `load` on image once it fails to load ([#52990](https://github.com/angular/angular/pull/52990)) |
|
||||
| [7affa57754](https://github.com/angular/angular/commit/7affa5775427e92ef6e949c879765b7c8aa172da) | fix | scan images once page is loaded ([#52991](https://github.com/angular/angular/pull/52991)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [ec2d6e7b9c](https://github.com/angular/angular/commit/ec2d6e7b9c2b386247d1320ee89f8e3ac5e5a0dd) | fix | changed after checked error in for loops ([#52935](https://github.com/angular/angular/pull/52935)) |
|
||||
| [406049b95e](https://github.com/angular/angular/commit/406049b95e5234f17a7a18839ac848640f53fdde) | fix | generate i18n instructions for blocks ([#52958](https://github.com/angular/angular/pull/52958)) |
|
||||
| [d9d566d315](https://github.com/angular/angular/commit/d9d566d31540582d73201675d0b8ed901261669e) | fix | nested for loops incorrectly calculating computed variables ([#52931](https://github.com/angular/angular/pull/52931)) |
|
||||
| [5fb707f81a](https://github.com/angular/angular/commit/5fb707f81aee43751e61d2ed0861afc9b85bc85a) | fix | produce placeholder for blocks in i18n bundles ([#52958](https://github.com/angular/angular/pull/52958)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [b4d022e230](https://github.com/angular/angular/commit/b4d022e230ca141b12437949d11dc384bfe5c082) | fix | add diagnostic for control flow that prevents content projection ([#52726](https://github.com/angular/angular/pull/52726)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [ed0fbd4071](https://github.com/angular/angular/commit/ed0fbd4071339b1af22d82bac07d51c6c41790cd) | fix | cleanup loading promise when no dependencies are defined ([#53031](https://github.com/angular/angular/pull/53031)) |
|
||||
| [1ce31d819b](https://github.com/angular/angular/commit/1ce31d819b2e4f4425a41f07167a6edce98e77e1) | fix | handle local refs when `getDeferBlocks` is invoked in tests ([#52973](https://github.com/angular/angular/pull/52973)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e33f6e0f1a](https://github.com/angular/angular/commit/e33f6e0f1a483cad908fa6d7376d62332797499c) | fix | control flow migration fails for async pipe with unboxing of observable ([#52756](https://github.com/angular/angular/pull/52756)) ([#52972](https://github.com/angular/angular/pull/52972)) |
|
||||
| [5564d020cd](https://github.com/angular/angular/commit/5564d020cdcea8273b65cf69c45c3f935195af66) | fix | Fixes control flow migration if then else case ([#53006](https://github.com/angular/angular/pull/53006)) |
|
||||
| [28f6cbf9c9](https://github.com/angular/angular/commit/28f6cbf9c91f957b4926fe34610387e1f1919d4f) | fix | fixes migrations of nested switches in control flow ([#53010](https://github.com/angular/angular/pull/53010)) |
|
||||
| [e090b48bf8](https://github.com/angular/angular/commit/e090b48bf8534761d46523be57a7889a325bcdec) | fix | tweaks to formatting in control flow migration ([#53058](https://github.com/angular/angular/pull/53058)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.0.4"></a>
|
||||
# 17.0.4 (2023-11-20)
|
||||
### common
|
||||
@@ -379,24 +275,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.1.0-next.0"></a>
|
||||
# 17.1.0-next.0 (2023-11-15)
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [56a76d73e0](https://github.com/angular/angular/commit/56a76d73e037aeea1975808d5c51608fd23d4fa6) | fix | modify `getConstructorDependencies` helper to work with reflection host after the previous change ([#52215](https://github.com/angular/angular/pull/52215)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [94096c6ede](https://github.com/angular/angular/commit/94096c6ede67436a349ae07901f2bb418bf9f461) | feat | support TypeScript 5.3 ([#52572](https://github.com/angular/angular/pull/52572)) |
|
||||
| [bdd61c768a](https://github.com/angular/angular/commit/bdd61c768a28b56c68634b99c036986499829f45) | fix | replace assertion with more intentional error ([#52234](https://github.com/angular/angular/pull/52234)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [726530a9af](https://github.com/angular/angular/commit/726530a9af9c8daf7295cc3548f24e70f380d70e) | feat | Allow `onSameUrlNavigation: 'ignore'` in `navigateByUrl` ([#52265](https://github.com/angular/angular/pull/52265)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.0.3"></a>
|
||||
# 17.0.3 (2023-11-15)
|
||||
### animations
|
||||
@@ -489,17 +367,16 @@ Live long and prosper 🖖🏻
|
||||
|
||||
Node.js v16 is planned to be End-of-Life on 2023-09-11. Angular will stop supporting Node.js v16 in Angular v17. For Node.js release schedule details, please see: https://github.com/nodejs/release#release-schedule
|
||||
### common
|
||||
-
|
||||
- the NgSwitch directive now defaults to the === equality operator,
|
||||
migrating from the previously used ==. NgSwitch expressions and / or
|
||||
migrating from the previously used == operator. NgSwitch expressions and / or
|
||||
individual condition values need adjusting to this stricter equality
|
||||
check. The added warning message should help pinpointing NgSwitch
|
||||
usages where adjustements are needed.
|
||||
check. The added warning message should help pin-pointing NgSwitch
|
||||
usages where adjustments are needed.
|
||||
### core
|
||||
- Angular now required `zone.js` version `~0.14.0`
|
||||
- Angular now requires `zone.js` version `~0.14.0`
|
||||
- Versions of TypeScript older than 5.2 are no longer supported.
|
||||
- The `mutate` method was removed from the `WritableSignal` interface and completely
|
||||
dropped from the public API surface. As an alternative please use the update method and
|
||||
dropped from the public API surface. As an alternative, please use the `update` method and
|
||||
make immutable changes to the object.
|
||||
|
||||
Example before:
|
||||
@@ -597,7 +474,7 @@ Live long and prosper 🖖🏻
|
||||
- Swapping out the context object for `EmbeddedViewRef`
|
||||
is no longer supported. Support for this was introduced with v12.0.0, but
|
||||
this pattern is rarely used. There is no replacement, but you can use
|
||||
simple assignments in most cases, or `Object.assign , or alternatively
|
||||
simple assignments in most cases, or `Object.assign` , or alternatively
|
||||
still replace the full object by using a `Proxy` (see `NgTemplateOutlet`
|
||||
as an example).
|
||||
|
||||
|
||||
@@ -61,6 +61,25 @@ Get started with Angular, learn the fundamentals and explore advanced topics on
|
||||
- [Lazy Loading][lazyloading]
|
||||
- [Animations][animations]
|
||||
|
||||
### Local Development
|
||||
|
||||
To contribute to Angular docs, you can setup a local environment with the following commands:
|
||||
|
||||
```bash
|
||||
# Clone Angular repo
|
||||
git clone https://github.com/angular/angular.git
|
||||
|
||||
# Navigate to project directory
|
||||
cd angular
|
||||
|
||||
# Install dependencies
|
||||
yarn
|
||||
|
||||
# Build and run local dev server
|
||||
# Note: Initial build will take some time
|
||||
yarn docs
|
||||
```
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
@@ -196,10 +196,10 @@ cldr_xml_data_repository(
|
||||
# sass rules
|
||||
http_archive(
|
||||
name = "io_bazel_rules_sass",
|
||||
sha256 = "7a90567717c3417a23ff4b21963a698a6e9aa669ac9e682121ea33f218a2816a",
|
||||
strip_prefix = "rules_sass-425ecafa8268bc013d684216363fe75bc802323e",
|
||||
sha256 = "386ef2b97bd4342e45db450ce7eb5acb343d22f7580c0bbae89186dc009fa14e",
|
||||
strip_prefix = "rules_sass-d970cb53ad159e7fd1d3a99a00a530cc2c83eff9",
|
||||
urls = [
|
||||
"https://github.com/bazelbuild/rules_sass/archive/425ecafa8268bc013d684216363fe75bc802323e.zip",
|
||||
"https://github.com/bazelbuild/rules_sass/archive/d970cb53ad159e7fd1d3a99a00a530cc2c83eff9.zip",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
+40
-2
@@ -1,6 +1,6 @@
|
||||
load("//:packages.bzl", "link_packages")
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin")
|
||||
load("@npm//@angular-devkit/architect-cli:index.bzl", "architect")
|
||||
load("@npm//@angular-devkit/architect-cli:index.bzl", "architect", "architect_test")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
@@ -21,6 +21,13 @@ APPLICATION_FILES = [
|
||||
],
|
||||
)
|
||||
|
||||
TEST_FILES = APPLICATION_FILES + [
|
||||
"karma.conf.js",
|
||||
"tsconfig.spec.json",
|
||||
] + glob(
|
||||
["**/*.spec.ts"],
|
||||
)
|
||||
|
||||
APPLICATION_DEPS = link_packages([
|
||||
"@npm//@angular-devkit/build-angular",
|
||||
"@npm//@angular/animations",
|
||||
@@ -69,6 +76,21 @@ APPLICATION_DEPS = link_packages([
|
||||
"@npm//angular-split",
|
||||
])
|
||||
|
||||
TEST_DEPS = APPLICATION_DEPS + link_packages([
|
||||
"@npm//@angular/platform-browser-dynamic",
|
||||
"@npm//@angular/build-tooling/bazel/browsers/chromium",
|
||||
"@npm//@types/jasmine",
|
||||
"@npm//@types/node",
|
||||
"@npm//assert",
|
||||
"@npm//jasmine",
|
||||
"@npm//jasmine-core",
|
||||
"@npm//karma-chrome-launcher",
|
||||
"@npm//karma-coverage",
|
||||
"@npm//karma-jasmine",
|
||||
"@npm//karma-jasmine-html-reporter",
|
||||
"//aio/tools:windows-chromium-path",
|
||||
])
|
||||
|
||||
copy_to_bin(
|
||||
name = "application_files_bin",
|
||||
srcs = APPLICATION_FILES,
|
||||
@@ -78,7 +100,7 @@ architect(
|
||||
name = "build",
|
||||
args = [
|
||||
"angular-dev:build",
|
||||
"--output-path=build-app",
|
||||
"--output-path=build",
|
||||
],
|
||||
chdir = "$(RULEDIR)",
|
||||
data = APPLICATION_DEPS + [
|
||||
@@ -100,3 +122,19 @@ architect(
|
||||
":application_files_bin",
|
||||
],
|
||||
)
|
||||
|
||||
architect_test(
|
||||
name = "test",
|
||||
args = [
|
||||
"angular-dev:test",
|
||||
"--no-watch",
|
||||
],
|
||||
chdir = package_name(),
|
||||
data = TEST_DEPS + TEST_FILES,
|
||||
env = {
|
||||
"CHROME_BIN": "../$(CHROMIUM)",
|
||||
},
|
||||
toolchains = [
|
||||
"@npm//@angular/build-tooling/bazel/browsers/chromium:toolchain_alias",
|
||||
],
|
||||
)
|
||||
|
||||
+2
-2
@@ -26,8 +26,7 @@
|
||||
"index": "src/index.html",
|
||||
"browser": "src/main.ts",
|
||||
"server": "src/main.server.ts",
|
||||
// TODO(josephperrott): enabled prerender
|
||||
"prerender": false,
|
||||
"prerender": true,
|
||||
"polyfills": ["src/polyfills.ts", "zone.js"],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
@@ -89,6 +88,7 @@
|
||||
"polyfills": ["zone.js", "zone.js/testing"],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"include": ["src/app"],
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": ["src/favicon.ico", "src/assets"],
|
||||
"styles": ["@angular/docs/styles/global-styles.scss"],
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"hosting": {
|
||||
"target": "angular-docs",
|
||||
"public": "./build/browser",
|
||||
"ignore": ["**/.*"],
|
||||
"headers": [
|
||||
{
|
||||
"source": "assets/tutorials/common/*.jpg",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Cross-Origin-Resource-Policy",
|
||||
"value": "cross-origin"
|
||||
},
|
||||
{
|
||||
"key": "Access-Control-Allow-Origin",
|
||||
"value": "*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "*.[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].+(css|js)",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Cache-Control",
|
||||
"value": "public,max-age=31536000,immutable"
|
||||
},
|
||||
{
|
||||
"key": "Access-Control-Allow-Origin",
|
||||
"value": "*"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "/[0-9a-f][0-9a-f][0-9a-f].[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].js",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Cross-Origin-Embedder-Policy",
|
||||
"value": "require-corp"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "/@(ngsw-worker.js|ngsw.json)",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Cache-Control",
|
||||
"value": "no-cache"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "/**",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Cross-Origin-Opener-Policy",
|
||||
"value": "same-origin"
|
||||
},
|
||||
|
||||
{
|
||||
"key": "Cross-Origin-Embedder-Policy",
|
||||
"value": "require-corp"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "**",
|
||||
"destination": "/index.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
const {getAdjustedChromeBinPathForWindows} = require('../aio/tools/windows-chromium-path');
|
||||
|
||||
process.env.CHROME_BIN = getAdjustedChromeBinPathForWindows();
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma'),
|
||||
{'reporter:jasmine-seed': ['type', JasmineSeedReporter]},
|
||||
],
|
||||
proxies: {
|
||||
'/dummy/image': 'src/assets/images/logos/angular/angular.png',
|
||||
},
|
||||
client: {
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
random: true,
|
||||
seed: '',
|
||||
},
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/site'),
|
||||
subdir: '.',
|
||||
reporters: [
|
||||
{ type: 'html' },
|
||||
{ type: 'text-summary' }
|
||||
],
|
||||
},
|
||||
reporters: ['progress', 'kjhtml', 'jasmine-seed'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
customLaunchers: {
|
||||
ChromeHeadlessNoSandbox: {
|
||||
base: 'ChromeHeadless',
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
flags: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio'],
|
||||
},
|
||||
},
|
||||
browsers: ['ChromeHeadlessNoSandbox'],
|
||||
browserNoActivityTimeout: 60000,
|
||||
singleRun: false,
|
||||
restartOnFileChange: true,
|
||||
});
|
||||
};
|
||||
|
||||
// Helpers
|
||||
function JasmineSeedReporter(baseReporterDecorator) {
|
||||
baseReporterDecorator(this);
|
||||
|
||||
this.onBrowserComplete = (browser, result) => {
|
||||
const seed = result.order && result.order.random && result.order.seed;
|
||||
if (seed) this.write(`${browser}: Randomized with seed ${seed}.\n`);
|
||||
};
|
||||
|
||||
this.onRunComplete = () => undefined;
|
||||
}
|
||||
@@ -6,9 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
|
||||
|
||||
import {ProgressBarComponent} from './progress-bar.component';
|
||||
import {PROGRESS_BAR_DELAY, ProgressBarComponent} from './progress-bar.component';
|
||||
import {RouterTestingHarness, RouterTestingModule} from '@angular/router/testing';
|
||||
|
||||
describe('ProgressBarComponent', () => {
|
||||
let component: ProgressBarComponent;
|
||||
@@ -16,7 +17,7 @@ describe('ProgressBarComponent', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ProgressBarComponent],
|
||||
imports: [ProgressBarComponent, RouterTestingModule],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ProgressBarComponent);
|
||||
@@ -24,7 +25,13 @@ describe('ProgressBarComponent', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
it('should call progressBar.complete() on route change', fakeAsync(async () => {
|
||||
const progressBarCompleteSpy = spyOn(component.progressBar, 'complete');
|
||||
|
||||
const harness = await RouterTestingHarness.create();
|
||||
await harness.navigateByUrl('/');
|
||||
|
||||
tick(PROGRESS_BAR_DELAY);
|
||||
expect(progressBarCompleteSpy).toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
import {filter, map, switchMap, take} from 'rxjs/operators';
|
||||
|
||||
/** Time to wait after navigation starts before showing the progress bar. This delay allows a small amount of time to skip showing the progress bar when a navigation is effectively immediate. 30ms is approximately the amount of time we can wait before a delay is perceptible.*/
|
||||
const PROGRESS_BAR_DELAY = 30;
|
||||
export const PROGRESS_BAR_DELAY = 30;
|
||||
|
||||
@Component({
|
||||
selector: 'adev-progress-bar',
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// seconday nav - translated on x to display levels
|
||||
// secondary nav - translated on x to display levels
|
||||
.adev-nav-secondary {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Injector} from '@angular/core';
|
||||
import {WINDOW} from '@angular/docs';
|
||||
import {ENVIRONMENT, WINDOW} from '@angular/docs';
|
||||
import {AnalyticsService} from './analytics.service';
|
||||
|
||||
describe('AnalyticsService', () => {
|
||||
@@ -36,6 +36,7 @@ describe('AnalyticsService', () => {
|
||||
|
||||
injector = Injector.create({
|
||||
providers: [
|
||||
{provide: ENVIRONMENT, useValue: {}},
|
||||
{provide: AnalyticsService, deps: [WINDOW]},
|
||||
{provide: WINDOW, useFactory: () => mockWindow, deps: []},
|
||||
],
|
||||
|
||||
@@ -38,13 +38,13 @@ export class ThemeManager {
|
||||
}
|
||||
|
||||
this.loadThemePreference();
|
||||
this.watchPreferedColorScheme();
|
||||
this.watchPreferredColorScheme();
|
||||
}
|
||||
|
||||
setTheme(theme: Theme): void {
|
||||
this.theme.set(theme);
|
||||
this.setThemeInLocalStorage();
|
||||
this.setThemeBodyClasses(theme === 'auto' ? preferedScheme() : theme);
|
||||
this.setThemeBodyClasses(theme === 'auto' ? preferredScheme() : theme);
|
||||
}
|
||||
|
||||
// 1. Read theme preferences stored in localStorage
|
||||
@@ -54,7 +54,7 @@ export class ThemeManager {
|
||||
const useTheme = savedUserPreference ?? 'auto';
|
||||
|
||||
this.theme.set(useTheme);
|
||||
this.setThemeBodyClasses(useTheme === 'auto' ? preferedScheme() : useTheme);
|
||||
this.setThemeBodyClasses(useTheme === 'auto' ? preferredScheme() : useTheme);
|
||||
}
|
||||
|
||||
// Set theme classes on the body element
|
||||
@@ -81,14 +81,14 @@ export class ThemeManager {
|
||||
}
|
||||
}
|
||||
|
||||
private watchPreferedColorScheme() {
|
||||
private watchPreferredColorScheme() {
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => {
|
||||
const preferedScheme = event.matches ? 'dark' : 'light';
|
||||
this.setThemeBodyClasses(preferedScheme);
|
||||
const preferredScheme = event.matches ? 'dark' : 'light';
|
||||
this.setThemeBodyClasses(preferredScheme);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function preferedScheme(): 'dark' | 'light' {
|
||||
function preferredScheme(): 'dark' | 'light' {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import {EmbeddedTutorialManager} from '../embedded-tutorial-manager.service';
|
||||
|
||||
import {CodeEditor, REQUIRED_FILES} from './code-editor.component';
|
||||
import {CodeMirrorEditor} from './code-mirror-editor.service';
|
||||
import {FakeChangeDetectorRef} from '../../../../../shared/src/lib/utils/test-utils.spec';
|
||||
import {FakeChangeDetectorRef} from '@angular/docs/testing';
|
||||
import {TutorialType} from '@angular/docs';
|
||||
|
||||
const files = [
|
||||
|
||||
@@ -66,7 +66,7 @@ export const getTooltipExtension = (
|
||||
dom: tooltip,
|
||||
|
||||
// Note: force the tooltip to scroll to the top on mount and on position change
|
||||
// because depending on the position of the mouse and the siez of the tooltip content,
|
||||
// because depending on the position of the mouse and the size of the tooltip content,
|
||||
// the tooltip might render with its initial scroll position on the bottom
|
||||
mount: (_) => forceTooltipScrollTop(),
|
||||
positioned: (_) => forceTooltipScrollTop(),
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</as-split-area>
|
||||
|
||||
<as-split-area [size]="50">
|
||||
<!-- Preview, Termnial & Console -->
|
||||
<!-- Preview, Terminal & Console -->
|
||||
@if (!displayOnlyTerminal()) {
|
||||
<as-split class="adev-right-side" direction="vertical" restrictMove="true" gutterSize="5">
|
||||
<!-- Preview Section: for larger screens -->
|
||||
|
||||
@@ -8,16 +8,9 @@
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import type {FileSystemTree} from '@webcontainer/api';
|
||||
|
||||
import type {
|
||||
TutorialConfig,
|
||||
TutorialMetadata,
|
||||
} from '@angular/docs';
|
||||
import {
|
||||
TUTORIALS_COMMON_DIRECTORY,
|
||||
TUTORIALS_METADATA_WEB_PATH,
|
||||
TUTORIALS_SOURCE_CODE_WEB_PATH,
|
||||
TutorialType,
|
||||
} from '@angular/docs';
|
||||
import type {TutorialConfig, TutorialMetadata} from '@angular/docs';
|
||||
import {TUTORIALS_ASSETS_WEB_PATH} from '../editor/constants';
|
||||
import {TutorialType} from '@angular/docs';
|
||||
import {EmbeddedTutorialManager} from './embedded-tutorial-manager.service';
|
||||
|
||||
describe('EmbeddedTutorialManager', () => {
|
||||
@@ -252,13 +245,7 @@ describe('EmbeddedTutorialManager', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
const commonSourceCode: FileSystemTree = {
|
||||
'common.ts': {
|
||||
file: {
|
||||
contents: 'code',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const metadata: TutorialMetadata = {
|
||||
tutorialFiles: {'app.js': ''},
|
||||
openFiles: ['app.js'],
|
||||
@@ -271,14 +258,13 @@ describe('EmbeddedTutorialManager', () => {
|
||||
|
||||
const fetchMock = spyOn(window, 'fetch');
|
||||
fetchMock.and.returnValues(
|
||||
Promise.resolve(new Response(JSON.stringify(commonSourceCode))),
|
||||
Promise.resolve(new Response(JSON.stringify(tutorialSourceCode))),
|
||||
Promise.resolve(new Response(JSON.stringify(metadata))),
|
||||
);
|
||||
|
||||
await service.fetchAndSetTutorialFiles(tutorial);
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(3);
|
||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(service.tutorialId()).toEqual(tutorial);
|
||||
expect(service.tutorialFilesystemTree()).toEqual(tutorialSourceCode);
|
||||
@@ -289,8 +275,6 @@ describe('EmbeddedTutorialManager', () => {
|
||||
|
||||
expect(service.type()).toEqual(metadata.type);
|
||||
|
||||
expect(service.commonFilesystemTree()).toEqual(commonSourceCode);
|
||||
|
||||
expect(service.hiddenFiles()).toEqual(metadata.hiddenFiles);
|
||||
expect(service.answerFiles()).toEqual(metadata.answerFiles!);
|
||||
});
|
||||
@@ -299,15 +283,11 @@ describe('EmbeddedTutorialManager', () => {
|
||||
const fetchMock = spyOn(window, 'fetch');
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${TUTORIALS_COMMON_DIRECTORY}.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${tutorial}.json`)
|
||||
.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/source-code.json`)
|
||||
.and.returnValues(Promise.resolve(new Response(null, {status: 404})));
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_METADATA_WEB_PATH}/${tutorial}.json`)
|
||||
.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/metadata.json`)
|
||||
.and.returnValues(Promise.resolve(new Response(null, {status: 404})));
|
||||
|
||||
await expectAsync(service.fetchAndSetTutorialFiles(tutorial)).toBeRejectedWithError(
|
||||
@@ -315,38 +295,15 @@ describe('EmbeddedTutorialManager', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if the tutorial common project cannot be fetched', async () => {
|
||||
const fetchMock = spyOn(window, 'fetch');
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${TUTORIALS_COMMON_DIRECTORY}.json`)
|
||||
.and.returnValues(Promise.resolve(new Response(null, {status: 404})));
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${tutorial}.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_METADATA_WEB_PATH}/${tutorial}.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
await expectAsync(service.fetchAndSetTutorialFiles(tutorial)).toBeRejectedWithError(
|
||||
'Missing source code for tutorial common',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not set shouldReinstallDependencies if project did not change', async () => {
|
||||
const fetchMock = spyOn(window, 'fetch');
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${TUTORIALS_COMMON_DIRECTORY}.json`)
|
||||
.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/source-code.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${tutorial}.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock.withArgs(`${TUTORIALS_METADATA_WEB_PATH}/${tutorial}.json`).and.returnValues(
|
||||
fetchMock.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/metadata.json`).and.returnValues(
|
||||
Promise.resolve(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
@@ -368,15 +325,12 @@ describe('EmbeddedTutorialManager', () => {
|
||||
it('should trigger shouldReInstallDependencies if new metadata has different dependencies', async () => {
|
||||
const fetchMock = spyOn(window, 'fetch');
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${TUTORIALS_COMMON_DIRECTORY}.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${tutorial}.json`)
|
||||
.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/source-code.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock.withArgs(`${TUTORIALS_METADATA_WEB_PATH}/${tutorial}.json`).and.returnValues(
|
||||
fetchMock.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/metadata.json`).and.returnValues(
|
||||
Promise.resolve(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
@@ -404,14 +358,10 @@ describe('EmbeddedTutorialManager', () => {
|
||||
const fetchMock = spyOn(window, 'fetch');
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${TUTORIALS_COMMON_DIRECTORY}.json`)
|
||||
.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/source-code.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${tutorial}.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock.withArgs(`${TUTORIALS_METADATA_WEB_PATH}/${tutorial}.json`).and.returnValues(
|
||||
fetchMock.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/metadata.json`).and.returnValues(
|
||||
Promise.resolve(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
@@ -437,14 +387,10 @@ describe('EmbeddedTutorialManager', () => {
|
||||
const fetchMock = spyOn(window, 'fetch');
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${TUTORIALS_COMMON_DIRECTORY}.json`)
|
||||
.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/source-code.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${tutorial}.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock.withArgs(`${TUTORIALS_METADATA_WEB_PATH}/${tutorial}.json`).and.returnValues(
|
||||
fetchMock.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/metadata.json`).and.returnValues(
|
||||
Promise.resolve(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
@@ -472,16 +418,12 @@ describe('EmbeddedTutorialManager', () => {
|
||||
const fetchMock = spyOn(window, 'fetch');
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${TUTORIALS_COMMON_DIRECTORY}.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${tutorial}.json`)
|
||||
.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/source-code.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
const allFiles = ['file1.ts', 'file2.ts'];
|
||||
|
||||
fetchMock.withArgs(`${TUTORIALS_METADATA_WEB_PATH}/${tutorial}.json`).and.returnValues(
|
||||
fetchMock.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/metadata.json`).and.returnValues(
|
||||
Promise.resolve(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
@@ -507,16 +449,12 @@ describe('EmbeddedTutorialManager', () => {
|
||||
const fetchMock = spyOn(window, 'fetch');
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${TUTORIALS_COMMON_DIRECTORY}.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${tutorial}.json`)
|
||||
.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/source-code.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
const allFiles = ['file1.ts', 'file2.ts'];
|
||||
|
||||
fetchMock.withArgs(`${TUTORIALS_METADATA_WEB_PATH}/${tutorial}.json`).and.returnValues(
|
||||
fetchMock.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/metadata.json`).and.returnValues(
|
||||
Promise.resolve(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
@@ -539,16 +477,12 @@ describe('EmbeddedTutorialManager', () => {
|
||||
const fetchMock = spyOn(window, 'fetch');
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${TUTORIALS_COMMON_DIRECTORY}.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
fetchMock
|
||||
.withArgs(`${TUTORIALS_SOURCE_CODE_WEB_PATH}/${tutorial}.json`)
|
||||
.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/source-code.json`)
|
||||
.and.returnValues(Promise.resolve(new Response('{}', {status: 200})));
|
||||
|
||||
const allFiles = ['file1.ts', 'file2.ts'];
|
||||
|
||||
fetchMock.withArgs(`${TUTORIALS_METADATA_WEB_PATH}/${tutorial}.json`).and.returnValues(
|
||||
fetchMock.withArgs(`${TUTORIALS_ASSETS_WEB_PATH}/${tutorial}/metadata.json`).and.returnValues(
|
||||
Promise.resolve(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
|
||||
@@ -15,7 +15,7 @@ import {TutorialType} from '@angular/docs';
|
||||
import {
|
||||
FakeWebContainer,
|
||||
FakeWebContainerProcess,
|
||||
} from '../../../../shared/src/lib/utils/test-utils.spec';
|
||||
} from '@angular/docs/testing';
|
||||
import {AlertManager} from './alert-manager.service';
|
||||
import {EmbeddedTutorialManager} from './embedded-tutorial-manager.service';
|
||||
import {LoadingStep} from './enums/loading-steps';
|
||||
|
||||
@@ -26,7 +26,7 @@ export const DEV_SERVER_READY_MSG = 'Watch mode enabled. Watching for file chang
|
||||
export const OUT_OF_MEMORY_MSG = 'Out of memory';
|
||||
|
||||
const enum PROCESS_EXIT_CODE {
|
||||
SUCCESS = 0, // process exited succesfully
|
||||
SUCCESS = 0, // process exited successfully
|
||||
ERROR = 10, // process exited with error
|
||||
SIGTERM = 143, // 143 = gracefully terminated by SIGTERM, e.g. Ctrl + C
|
||||
}
|
||||
@@ -73,7 +73,7 @@ export class NodeRuntimeSandbox {
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
// Note: the error state can already be set when loading the NodeRuntimeSanbox
|
||||
// Note: the error state can already be set when loading the NodeRuntimeSandbox
|
||||
// in an unsupported environment.
|
||||
if (this.nodeRuntimeState.error()) {
|
||||
return;
|
||||
@@ -155,7 +155,7 @@ export class NodeRuntimeSandbox {
|
||||
* Initialize the WebContainer for an Angular project
|
||||
*/
|
||||
private async initProject(): Promise<void> {
|
||||
// prevent re-initializion
|
||||
// prevent re-initialization
|
||||
if (this._isProjectInitialized()) return;
|
||||
|
||||
// clean up the sandbox if it was initialized before so that the CLI can
|
||||
@@ -230,7 +230,7 @@ export class NodeRuntimeSandbox {
|
||||
* Initialize the WebContainer for the Angular CLI
|
||||
*/
|
||||
private async initAngularCli() {
|
||||
// prevent re-initializion
|
||||
// prevent re-initialization
|
||||
if (this._isAngularCliInitialized()) return;
|
||||
|
||||
// clean up the sandbox if a project was initialized before so the CLI can
|
||||
@@ -292,7 +292,7 @@ export class NodeRuntimeSandbox {
|
||||
|
||||
const terminal = this.terminalHandler.interactiveTerminalInstance;
|
||||
|
||||
// use WebContainer spawn directly so that the proccess isn't killed on
|
||||
// use WebContainer spawn directly so that the process isn't killed on
|
||||
// cleanup
|
||||
const shellProcess = await webContainer.spawn('bash');
|
||||
|
||||
@@ -516,10 +516,10 @@ export class NodeRuntimeSandbox {
|
||||
|
||||
/**
|
||||
* Kill existing processes and remove files from the WebContainer
|
||||
* when switching tutorials that have diferent requirements
|
||||
* when switching tutorials that have different requirements
|
||||
*/
|
||||
private async cleanup() {
|
||||
// await the proccess to be killed before removing the files because
|
||||
// await the process to be killed before removing the files because
|
||||
// a process can create files during the promise
|
||||
await this.killExistingProcesses();
|
||||
await this.removeFiles();
|
||||
|
||||
@@ -11,7 +11,8 @@ import {By} from '@angular/platform-browser';
|
||||
|
||||
import {Terminal} from './terminal.component';
|
||||
import {TerminalHandler, TerminalType} from './terminal-handler.service';
|
||||
import {FakeEventTarget, WINDOW} from '@angular/docs';
|
||||
import {WINDOW} from '@angular/docs';
|
||||
import {FakeEventTarget} from '@angular/docs/testing';
|
||||
|
||||
describe('Terminal', () => {
|
||||
let component: Terminal;
|
||||
|
||||
@@ -80,7 +80,7 @@ export class TypingsLoader {
|
||||
.readFile(`./node_modules/${library}/package.json`, 'utf-8')
|
||||
.catch((error) => {
|
||||
// Note: "ENOENT" errors occurs:
|
||||
// - While reseting the NodeRuntimeSandbox.
|
||||
// - While resetting the NodeRuntimeSandbox.
|
||||
// - When the library is not a dependency in the project, its package.json won't exist.
|
||||
//
|
||||
// In both cases we ignore the error to continue the process.
|
||||
|
||||
@@ -142,7 +142,8 @@
|
||||
display: inline-block;
|
||||
padding: 7px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s linear;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.5s linear, visibility 0.5s linear;
|
||||
|
||||
button {
|
||||
font-size: 1rem;
|
||||
@@ -307,6 +308,7 @@
|
||||
.adev-cta,
|
||||
.adev-arrow {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import ApiItemLabel from './api-item-label.component';
|
||||
import {ApiItemType} from '../interfaces/api-item-type';
|
||||
|
||||
describe('ApiItemLabel', () => {
|
||||
let component: ApiItemLabel;
|
||||
@@ -23,7 +24,32 @@ describe('ApiItemLabel', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
it('should by default display short label for Class', () => {
|
||||
component.type = ApiItemType.CLASS;
|
||||
fixture.detectChanges();
|
||||
|
||||
const label = fixture.nativeElement.innerText;
|
||||
|
||||
expect(label).toBe('C');
|
||||
});
|
||||
|
||||
it('should display full label for Class when labelMode equals full', () => {
|
||||
component.type = ApiItemType.CLASS;
|
||||
component.mode = 'full';
|
||||
fixture.detectChanges();
|
||||
|
||||
const label = fixture.nativeElement.innerText;
|
||||
|
||||
expect(label).toBe('Class');
|
||||
});
|
||||
|
||||
it('should display short label for Class when labelMode equals short', () => {
|
||||
component.type = ApiItemType.CLASS;
|
||||
component.mode = 'short';
|
||||
fixture.detectChanges();
|
||||
|
||||
const label = fixture.nativeElement.innerText;
|
||||
|
||||
expect(label).toBe('C');
|
||||
});
|
||||
});
|
||||
|
||||
+79
-4
@@ -13,17 +13,44 @@ import {ApiItemsGroup} from '../interfaces/api-items-group';
|
||||
import {ApiReferenceManager} from '../api-reference-list/api-reference-manager.service';
|
||||
import {ApiItemType} from '../interfaces/api-item-type';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {By} from '@angular/platform-browser';
|
||||
|
||||
describe('ApiItemsSection', () => {
|
||||
let component: ApiItemsSection;
|
||||
let fixture: ComponentFixture<ApiItemsSection>;
|
||||
let apiReferenceManagerSpy: jasmine.SpyObj<ApiReferenceManager>;
|
||||
|
||||
const fakeGroup: ApiItemsGroup = {
|
||||
const fakeFeaturedGroup: ApiItemsGroup = {
|
||||
title: 'Featured',
|
||||
isFeatured: true,
|
||||
items: [
|
||||
{title: 'Fake Title', itemType: ApiItemType.CLASS, url: 'api/fakeTitle', isFeatured: true},
|
||||
{
|
||||
title: 'Fake Featured Title',
|
||||
itemType: ApiItemType.CLASS,
|
||||
url: 'api/fakeFeaturedTitle',
|
||||
isFeatured: true,
|
||||
},
|
||||
{
|
||||
title: 'Fake Deprecated Title',
|
||||
itemType: ApiItemType.CONST,
|
||||
url: 'api/fakeDeprecatedTitle',
|
||||
isFeatured: false,
|
||||
isDeprecated: true,
|
||||
},
|
||||
{
|
||||
title: 'Fake Standard Title',
|
||||
itemType: ApiItemType.DIRECTIVE,
|
||||
url: 'api/fakeTitle',
|
||||
isFeatured: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const fakeGroup: ApiItemsGroup = {
|
||||
title: 'Example group',
|
||||
isFeatured: false,
|
||||
items: [
|
||||
{title: 'Fake Title', itemType: ApiItemType.CONST, url: 'api/fakeTitle', isFeatured: false},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -34,12 +61,60 @@ describe('ApiItemsSection', () => {
|
||||
});
|
||||
fixture = TestBed.createComponent(ApiItemsSection);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should render star icon for featured group', () => {
|
||||
component.group = fakeFeaturedGroup;
|
||||
fixture.detectChanges();
|
||||
|
||||
const starIcon = fixture.debugElement.query(By.css('.adev-api-items-section-header docs-icon'));
|
||||
|
||||
expect(starIcon).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not render star icon for standard group', () => {
|
||||
component.group = fakeGroup;
|
||||
fixture.detectChanges();
|
||||
|
||||
const starIcon = fixture.debugElement.query(By.css('.adev-api-items-section-header docs-icon'));
|
||||
|
||||
expect(starIcon).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
it('should render list of all APIs of provided group', () => {
|
||||
component.group = fakeFeaturedGroup;
|
||||
fixture.detectChanges();
|
||||
|
||||
const apis = fixture.debugElement.queryAll(By.css('.adev-api-items-section-grid li'));
|
||||
|
||||
expect(apis.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should display deprecated icon for deprecated API', () => {
|
||||
component.group = fakeFeaturedGroup;
|
||||
fixture.detectChanges();
|
||||
|
||||
const deprecatedApiIcons = fixture.debugElement.queryAll(
|
||||
By.css('.adev-api-items-section-grid li .adev-deprecated'),
|
||||
);
|
||||
const deprecatedApiTitle = deprecatedApiIcons[0].parent?.query(By.css('.adev-item-title'));
|
||||
|
||||
expect(deprecatedApiIcons.length).toBe(1);
|
||||
expect(deprecatedApiIcons[0]).toBeTruthy();
|
||||
expect(deprecatedApiTitle?.nativeElement.innerText).toBe('Fake Deprecated Title');
|
||||
});
|
||||
|
||||
it('should display star icon for featured API', () => {
|
||||
component.group = fakeFeaturedGroup;
|
||||
fixture.detectChanges();
|
||||
|
||||
const featuredApiIcons = fixture.debugElement.queryAll(
|
||||
By.css('.adev-api-items-section-grid li .adev-api-items-section-item-featured'),
|
||||
);
|
||||
const featuredApiTitle = featuredApiIcons[0].parent?.query(By.css('.adev-item-title'));
|
||||
|
||||
expect(featuredApiIcons.length).toBe(1);
|
||||
expect(featuredApiIcons[0]).toBeTruthy();
|
||||
expect(featuredApiTitle?.nativeElement.innerText).toBe('Fake Featured Title');
|
||||
});
|
||||
});
|
||||
|
||||
+37
-6
@@ -17,22 +17,25 @@ import {provideRouter} from '@angular/router';
|
||||
import {RouterTestingHarness, RouterTestingModule} from '@angular/router/testing';
|
||||
|
||||
import ApiReferenceDetailsPage from './api-reference-details-page.component';
|
||||
import {By} from '@angular/platform-browser';
|
||||
|
||||
describe('ApiReferenceDetailsPage', () => {
|
||||
let component: ApiReferenceDetailsPage;
|
||||
let loader: HarnessLoader;
|
||||
let harness: RouterTestingHarness;
|
||||
|
||||
let fakeApiReferenceScrollHandler = {
|
||||
setupListeners: () => {},
|
||||
membersMarginTopInPx: signal(0),
|
||||
membersMarginTopInPx: signal(10),
|
||||
updateMembersMarginTop: () => {},
|
||||
};
|
||||
|
||||
const SAMPLE_CONTENT_WITH_TABS = `<div class="adev-reference-tabs">
|
||||
<div data-tab="API" class="adev-reference-tab"></div>
|
||||
<div data-tab="Description" class="adev-reference-tab"></div>
|
||||
<div data-tab="Examples" class="adev-reference-tab"></div>
|
||||
<div data-tab="Usage Notes" class="adev-reference-tab"></div>
|
||||
<div data-tab="API" data-tab-url="api" class="adev-reference-tab"></div>
|
||||
<div data-tab="Description" data-tab-url="description" class="adev-reference-tab"></div>
|
||||
<div data-tab="Examples" data-tab-url="examples" class="adev-reference-tab"></div>
|
||||
<div data-tab="Usage Notes" data-tab-url="usage-notes" class="adev-reference-tab"></div>
|
||||
<div class="adev-reference-members-container"></div>
|
||||
</div>`;
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -54,7 +57,7 @@ describe('ApiReferenceDetailsPage', () => {
|
||||
],
|
||||
});
|
||||
TestBed.overrideProvider(ReferenceScrollHandler, {useValue: fakeApiReferenceScrollHandler});
|
||||
const harness = await RouterTestingHarness.create();
|
||||
harness = await RouterTestingHarness.create();
|
||||
const {fixture} = harness;
|
||||
component = await harness.navigateByUrl('/', ApiReferenceDetailsPage);
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
@@ -72,4 +75,32 @@ describe('ApiReferenceDetailsPage', () => {
|
||||
|
||||
expect(tabs.length).toBe(4);
|
||||
}));
|
||||
|
||||
it('should display members cards when API tab is active', waitForAsync(async () => {
|
||||
const matTabGroup = await loader.getHarness(MatTabGroupHarness);
|
||||
const tabs = await matTabGroup.getTabs();
|
||||
|
||||
let membersCard = harness.fixture.debugElement.query(
|
||||
By.css('.adev-reference-members-container'),
|
||||
);
|
||||
expect(membersCard).toBeTruthy();
|
||||
|
||||
await matTabGroup.selectTab({label: await tabs[1].getLabel()});
|
||||
|
||||
membersCard = harness.fixture.debugElement.query(By.css('.adev-reference-members-container'));
|
||||
expect(membersCard).toBeFalsy();
|
||||
|
||||
await matTabGroup.selectTab({label: await tabs[0].getLabel()});
|
||||
|
||||
membersCard = harness.fixture.debugElement.query(By.css('.adev-reference-members-container'));
|
||||
expect(membersCard).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should setup scroll listeners when API members are loaded', () => {
|
||||
const setupListenersSpy = spyOn(fakeApiReferenceScrollHandler, 'setupListeners');
|
||||
|
||||
component.membersCardsLoaded();
|
||||
|
||||
expect(setupListenersSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
+17
-1
@@ -8,7 +8,7 @@
|
||||
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import ApiReferenceList from './api-reference-list.component';
|
||||
import ApiReferenceList, {ALL_STATUSES_KEY} from './api-reference-list.component';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {ApiReferenceManager} from './api-reference-manager.service';
|
||||
import {signal} from '@angular/core';
|
||||
@@ -95,4 +95,20 @@ describe('ApiReferenceList', () => {
|
||||
|
||||
expect(component.filteredGroups()![0].items).toEqual([fakeItem2]);
|
||||
});
|
||||
|
||||
it('should set selected type when provided type is different than selected', () => {
|
||||
component.filterByItemType(ApiItemType.BLOCK);
|
||||
|
||||
expect(component.type()).toBe(ApiItemType.BLOCK);
|
||||
});
|
||||
|
||||
it('should reset selected type when provided type is equal to selected', () => {
|
||||
component.filterByItemType(ApiItemType.BLOCK);
|
||||
|
||||
expect(component.type()).toBe(ApiItemType.BLOCK);
|
||||
|
||||
component.filterByItemType(ApiItemType.BLOCK);
|
||||
|
||||
expect(component.type()).toBe(ALL_STATUSES_KEY);
|
||||
});
|
||||
});
|
||||
|
||||
+33
-7
@@ -6,16 +6,18 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import CliReferenceDetailsPage from './cli-reference-details-page.component';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {RouterTestingHarness, RouterTestingModule} from '@angular/router/testing';
|
||||
import {signal} from '@angular/core';
|
||||
import {ReferenceScrollHandler} from '../services/reference-scroll-handler.service';
|
||||
import {provideRouter} from '@angular/router';
|
||||
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
|
||||
|
||||
describe('CliReferenceDetailsPage', () => {
|
||||
let component: CliReferenceDetailsPage;
|
||||
let fixture: ComponentFixture<CliReferenceDetailsPage>;
|
||||
let harness: RouterTestingHarness;
|
||||
|
||||
let fakeApiReferenceScrollHandler = {
|
||||
setupListeners: () => {},
|
||||
@@ -23,18 +25,42 @@ describe('CliReferenceDetailsPage', () => {
|
||||
updateMembersMarginTop: () => {},
|
||||
};
|
||||
|
||||
const SAMPLE_CONTENT = `
|
||||
<div class="cli">
|
||||
<div class="adev-reference-cli-content">First column content</div>
|
||||
<div class="adev-reference-members-container">Members content</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CliReferenceDetailsPage, RouterTestingModule],
|
||||
providers: [
|
||||
provideRouter([
|
||||
{
|
||||
path: '**',
|
||||
component: CliReferenceDetailsPage,
|
||||
data: {
|
||||
'docContent': {
|
||||
id: 'id',
|
||||
contents: SAMPLE_CONTENT,
|
||||
},
|
||||
},
|
||||
},
|
||||
]),
|
||||
],
|
||||
});
|
||||
TestBed.overrideProvider(ReferenceScrollHandler, {useValue: fakeApiReferenceScrollHandler});
|
||||
|
||||
fixture = TestBed.createComponent(CliReferenceDetailsPage);
|
||||
component = fixture.componentInstance;
|
||||
harness = await RouterTestingHarness.create();
|
||||
const {fixture} = harness;
|
||||
component = await harness.navigateByUrl('/', CliReferenceDetailsPage);
|
||||
TestbedHarnessEnvironment.loader(fixture);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
it('should set content on init', () => {
|
||||
expect(component.mainContentInnerHtml()).toBe('First column content');
|
||||
expect(component.cardsInnerHtml()).toBe('Members content');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -56,10 +56,12 @@ export function getApiNavigationItems(): NavigationItem[] {
|
||||
|
||||
const packageNavigationItem: NavigationItem = {
|
||||
label: packageNameWithoutPrefix,
|
||||
children: packageApis.map((api) => ({
|
||||
path: getApiUrl(packageNameWithoutPrefix, api.name),
|
||||
label: api.name,
|
||||
})),
|
||||
children: packageApis
|
||||
.map((api) => ({
|
||||
path: getApiUrl(packageNameWithoutPrefix, api.name),
|
||||
label: api.name,
|
||||
}))
|
||||
.sort((a, b) => a.label.localeCompare(b.label)),
|
||||
};
|
||||
|
||||
apiNavigationItems.push(packageNavigationItem);
|
||||
|
||||
@@ -6,11 +6,23 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {ApiItemType} from '../interfaces/api-item-type';
|
||||
import {ApiLabel} from './api-label.pipe';
|
||||
|
||||
describe('ApiLabel', () => {
|
||||
it('create an instance', () => {
|
||||
const pipe = new ApiLabel();
|
||||
expect(pipe).toBeTruthy();
|
||||
let pipe: ApiLabel;
|
||||
|
||||
beforeEach(() => {
|
||||
pipe = new ApiLabel();
|
||||
});
|
||||
|
||||
it(`should return short label when labelType equals short`, () => {
|
||||
const result = pipe.transform(ApiItemType.CLASS, 'short');
|
||||
expect(result).toBe('C');
|
||||
});
|
||||
|
||||
it(`should return short label when labelType equals short`, () => {
|
||||
const result = pipe.transform(ApiItemType.CLASS, 'full');
|
||||
expect(result).toBe('Class');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -160,6 +160,9 @@ export class ReferenceScrollHandler implements OnDestroy, ReferenceScrollHandler
|
||||
for (const line of Array.from(activeLines)) {
|
||||
line.classList.remove(API_TAB_ACTIVE_CODE_LINE);
|
||||
}
|
||||
this.getAllMemberCards().forEach((card) => {
|
||||
card.blur();
|
||||
});
|
||||
} else {
|
||||
const lines = this.document.querySelectorAll<HTMLButtonElement>(
|
||||
`button[${MEMBER_ID_ATTRIBUTE}="${currentActiveMemberId}"]`,
|
||||
@@ -167,6 +170,7 @@ export class ReferenceScrollHandler implements OnDestroy, ReferenceScrollHandler
|
||||
for (const line of Array.from(lines)) {
|
||||
line.classList.add(API_TAB_ACTIVE_CODE_LINE);
|
||||
}
|
||||
this.document.getElementById(`${currentActiveMemberId}`)?.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@use '@angular/docs/styles/media-queries' as mq;
|
||||
|
||||
// horizozntal tutorial nav bar
|
||||
// horizontal tutorial nav bar
|
||||
.adev-tutorial-nav-container {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
EmbeddedEditor,
|
||||
EmbeddedTutorialManager,
|
||||
NodeRuntimeSandbox
|
||||
} from '@angular/docs';
|
||||
} from '../../editor';
|
||||
|
||||
import {mockAsyncProvider} from '../../core/services/inject-async';
|
||||
import Tutorial from './tutorial.component';
|
||||
|
||||
@@ -125,6 +125,7 @@ export const routes: Route[] = [
|
||||
{
|
||||
path: '',
|
||||
loadComponent: () => import('./features/home/home.component'),
|
||||
data: {label: 'Home'}
|
||||
},
|
||||
{
|
||||
path: PagePrefix.DOCS,
|
||||
@@ -141,7 +142,7 @@ export const routes: Route[] = [
|
||||
{
|
||||
path: PagePrefix.PLAYGROUND,
|
||||
loadComponent: () => import('./features/playground/playground.component'),
|
||||
data: {...commonTutorialRouteData},
|
||||
data: {...commonTutorialRouteData, label: 'Playground'},
|
||||
},
|
||||
...SUB_NAVIGATION_ROUTES,
|
||||
...API_REFERENCE_ROUTES,
|
||||
|
||||
@@ -943,6 +943,7 @@ export const TUTORIALS_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
||||
{
|
||||
path: DefaultPage.TUTORIALS,
|
||||
contentPath: 'tutorials/home',
|
||||
label: 'Tutorials'
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1336,6 +1337,11 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
||||
path: 'reference/migrations/typed-forms',
|
||||
contentPath: 'reference/migrations/typed-forms',
|
||||
},
|
||||
{
|
||||
label: 'Control Flow Syntax',
|
||||
path: 'reference/migrations/control-flow',
|
||||
contentPath: 'reference/migrations/control-flow',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -19,7 +19,7 @@ In the image above, there is a series of change detection calls triggered by eve
|
||||
|
||||
## Run tasks outside `NgZone`
|
||||
|
||||
In such cases, you can instruct Angular to avoid calling change detection for tasks scheduled by a given piece of code using [NgZone](/guide/zone).
|
||||
In such cases, you can instruct Angular to avoid calling change detection for tasks scheduled by a given piece of code using [NgZone](/api/core/NgZone).
|
||||
|
||||
<docs-code header="Run outside of the Zone" language='ts' linenums>
|
||||
import { Component, NgZone, OnInit } from '@angular/core';
|
||||
|
||||
@@ -78,7 +78,7 @@ For example, imagine the following scenario:
|
||||
1. After some time, a new version of the application is deployed to the server.
|
||||
This newer version includes the files `index.html`, `main.<main-hash-2>.js` and `lazy-chunk.<lazy-hash-2>.js`.
|
||||
|
||||
IMPORANT: The hashes are different now, because the content of the files changed. The old version is no longer available on the server.
|
||||
IMPORTANT: The hashes are different now, because the content of the files changed. The old version is no longer available on the server.
|
||||
|
||||
1. In the meantime, the user's browser decides to evict `lazy-chunk.<lazy-hash-1>.js` from its cache.
|
||||
Browsers might decide to evict specific (or all) resources from a cache in order to reclaim disk space.
|
||||
|
||||
@@ -110,7 +110,7 @@ Make a change to the application, and watch the service worker install the updat
|
||||
<docs-code language="shell">
|
||||
|
||||
ng build
|
||||
npx http-server -p 8080 -c-1 dist/
|
||||
npx http-server -p 8080 -c-1 dist/<project-name>/browser
|
||||
|
||||
</docs-code>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Architect } from '@angular-devkit/architect';
|
||||
import { TestingArchitectHost } from '@angular-devkit/architect/testing';
|
||||
import { schema } from '@angular-devkit/core';
|
||||
import { promises as fs } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
describe('Copy File Builder', () => {
|
||||
let architect: Architect;
|
||||
@@ -19,7 +20,7 @@ describe('Copy File Builder', () => {
|
||||
|
||||
// This will either take a Node package name, or a path to the directory
|
||||
// for the package.json file.
|
||||
await architectHost.addBuilderFromPackage('..');
|
||||
await architectHost.addBuilderFromPackage(join(__dirname, '..'));
|
||||
});
|
||||
|
||||
it('can copy files', async () => {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// #docregion
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HeroModule } from './hero.module';
|
||||
import { HEROES } from './mock-heroes';
|
||||
|
||||
@Injectable({
|
||||
// we declare that this service should be created
|
||||
// by any injector that includes HeroModule.
|
||||
providedIn: HeroModule,
|
||||
})
|
||||
export class HeroService {
|
||||
getHeroes() { return HEROES; }
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { AppPage } from './app.po';
|
||||
|
||||
describe('forms-overvoew App', () => {
|
||||
describe('forms-overview App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
docs_example(
|
||||
name = "providers",
|
||||
)
|
||||
@@ -1,23 +0,0 @@
|
||||
import { element, by } from 'protractor';
|
||||
import { AppPage } from './app.po';
|
||||
|
||||
describe('providers App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(async () => {
|
||||
page = new AppPage();
|
||||
await page.navigateTo();
|
||||
});
|
||||
|
||||
it('should display header that says Users list', async () => {
|
||||
expect(await page.getTitleText()).toEqual('Users list');
|
||||
});
|
||||
|
||||
it('shows a list of customers', async () => {
|
||||
const items = element.all(by.css('app-root li'));
|
||||
expect(await items.count()).toBe(10);
|
||||
expect(await items.get(0).getText()).toBe('1 Maria');
|
||||
expect(await items.get(9).getText()).toBe('10 Seth');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
<h1>
|
||||
{{ title }}
|
||||
</h1>
|
||||
|
||||
<li *ngFor="let user of users">
|
||||
<span>{{ user.id }}</span> {{ user.name }}
|
||||
</li>
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { User, UserService } from './user.service';
|
||||
|
||||
// #docregion component-providers
|
||||
@Component({
|
||||
// #enddocregion component-providers
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css'],
|
||||
// #docregion component-providers
|
||||
providers: [UserService]
|
||||
})
|
||||
// #enddocregion component-providers
|
||||
export class AppComponent implements OnInit {
|
||||
title = 'Users list';
|
||||
users: User[] = [];
|
||||
|
||||
constructor(private userService: UserService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.userService.getUsers().then(users => this.users = users);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ AppComponent ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
||||
@@ -1,9 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { UserService } from './user.service';
|
||||
|
||||
@NgModule({
|
||||
providers: [UserService],
|
||||
})
|
||||
export class UserModule {
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UserService {
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { UserModule } from './user.module';
|
||||
|
||||
@Injectable({
|
||||
providedIn: UserModule,
|
||||
})
|
||||
export class UserService {
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'any',
|
||||
})
|
||||
export class UserService {
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UserService {
|
||||
getUsers(): Promise<User[]> {
|
||||
return Promise.resolve([
|
||||
{ id: 1, name: 'Maria' },
|
||||
{ id: 2, name: 'Alex' },
|
||||
{ id: 3, name: 'Chuntao' },
|
||||
{ id: 4, name: 'Béatrice' },
|
||||
{ id: 5, name: 'Sarah' },
|
||||
{ id: 6, name: 'Andrés' },
|
||||
{ id: 7, name: 'Abdul' },
|
||||
{ id: 8, name: 'Pierre' },
|
||||
{ id: 9, name: 'Jiao' },
|
||||
{ id: 10, name: 'Seth' }
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Providers</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root>Loading...</app-root>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +0,0 @@
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"description": "Providers",
|
||||
"files": [
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
"!**/*.[0,1,2].*"
|
||||
],
|
||||
"file": "src/app/app.component.ts",
|
||||
"tags": ["providers"]
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
<div class="section">
|
||||
<h2>@Optional() Component</h2>
|
||||
<p>This component still works even though the OptionalService (notice @Optional() in the consturctor isn't provided or configured anywhere. Angular goes through tree and visibilty rules, and if it doesn't find the requested service, returns null.</p>
|
||||
<p>This component still works even though the OptionalService (notice @Optional() in the constructor isn't provided or configured anywhere. Angular goes through tree and visibility rules, and if it doesn't find the requested service, returns null.</p>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// TODO: Feature Componetized like HeroCenter
|
||||
// TODO: Feature Componentized like HeroCenter
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
// TODO: Feature Componetized like CrisisCenter
|
||||
// TODO: Feature Componentized like CrisisCenter
|
||||
// #docregion rxjs-imports
|
||||
import { Observable } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
|
||||
@@ -41,7 +41,7 @@ describe('Lazy Loading AngularJS Tests', () => {
|
||||
|
||||
// Workaround for https://github.com/angular/protractor/issues/4724
|
||||
async function loadAngularJS() {
|
||||
// Abort if `resumeBootstrap` has already occured
|
||||
// Abort if `resumeBootstrap` has already occurred
|
||||
if (await browser.executeScript(`return '__TESTABILITY__NG1_APP_ROOT_INJECTOR__' in window;`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ application-wide. Activities that trigger this checking include user interaction
|
||||
timers, and more.
|
||||
|
||||
**`ChangeDetectionStrategy.OnPush`** is an optional mode that reduces the amount of checking Angular
|
||||
needs to perform. In this mode, the framework only checks if a component's DOM needs an update when
|
||||
needs to perform. In this mode, the framework only checks if a component's DOM needs an update when:
|
||||
|
||||
- A component input has changes as a result of a binding in a template, or
|
||||
- When an event listener in this component runs
|
||||
- When the component is explicitly marked for check, via `ChangeDetectorRef.markForCheck` or something which wraps it, like `AsyncPipe`.
|
||||
- An event listener in this component runs
|
||||
- The component is explicitly marked for check, via `ChangeDetectorRef.markForCheck` or something which wraps it, like `AsyncPipe`.
|
||||
|
||||
Additionally, when an OnPush component is checked, Angular _also_ checks all of its ancestor
|
||||
components, traversing upwards through the application tree.
|
||||
|
||||
@@ -31,7 +31,7 @@ process.
|
||||
<td>Creation</td>
|
||||
<td><code>constructor</code></td>
|
||||
<td>
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor">
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor" target="_blank">
|
||||
Standard JavaScript class constructor
|
||||
</a>. Runs when Angular instantiates the component.
|
||||
</td>
|
||||
@@ -226,7 +226,7 @@ invoked after Angular has finished rendering _all components_ on the page into t
|
||||
|
||||
These functions are different from the other lifecycle hooks described in this guide. Rather than a
|
||||
class method, they are standalone functions that accept a callback. The execution of render
|
||||
callbacks is not tied to any specific component instance, but instead an application-wide hook.
|
||||
callbacks are not tied to any specific component instance, but instead an application-wide hook.
|
||||
|
||||
`afterRender` and `afterNextRender` must be called in
|
||||
an [injection context](guide/di/dependency-injection-context), typically a
|
||||
@@ -259,7 +259,7 @@ export class UserProfile {
|
||||
nativeElement.style.padding = computePadding();
|
||||
}, {phase: AfterRenderPhase.Write});
|
||||
|
||||
// Use the `Read` phase to read geometric properties after all writes have occured.
|
||||
// Use the `Read` phase to read geometric properties after all writes have occurred.
|
||||
afterNextRender(() => {
|
||||
this.elementHeight = nativeElement.getBoundingClientRect().height;
|
||||
}, {phase: AfterRenderPhase.Read});
|
||||
|
||||
@@ -40,7 +40,7 @@ directive's capabilities.
|
||||
|
||||
A **view container** is a node in Angular's component tree that can contain content. Any component
|
||||
or directive can inject `ViewContainerRef` to get a reference to a view container corresponding to
|
||||
that component or directive's location in the DOM..
|
||||
that component or directive's location in the DOM.
|
||||
|
||||
You can use the `createComponent`method on `ViewContainerRef` to dynamically create and render a
|
||||
component. When you create a new component with a `ViewContainerRef`, Angular appends it into the
|
||||
|
||||
@@ -147,7 +147,7 @@ export class CustomMenu {
|
||||
<custom-menu>
|
||||
<custom-menu-item>Cheese</custom-menu-item>
|
||||
<custom-menu-item>Tomato</custom-menu-item>
|
||||
</custom-expando>
|
||||
</custom-menu>
|
||||
`
|
||||
})
|
||||
</docs-code>
|
||||
|
||||
@@ -240,10 +240,13 @@ In the example below, the prefetching starts when a browser becomes idle and the
|
||||
|
||||
## Testing
|
||||
|
||||
Angular provides TestBed APIs to simplify the process of testing `@defer` blocks and triggering different states during testing. By default, `@defer` blocks in tests are in "paused" (`@placeholder`) state, so that you can manually transition between states.
|
||||
Angular provides TestBed APIs to simplify the process of testing `@defer` blocks and triggering different states during testing. By default, `@defer` blocks in tests will play through like a defer block would behave in a real application. If you want to manually step through states, you can switch the defer block behavior to `Manual` in the TestBed configuration.
|
||||
|
||||
```typescript
|
||||
it('should render a defer block in different states', async () => {
|
||||
// configures the defer block behavior to start in "paused" state for manual control.
|
||||
TestBed.configureTestingModule({deferBlockBehavior: DeferBlockBehavior.Manual});
|
||||
|
||||
@Component({
|
||||
// ...
|
||||
template: `
|
||||
|
||||
@@ -11,7 +11,7 @@ The injection context is available in these situations:
|
||||
* In the `factory` function specified for an `InjectionToken`.
|
||||
* Within a stack frame that is run in a injection context.
|
||||
|
||||
Knowing when your are in an injection context, will allow you to use the [`inject`](api/core/inject) function to inject instances.
|
||||
Knowing when you are in an injection context, will allow you to use the [`inject`](api/core/inject) function to inject instances.
|
||||
|
||||
## Class constructors
|
||||
|
||||
@@ -33,6 +33,7 @@ class MyComponent {
|
||||
Some APIs are designed to be run in an injection context. This is the case, for example, of the router guards. It allows the use of [`inject`](api/core/inject) to access a service within the guard function.
|
||||
|
||||
Here is an example for `CanActivateFn`
|
||||
|
||||
<docs-code language="typescript" highlight="[3]">
|
||||
const canActivateTeam: CanActivateFn =
|
||||
(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
|
||||
|
||||
@@ -14,8 +14,7 @@ In the following example, the app component provides a `Logger` instance.
|
||||
providers: [Logger],
|
||||
</docs-code>
|
||||
|
||||
You can, however, configure DI to use a different class or any other different value to associate with the `Logger` class.
|
||||
Note that `Logger` becomes is only used as "token" for the dependency then.
|
||||
You can, however, configure DI to associate the `Logger` provider token with a different class or any other value.
|
||||
So when the `Logger` is injected, the configured value is used instead.
|
||||
|
||||
In fact, the class provider syntax is a shorthand expression that expands into a provider configuration, defined by the `Provider` interface.
|
||||
|
||||
@@ -12,9 +12,6 @@ With hierarchical dependency injection, you can isolate sections of the applicat
|
||||
|
||||
## Types of injector hierarchies
|
||||
|
||||
Injectors in Angular have rules that you can leverage to achieve the desired visibility of injectables in your applications.
|
||||
By understanding these rules, you can determine whether to declare a provider at the application level, in a Component, or in a Directive.
|
||||
|
||||
Angular has two injector hierarchies:
|
||||
|
||||
| Injector hierarchies | Details |
|
||||
@@ -1146,7 +1143,7 @@ graph BT;
|
||||
subgraph A[" "]
|
||||
direction LR
|
||||
RootInjector["(A) RootInjector"]
|
||||
ServicesA["CarService, EngineService, TitleService"]
|
||||
ServicesA["CarService, EngineService, TiresService"]
|
||||
end
|
||||
|
||||
subgraph B[" "]
|
||||
|
||||
@@ -5,7 +5,7 @@ This tutorial shows you how to create a template-driven form. The control elemen
|
||||
Template-driven forms use [two-way data binding](guide/templates/two-way-binding) to update the data model in the component as changes are made in the template and vice versa.
|
||||
|
||||
<docs-callout helpful title="Template vs Reactive forms">
|
||||
Angular supports two design approaches for interactive forms. Template-driven forms allow you to use form-specific directives in your Angular template.Reactive forms provide a model-driven approach to buildin forms.
|
||||
Angular supports two design approaches for interactive forms. Template-driven forms allow you to use form-specific directives in your Angular template.Reactive forms provide a model-driven approach to building forms.
|
||||
|
||||
Template-driven forms are a great choice for small or simple forms, while reactive forms are more scalable and suitable for complex forms. For a comparison of the two approaches, see [Choosing an approach](guide/forms#choosing-an-approach)
|
||||
</docs-callout>
|
||||
|
||||
@@ -39,7 +39,7 @@ In order for this interceptor to actually intercept requests, you must configure
|
||||
|
||||
## Configuring interceptors
|
||||
|
||||
You declare the set of interceptors to use when when configuring `HttpClient` through dependency injection, by using the `withInterceptors` feature:
|
||||
You declare the set of interceptors to use when configuring `HttpClient` through dependency injection, by using the `withInterceptors` feature:
|
||||
|
||||
<docs-code language="ts">
|
||||
bootstrapApplication(AppComponent, {providers: [
|
||||
@@ -175,7 +175,7 @@ A DI-based interceptor is an injectable class which implements the `HttpIntercep
|
||||
public class LoggingInterceptor implements HttpInterceptor {
|
||||
intercept(req: HttpRequest<any>, handler: HttpHandler): Observable<HttpEvent<any>> {
|
||||
console.log('Request URL: ' + req.url);
|
||||
return handler.next(req);
|
||||
return handler.handle(req);
|
||||
}
|
||||
}
|
||||
</docs-code>
|
||||
|
||||
@@ -4,12 +4,14 @@ Before you can use `HttpClient` in your app, you must configure it using [depend
|
||||
|
||||
## Providing `HttpClient` through dependency injection
|
||||
|
||||
`HttpClient` is provided using the `provideHttpClient` helper function, which most apps include in the application `providers` in `main.ts`.
|
||||
`HttpClient` is provided using the `provideHttpClient` helper function, which most apps include in the application `providers` in `app.config.ts`.
|
||||
|
||||
<docs-code language="ts">
|
||||
bootstrapApplication(App, {providers: [
|
||||
provideHttpClient(),
|
||||
]});
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideHttpClient(),
|
||||
]
|
||||
};
|
||||
</docs-code>
|
||||
|
||||
If your app is using NgModule-based bootstrap instead, you can include `provideHttpClient` in the providers of your app's NgModule:
|
||||
|
||||
@@ -50,7 +50,7 @@ const configPromise = firstValueFrom(config$);
|
||||
const req = httpTesting.expectOne('/api/config', 'Request to load the configuration');
|
||||
|
||||
// We can assert various properties of the request if desired.
|
||||
expect(req.method).toBe('GET');
|
||||
expect(req.request.method).toBe('GET');
|
||||
|
||||
// Flushing the request causes it to complete, delivering the result.
|
||||
req.flush(DEFAULT_CONFIG);
|
||||
|
||||
@@ -39,38 +39,6 @@ Additionally, these providers are also available to all the classes in the appli
|
||||
|
||||
You should always provide your service in the root injector unless there is a case where you want the service to be available only if the consumer imports a particular `@NgModule`.
|
||||
|
||||
## `providedIn` and NgModules
|
||||
|
||||
It's also possible to specify that a service should be provided in a particular `@NgModule`.
|
||||
For example, if you don't want `UserService` to be available to applications unless they import a `UserModule` you've created, you can specify that the service should be provided in the module:
|
||||
|
||||
<docs-code header="src/app/user.service.ts" highlight="[5]">
|
||||
import { Injectable } from '@angular/core';
|
||||
import { UserModule } from './user.module';
|
||||
|
||||
@Injectable({
|
||||
providedIn: UserModule,
|
||||
})
|
||||
export class UserService {
|
||||
}
|
||||
</docs-code>
|
||||
|
||||
The example above shows the **preferred** way to provide a service in a module.
|
||||
This method is preferred because it enables tree-shaking of the service if nothing injects it.
|
||||
|
||||
If it's not possible to specify in the service which module should provide it, you can also declare a provider for the service within the module:
|
||||
|
||||
<docs-code header="src/app/user.module.ts" highlight="[5]">
|
||||
import { NgModule } from '@angular/core';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
@NgModule({
|
||||
providers: [UserService],
|
||||
})
|
||||
export class UserModule {
|
||||
}
|
||||
</docs-code>
|
||||
|
||||
## Limiting provider scope by lazy loading modules
|
||||
|
||||
In the basic CLI-generated app, modules are eagerly loaded which means that they are all loaded when the application launches.
|
||||
|
||||
@@ -58,7 +58,7 @@ The tabs for the example show the following:
|
||||
| Files | Details |
|
||||
|:--- |:--- |
|
||||
| flying-heroes.component.html | Template with the new pipe used. |
|
||||
| flying-heroes.pipe.ts | File with custom pipe that filters flying heros. |
|
||||
| flying-heroes.pipe.ts | File with custom pipe that filters flying heroes. |
|
||||
|
||||
<docs-code-multifile path="adev/src/content/examples/pipes/src/app/flying-heroes.component.ts_FlyingHeroesComponent" preview>
|
||||
<docs-code header="src/app/flying-heroes.component.html" path="adev/src/content/examples/pipes/src/app/flying-heroes.component.html" visibleRegion="template-flying-heroes"/>
|
||||
|
||||
@@ -443,7 +443,7 @@ gotoItems(hero: Hero) {
|
||||
You can configure your routes to lazy load modules, which means that Angular only loads modules as needed, rather than loading all modules when the application launches.
|
||||
Additionally, preload parts of your application in the background to improve the user experience.
|
||||
|
||||
For more information on lazy loading and preloading see the dedicated guide lazy loading.
|
||||
For more information on lazy loading and preloading see the dedicated guide [Lazy loading](guide/ngmodules/lazy-loading).
|
||||
|
||||
## Preventing unauthorized access
|
||||
|
||||
|
||||
@@ -271,5 +271,5 @@ For more information about routing, see the following topics:
|
||||
|
||||
<docs-pill-row>
|
||||
<docs-pill href="guide/routing/common-router-tasks" title="In-app Routing and Navigation"/>
|
||||
<docs-pill href="api/router" title="Router API"/>
|
||||
<docs-pill href="api/router/Router" title="Router API"/>
|
||||
</docs-pill-row>
|
||||
|
||||
@@ -110,7 +110,7 @@ To learn more about the Angular Router, see the following topics:
|
||||
|
||||
<docs-pill-row>
|
||||
<docs-pill href="guide/routing/common-router-tasks" title="In-app Routing and Navigation"/>
|
||||
<docs-pill href="api/router" title="Router API"/>
|
||||
<docs-pill href="api/router/Router" title="Router API"/>
|
||||
</docs-pill-row>
|
||||
|
||||
HELPFUL: This content is based on [Custom Route Matching with the Angular Router](https://medium.com/@brandontroberts/custom-route-matching-with-the-angular-router-fbdd48665483), by [Brandon Roberts](https://twitter.com/brandontroberts).
|
||||
|
||||
@@ -35,13 +35,13 @@ You can bind to keyboard events using Angular's binding syntax. You can specify
|
||||
|
||||
Combinations of keys can be separated by a `.` (period). For example, `keydown.enter` will allow you to bind events to the `enter` key. You can also use modifier keys, such as `shift`, `alt`, `control`, and the `command` keys from Mac. The following example shows how to bind a keyboard event to `keydown.shift.t`.
|
||||
|
||||
```typescript
|
||||
```html
|
||||
<input (keydown.shift.t)="onKeydown($event)" />
|
||||
```
|
||||
|
||||
Depending on the operating system, some key combinations might create special characters instead of the key combination that you expect. MacOS, for example, creates special characters when you use the option and shift keys together. If you bind to `keydown.shift.alt.t`, on macOS, that combination produces a `ˇ` character instead of a `t`, which doesn't match the binding and won't trigger your event handler. To bind to `keydown.shift.alt.t` on macOS, use the `code` keyboard event field to get the correct behavior, such as `keydown.code.shiftleft.altleft.keyt` shown in this example.
|
||||
|
||||
```typescript
|
||||
```html
|
||||
<input (keydown.code.shiftleft.altleft.keyt)="onKeydown($event)" />
|
||||
```
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ The template syntax guides show you how to control the UX/UI by coordinating dat
|
||||
|
||||
## Empower your HTML
|
||||
|
||||
Extend the HTML vocabulary of your applications With special Angular syntax in your templates.
|
||||
Extend the HTML vocabulary of your applications with special Angular syntax in your templates.
|
||||
For example, Angular helps you get and set DOM \(Document Object Model\) values dynamically with features such as built-in template functions, variables, event listening, and data binding.
|
||||
|
||||
Almost all HTML syntax is valid template syntax.
|
||||
|
||||
@@ -230,7 +230,7 @@ The following example re-implements the previous test with `DebugElement.query()
|
||||
|
||||
Some noteworthy observations:
|
||||
|
||||
* The `By.css()` static method selects `DebugElement` nodes with a [standard CSS selector](https://developer.mozilla.org/docs/Web/Guide/CSS/Getting_started/Selectors 'CSS selectors').
|
||||
* The `By.css()` static method selects `DebugElement` nodes with a [standard CSS selector](https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors 'CSS selectors').
|
||||
* The query returns a `DebugElement` for the paragraph.
|
||||
* You must unwrap that result to get the paragraph element.
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ Other common examples of event listeners include:
|
||||
|
||||
### $event
|
||||
|
||||
If you need to access the [event](https://developer.mozilla.org/en-US/docs/Web/API/Event) object, Angular provides an implicit `$event` variable that you can be pass to a function:
|
||||
If you need to access the [event](https://developer.mozilla.org/en-US/docs/Web/API/Event) object, Angular provides an implicit `$event` variable that you can pass to a function:
|
||||
|
||||
```html
|
||||
<button (click)="createUser($event)">Submit</button>
|
||||
|
||||
@@ -7,7 +7,7 @@ As a design system, this page contains visual and Markdown authoring guidance fo
|
||||
* Custom Angular docs elements: [`docs-card`](#cards), [`docs-callout`](#callouts), [`docs-pill`](#pills), and [`docs-steps`](#workflow)
|
||||
* Custom text elements: [alerts](#alerts)
|
||||
* Code examples: [`docs-code`](#code)
|
||||
* Built-in Markdown styled elements: links, lists, [headers](#headers), [horizonal lines](#horizontal-line-divider), [tables](#tables)
|
||||
* Built-in Markdown styled elements: links, lists, [headers](#headers), [horizontal lines](#horizontal-line-divider), [tables](#tables)
|
||||
* and more!
|
||||
|
||||
Get ready to:
|
||||
|
||||
@@ -224,7 +224,7 @@ For details of those options and their possible values, see the [Angular CLI Ref
|
||||
|
||||
## Complex configuration values
|
||||
|
||||
The `assets`, `index`, `styles`, and `scripts` options can have either simple path string values, or object values with specific fields.
|
||||
The `assets`, `index`, `outputPath`, `styles`, and `scripts` options can have either simple path string values, or object values with specific fields.
|
||||
The `sourceMap` and `optimization` options can be set to a simple boolean value. They can also be given a complex value using the configuration file.
|
||||
|
||||
The following sections provide more details of how these complex values are used in each case.
|
||||
@@ -418,6 +418,7 @@ Several options can be used to fine-tune the optimization of an application.
|
||||
|:--- |:--- |:--- |:--- |
|
||||
| `minify` | Minify CSS definitions by removing extraneous whitespace and comments, merging identifiers, and minimizing values. | `boolean` | `true` |
|
||||
| `inlineCritical` | Extract and inline critical CSS definitions to improve [First Contentful Paint](https://web.dev/first-contentful-paint). | `boolean` | `true` |
|
||||
| `removeSpecialComments` | Remove comments in global CSS that contains `@license` or `@preserve` or that starts with `//!` or `/*!`. | `boolean` | `true` |
|
||||
|
||||
#### Fonts optimization options
|
||||
|
||||
@@ -508,3 +509,16 @@ When supplying the value as a string the filename of the specified path will be
|
||||
|:--- |:--- |:--- |:--- |
|
||||
| `input` | The path of a file to use for the application's generated HTML index. | `string` | None (required) |
|
||||
| `output` | The output path of the application's generated HTML index file. The full provided path will be used and will be considered relative to the application's configured output path. | `string` | `index.html` |
|
||||
|
||||
### Output path configuration
|
||||
|
||||
The `outputPath` option can be either a String which will be used as the `base` value or an Object for more fine-tune configuration.
|
||||
|
||||
Several options can be used to fine-tune the output structure of an application.
|
||||
|
||||
| Options | Details | Value type | Default value |
|
||||
|:--- |:--- |:--- |:--- |
|
||||
| `base` | Specify the output path relative to workspace root. | `string` | |
|
||||
| `browser` | The output directory name for your browser build is within the base output path. This can be safely served to users. | `string` | `browser` |
|
||||
| `server` | The output directory name of your server build within the output path base. | `string` | `server` |
|
||||
| `media` | The output directory name for your media files located within the output browser directory. These media files are commonly referred to as resources in CSS files. | `string` | `media` |
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# Migration to Control Flow syntax
|
||||
|
||||
|
||||
[Control flow syntax](guide/templates/control-flow) is available from Angular 17 and simplified way to use the control-flow directives like *ngFor, *ngIf and *ngSwitch.
|
||||
|
||||
The new syntax is baked into the template, so you don't need to import `CommonModule` anymore. But wait there is more to it, there is an migration availble for migrating all your old code to use new Control Flow Syntax with Angular 17 release. Run the schematic with the following command:
|
||||
|
||||
<docs-code language="shell">
|
||||
|
||||
ng generate @angular/core:control-flow
|
||||
|
||||
</docs-code>
|
||||
@@ -18,4 +18,7 @@ Learn about how you can migrate your existing angular project to the latest feat
|
||||
|
||||
Strictly typed reactive forms add type safety and the types enable a variety of other improvements, such as better autocomplete in IDEs, and an explicit way to specify form structure.
|
||||
</docs-card>
|
||||
<docs-card title="Control Flow Syntax" link="Migrate now" href="reference/migrations/control-flow">
|
||||
Control Flow Syntax is available with Angular 17 release and allows you to use more ergonomic syntax which is close to javascript, better type checking and lazy load part of the component. It replaces the need to imports CommonModule to use functionalities like *ngFor,`*ngIf.
|
||||
</docs-card>
|
||||
</docs-card-container>
|
||||
|
||||
@@ -4,8 +4,8 @@ The logo graphics available for download on this page are provided under [CC BY
|
||||
|
||||
<docs-card-container>
|
||||
<docs-card title="Angular logos" href="https://drive.google.com/drive/folders/1IgcAwLDVZUz8ycnFa7T4_H6B4V4LhYUQ?usp=sharing" link="Download from Google Drive">
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
Black and white are the default color variations and should be used in most circumstances. A gradient version of the icon and lockup is available in both static and animated formats and can be used in cases where a color icon is required.
|
||||
</docs-card>
|
||||
|
||||
@@ -109,7 +109,7 @@ As a general rule, a fix is considered for an LTS version if it resolves one of:
|
||||
|
||||
## Deprecation policy
|
||||
|
||||
When the Angular team intends to remove an API or feature, it will be marked as *deprecated*. This occurs when when an API is obsolete, superseded by another API, or otherwise discontinued. Deprecated API remain available through their deprecated phase, which lasts a minimum two major versions (approximately one year).
|
||||
When the Angular team intends to remove an API or feature, it will be marked as *deprecated*. This occurs when an API is obsolete, superseded by another API, or otherwise discontinued. Deprecated API remain available through their deprecated phase, which lasts a minimum two major versions (approximately one year).
|
||||
|
||||
To help ensure that you have sufficient time and a clear path to update, this is our deprecation policy:
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ This table covers [Angular versions under active support](reference/releases#act
|
||||
|
||||
| Angular | Node.js | TypeScript | RxJS |
|
||||
| ------------------ | ------------------------------------ | -------------- | ------------------ |
|
||||
| 17.1.0 | ^18.13.0 \|\| ^20.9.0 | >=5.2.0 <5.4.0 | ^6.5.3 \|\| ^7.4.0 |
|
||||
| 17.0.x | ^18.13.0 \|\| ^20.9.0 | >=4.9.3 <5.3.0 | ^6.5.3 \|\| ^7.4.0 |
|
||||
| 16.1.x \|\| 16.2.x | ^16.14.0 \|\| ^18.10.0 | >=4.9.3 <5.2.0 | ^6.5.3 \|\| ^7.4.0 |
|
||||
| 16.0.x | ^16.14.0 \|\| ^18.10.0 | >=4.9.3 <5.1.0 | ^6.5.3 \|\| ^7.4.0 |
|
||||
|
||||
@@ -34,7 +34,6 @@ You can read more by following the links associated with the package names below
|
||||
| [Vercel](https://vercel.com/solutions/angular) | [`vercel init angular`](https://github.com/vercel/vercel/tree/main/examples/angular) |
|
||||
| [Netlify](https://www.netlify.com) | [`ng add @netlify-builder/deploy`](https://npmjs.org/package/@netlify-builder/deploy) |
|
||||
| [GitHub pages](https://pages.github.com) | [`ng add angular-cli-ghpages`](https://npmjs.org/package/angular-cli-ghpages) |
|
||||
| [NPM](https://npmjs.com) | [`ng add ngx-deploy-npm`](https://npmjs.org/package/ngx-deploy-npm) |
|
||||
| [Amazon Cloud S3](https://aws.amazon.com/s3/?nc2=h_ql_prod_st_s3) | [`ng add @jefiozie/ngx-aws-deploy`](https://www.npmjs.com/package/@jefiozie/ngx-aws-deploy) |
|
||||
|
||||
If you're deploying to a self-managed server or there's no builder for your favorite cloud platform, you can either [create a builder](tools/cli/cli-builder) that allows you to use the `ng deploy` command, or read through this guide to learn how to manually deploy your application.
|
||||
|
||||
@@ -169,10 +169,6 @@ To add a staging environment, create a copy of `src/environments/environment.ts`
|
||||
"production": { … },
|
||||
"staging": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.development.ts"
|
||||
},
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.staging.ts"
|
||||
|
||||
@@ -106,7 +106,7 @@ Also, the later sections of this guide provide additional information on several
|
||||
|
||||
For applications new to SSR, the [Angular SSR Guide](guide/ssr) provides additional information regarding the setup process for adding SSR to an application.
|
||||
|
||||
For applications that are already using SSR, additional manual adjustments will be needed to update the application server to support the new integrated SSR capabilities.
|
||||
For applications that are already using SSR, additional adjustments will be needed to update the application server to support the new integrated SSR capabilities.
|
||||
The `application` builder now provides the integrated functionality for all of the following preexisting builders:
|
||||
|
||||
- `app-shell`
|
||||
@@ -117,25 +117,23 @@ The `application` builder now provides the integrated functionality for all of t
|
||||
The `ng update` process will automatically remove usages of the `@nguniversal` scope packages where some of these builders were previously located.
|
||||
The new `@angular/ssr` package will also be automatically added and used with configuration and code being adjusted during the update.
|
||||
The `@angular/ssr` package supports the `browser` builder as well as the `application` builder.
|
||||
To convert from the separate SSR builders to the integrated capabilities of the `application` builder, there are several required manual steps.
|
||||
However, as each application is different, there may be more application specific changes needed beyond these to complete the process.
|
||||
To convert from the separate SSR builders to the integrated capabilities of the `application` builder, run the experimental `use-application-builder` migration.
|
||||
|
||||
1. Combine the options for the above mentioned SSR builders into the `application` builder options within the `angular.json` file.
|
||||
The previously used builders and their target configurations can then be fully removed from the file.
|
||||
2. Combine server TypeScript configuration from `tsconfig.server.json` into `tsconfig.app.json`.
|
||||
The `types` and `files` options are typically the only setting that needs to be combined but others may be needed based on application specific customizations.
|
||||
You should also add the TypeScript option `"esModuleInterop": true` to ensure `express` imports are [ESM compliant](#esm-default-imports-vs-namespace-imports).
|
||||
The `tsconfig.server.json` can then be removed as it will no longer be used during builds.
|
||||
3. Remove and/or update any `npm` scripts referencing the now removed builder targets.
|
||||
The `ng build` and `ng serve` commands provide equivalent functionality when using the `application` builder.
|
||||
4. Update application server code to remove Webpack specific elements.
|
||||
5. Update application server code to use new bootstrapping and output directory structure.
|
||||
An example of the changes for a v16 project that has been converted can be found [here](https://github.com/alan-agius4/angular-cli-use-application-builder/commit/1defdb93a7f508662bc427439e51505668bf84cd#diff-1ba718c1eb8aa39cd20c2562d92523068c734d75f54655e97d652b992d9b4259).
|
||||
6. Remove any CommonJS assumptions in the application server code such as `require`, `__filename`, `__dirname`, or other constructs from the [CommonJS module scope](https://nodejs.org/api/modules.html#the-module-scope).
|
||||
All application code should be ESM compatible.
|
||||
This does not apply to third-party dependencies.
|
||||
<docs-code language="shell">
|
||||
|
||||
In the future, a schematic will make this migration process easier for existing applications.
|
||||
ng update @angular/cli --name use-application-builder
|
||||
|
||||
</docs-code>
|
||||
|
||||
The migration does the following:
|
||||
|
||||
* Converts existing `browser` or `browser-esbuild` target to `application`
|
||||
* Removes any previous SSR builders (because `application` does that now).
|
||||
* Updates configuration accordingly.
|
||||
* Merges `tsconfig.server.json` with `tsconfig.app.json` and adds the TypeScript option `"esModuleInterop": true` to ensure `express` imports are [ESM compliant](#esm-default-imports-vs-namespace-imports).
|
||||
* Updates application server code to use new bootstrapping and output directory structure.
|
||||
|
||||
HELPFUL: Remember to remove any CommonJS assumptions in the application server code such as `require`, `__filename`, `__dirname`, or other constructs from the [CommonJS module scope](https://nodejs.org/api/modules.html#the-module-scope). All application code should be ESM compatible. This does not apply to third-party dependencies.
|
||||
|
||||
## Executing a build
|
||||
|
||||
@@ -175,8 +173,6 @@ Several build options are not yet implemented but will be added in the future as
|
||||
|
||||
- [WASM imports](https://github.com/angular/angular-cli/issues/25102) -- WASM can still be loaded manually via [standard web APIs](https://developer.mozilla.org/en-US/docs/WebAssembly/Loading_and_running).
|
||||
|
||||
Building libraries with the new build system via `ng-packagr` is also not yet possible but library build support will be available in a future release.
|
||||
|
||||
## ESM default imports vs. namespace imports
|
||||
|
||||
TypeScript by default allows default exports to be imported as namespace imports and then used in call expressions.
|
||||
|
||||
@@ -129,7 +129,7 @@ When you select a bar, DevTools displays useful information about it including:
|
||||
|
||||
### Understand component execution
|
||||
|
||||
The bar chat displayed after clicking on a change detection cycle displays a detailed view about how much time your application spent running change detection in that particular component or directive.
|
||||
The bar chart displayed after clicking on a change detection cycle displays a detailed view about how much time your application spent running change detection in that particular component or directive.
|
||||
|
||||
This example shows the total time spent by the `NgForOf` directive and which method was called on it.
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ It works with external templates in separate HTML files, and also with in-line t
|
||||
|
||||
## Configuring compiler options for the Angular Language Service
|
||||
|
||||
To enable the latest Language Service features, set the `strictTemplates` option in `tsconfig.json` by setting `strictTemplates` to `true,` as shown in the following example:
|
||||
To enable the latest Language Service features, set the `strictTemplates` option in `tsconfig.json` by setting `strictTemplates` to `true`, as shown in the following example:
|
||||
|
||||
<docs-code language="json">
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ In the **Edit** pane of your IDE:
|
||||
<docs-step title="Add the styles for the component">
|
||||
In this step, you will copy over the pre-written styles for the `HousingLocationComponent` to your app so that the app renders properly.
|
||||
|
||||
1. Open `src/app/housing-location/housing-location.css`, and paste the styles below into the file:
|
||||
1. Open `src/app/housing-location/housing-location.component.css`, and paste the styles below into the file:
|
||||
|
||||
Note: In the browser, these can go in `src/app/housing-location/housing-location.component.ts` in the `styles` array.
|
||||
|
||||
|
||||
@@ -47,5 +47,6 @@ In the code editor:
|
||||
Summary: In this lesson, you created a new property decorated with the `@Input()` decorator. You also used the non-null assertion operator to notify the compiler that the value of the new property won't be `null` or `undefined`.
|
||||
|
||||
<docs-pill-row>
|
||||
<docs-pill href="guide/components/inputs-outputs" title="Sharing data between child and parent directives and components"/>
|
||||
<docs-pill href="guide/components/inputs" title="Accepting data with input properties"/>
|
||||
<docs-pill href="guide/components/outputs" title="Custom events with outputs"/>
|
||||
</docs-pill-row>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user