mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
186 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ef06bbb38 | |||
| c7e4f14bc8 | |||
| 9d6fdd0af0 | |||
| 5474f80f6a | |||
| 3e1657d560 | |||
| 6e55752ad4 | |||
| 0bf6ab4066 | |||
| bb5facbcd2 | |||
| 77ae4459d3 | |||
| 0736882836 | |||
| 5cffdd0e54 | |||
| e8eb87ff58 | |||
| ee1f824c40 | |||
| 1e7644d560 | |||
| 0b8f3a14b3 | |||
| e242d1da14 | |||
| 772dbe8a8e | |||
| 065d263547 | |||
| a69edc487f | |||
| 246cdadcdc | |||
| da0dabc6b4 | |||
| c20103c7ca | |||
| 11bc6fffa2 | |||
| c06516c9db | |||
| 268ba5c4ca | |||
| dda23b9cb0 | |||
| c2d3f60da0 | |||
| f589b01672 | |||
| a779a1029b | |||
| 4307d6e5bd | |||
| 879bf22823 | |||
| b3328a9722 | |||
| b0854e2ec6 | |||
| 29f50976db | |||
| d207ea06d1 | |||
| 18e2dba7ae | |||
| e31eaf8595 | |||
| e6b7472d11 | |||
| a6db152c78 | |||
| 778edfc63c | |||
| 89893972c8 | |||
| aeff926381 | |||
| 24cc1441f5 | |||
| 6eea132952 | |||
| a47ea85c5e | |||
| 9ee4441c55 | |||
| e7b3b34e62 | |||
| ca2165e8df | |||
| d845643ba1 | |||
| 8f109da1c8 | |||
| 6de37b8f0a | |||
| 3cddc3d6bc | |||
| 0dfb4e8c8b | |||
| 97fa1cf544 | |||
| 9d4b580c12 | |||
| 0ef8b01804 | |||
| c06cbb2ec4 | |||
| 688b1c5b53 | |||
| 12ad314d5a | |||
| 8b9aea9503 | |||
| 1d9d74a2d1 | |||
| d4567b28f7 | |||
| e6d520f3d9 | |||
| e6f01d777d | |||
| fa3e7f87e7 | |||
| 8d184f9a31 | |||
| 2a61728137 | |||
| 63013546e1 | |||
| 9ebd41e39c | |||
| 34b2669d3a | |||
| d654c7933a | |||
| b101ad0d98 | |||
| 75a11d9ac8 | |||
| 2cdf074034 | |||
| 2566cbb48c | |||
| afeab76497 | |||
| bfb738c3c3 | |||
| eeef3e7479 | |||
| c2e5d76674 | |||
| d249665bd2 | |||
| 4f8a7280c1 | |||
| 04472d23c2 | |||
| 8dbae89ed4 | |||
| 6e13179fe1 | |||
| 1a66199a7f | |||
| 9e8a919385 | |||
| 367ef56224 | |||
| fdc6786b22 | |||
| b9c1604399 | |||
| 859eb13f98 | |||
| 1f84f01ff9 | |||
| 462c157e8a | |||
| 91366401f0 | |||
| b98b0da37e | |||
| dffb7c5218 | |||
| a926402b4c | |||
| 9067d362d4 | |||
| dc661ebda8 | |||
| c1f9d807e6 | |||
| 2afaaf0bf9 | |||
| 0c9c8f51df | |||
| cf27076918 | |||
| 48010b3c73 | |||
| 91a576db20 | |||
| 2050d8408d | |||
| e1d61243bd | |||
| 65330f03a9 | |||
| 17d3de25da | |||
| 2ed5a2db79 | |||
| 7dad7dfecd | |||
| 88b2ff25d7 | |||
| 7b5405b070 | |||
| d8d7784b3a | |||
| f6957b24cd | |||
| 8267d25dfd | |||
| 71f428679b | |||
| 12a9d2152e | |||
| 1c31828620 | |||
| 51fd5e3742 | |||
| 723342a60a | |||
| a8d5a31356 | |||
| 9ceadb0903 | |||
| f6bce3158a | |||
| ca4df9c334 | |||
| d87917542a | |||
| c7a2d76722 | |||
| 7b7dce358d | |||
| 1670798aa8 | |||
| 305042a945 | |||
| 8493eb7e65 | |||
| 7fe7836925 | |||
| 60104086ba | |||
| 8a79690fdc | |||
| 0562eae68e | |||
| fbc53e7c2d | |||
| f2523a8fef | |||
| e6088ab736 | |||
| 0a17e98ae2 | |||
| fe602c9c4e | |||
| b9ab8b99a1 | |||
| 0540a0339e | |||
| 89e7e52041 | |||
| bdac537852 | |||
| 80e602ca5c | |||
| 9619255dd5 | |||
| eca48016c4 | |||
| ec60d10422 | |||
| 9557e87ad8 | |||
| 45116097c1 | |||
| 4a8ab4f149 | |||
| 9f8a256467 | |||
| 6da24e6451 | |||
| f78ab1ed43 | |||
| 3df4d898fa | |||
| a1e9ccb340 | |||
| 7c9c54922c | |||
| aa261e7e01 | |||
| ee3fe6b8a2 | |||
| 97c18f4527 | |||
| df5cc1fbbf | |||
| 723d21455a | |||
| 72073cfb16 | |||
| 41c6877c01 | |||
| 12e0cfab54 | |||
| aab41ca42f | |||
| 3482636b5a | |||
| b11923cd7a | |||
| 0cf388e882 | |||
| d19ddd1a87 | |||
| c380d56c6d | |||
| f84d826a3a | |||
| 78cf10b28b | |||
| a0e5b85694 | |||
| ace0a50646 | |||
| 0f27f89d38 | |||
| a1e036cfd1 | |||
| 7ea1d4720a | |||
| 560dd44de4 | |||
| 87cdd84c17 | |||
| 4059ecaed1 | |||
| e03291bc34 | |||
| ffebb3e785 | |||
| 0f435a3cf9 | |||
| 1c50869792 | |||
| f6b828e292 | |||
| bd42b795f7 |
@@ -1,3 +1 @@
|
||||
4.0.0
|
||||
# [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117]
|
||||
# When updating the Bazel version you also need to update the RBE toolchains version in package.bzl
|
||||
|
||||
+15
-12
@@ -24,19 +24,19 @@ version: 2.1
|
||||
# **NOTE 2 **: If you change the cache key prefix, also sync the cache_key_fallback to match.
|
||||
# **NOTE 3 **: Keep the static part of the cache key as prefix to enable correct fallbacks.
|
||||
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
|
||||
var_3: &cache_key v4-angular-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||
var_3: &cache_key v4-angular-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "aio/yarn.lock" }}
|
||||
# We invalidate the cache if the Bazel version changes because otherwise the `bazelisk` cache
|
||||
# folder will contain all previously used versions and ultimately cause the cache restoring to
|
||||
# be slower due to its growing size.
|
||||
var_4: &cache_key_fallback v4-angular-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}
|
||||
|
||||
# Windows needs its own cache key because binaries in node_modules are different.
|
||||
var_3_win: &cache_key_win v4-angular-win-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||
var_3_win: &cache_key_win v4-angular-win-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "aio/yarn.lock" }}
|
||||
var_4_win: &cache_key_win_fallback v4-angular-win-node-14-{{ checksum "month.txt" }}-{{ checksum ".bazelversion" }}
|
||||
|
||||
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
|
||||
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
|
||||
var_5: &components_repo_unit_tests_cache_key v1-angular-components-{{ checksum "month.txt" }}-7cc42f5d835b7971e9ff73e220b801cf7834d515
|
||||
var_5: &components_repo_unit_tests_cache_key v1-angular-components-{{ checksum "month.txt" }}-d090617912da8e70aa336aa5b4d804b1b535402e
|
||||
var_6: &components_repo_unit_tests_cache_key_fallback v1-angular-components-{{ checksum "month.txt" }}
|
||||
|
||||
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
|
||||
@@ -441,6 +441,9 @@ jobs:
|
||||
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
|
||||
# Check the bundle sizes.
|
||||
- run: yarn --cwd aio payload-size aio-local
|
||||
# Run tests with RxJS v7.
|
||||
- run: yarn --cwd aio add rxjs@7.1.0
|
||||
- run: yarn --cwd aio test --progress=false --watch=false
|
||||
|
||||
test_aio_tools:
|
||||
executor: default-executor
|
||||
@@ -469,6 +472,10 @@ jobs:
|
||||
# Since the parallelism is set to "5", there will be five parallel CircleCI containers.
|
||||
# with either "0", "1", etc as node index. This can be passed to the "--shard" argument.
|
||||
- run: yarn --cwd aio example-e2e --setup --local --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL}
|
||||
# Run tests with RxJS v7.
|
||||
# (Exclude some examples that are not yet compatible with v7.)
|
||||
- run: yarn --cwd aio/tools/examples/shared add rxjs@7.1.0 && yarn --cwd aio boilerplate:add
|
||||
- run: yarn --cwd aio example-e2e --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --exclude=practical-observable-usage --exclude=upgrade-module --exclude=upgrade-phonecat
|
||||
|
||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||
aio_preview:
|
||||
@@ -671,7 +678,7 @@ jobs:
|
||||
background: true
|
||||
# add module umd tsc compile option so the test can work
|
||||
# properly in the legacy browsers
|
||||
- run: yarn tsc -p packages --module UMD
|
||||
- run: yarn tsc -p packages/tsconfig-legacy-saucelabs.json --module UMD
|
||||
- run: yarn tsc -p modules --module UMD
|
||||
- run: yarn bazel build //packages/zone.js:npm_package
|
||||
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
|
||||
@@ -734,10 +741,6 @@ jobs:
|
||||
# variable. It needs to be hardcoded here, because env variables interpolation is
|
||||
# not supported.
|
||||
- '/tmp/angular-components-repo'
|
||||
- run:
|
||||
# TODO(devversion): remove once https://github.com/angular/components/pull/23056 is available.
|
||||
name: Replace RBE container instance name
|
||||
command: sed -i "s#default_instance#primary_instance#g" ${COMPONENTS_REPO_TMP_DIR}/.bazelrc
|
||||
- run:
|
||||
# Updates the `angular/components` `package.json` file to refer to the release output
|
||||
# inside the `packages-dist` directory.
|
||||
@@ -784,11 +787,11 @@ jobs:
|
||||
- setup_win
|
||||
- run:
|
||||
name: Build all windows CI targets
|
||||
command: yarn bazel build --build_tag_filters=-ivy-only //packages/compiler-cli/... //tools/ts-api-guardian/...
|
||||
command: yarn bazel build --build_tag_filters=-ivy-only //packages/compiler-cli/...
|
||||
no_output_timeout: 15m
|
||||
- run:
|
||||
name: Test all windows CI targets
|
||||
command: yarn bazel test --test_tag_filters="-ivy-only,-browser:chromium-local" //packages/compiler-cli/... //tools/ts-api-guardian/...
|
||||
command: yarn bazel test --test_tag_filters="-ivy-only,-browser:chromium-local" //packages/compiler-cli/...
|
||||
no_output_timeout: 15m
|
||||
|
||||
test_ivy_aot_win:
|
||||
@@ -797,11 +800,11 @@ jobs:
|
||||
- setup_win
|
||||
- run:
|
||||
name: Build all windows CI targets
|
||||
command: yarn bazel build --config=ivy --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot //packages/compiler-cli/... //tools/ts-api-guardian/...
|
||||
command: yarn bazel build --config=ivy --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot //packages/compiler-cli/...
|
||||
no_output_timeout: 15m
|
||||
- run:
|
||||
name: Test all windows CI targets
|
||||
command: yarn bazel test --config=ivy --test_tag_filters="-no-ivy-aot,-fixme-ivy-aot,-browser:chromium-local" //packages/compiler-cli/... //tools/ts-api-guardian/... //packages/localize/...
|
||||
command: yarn bazel test --config=ivy --test_tag_filters="-no-ivy-aot,-fixme-ivy-aot,-browser:chromium-local" //packages/compiler-cli/... //packages/localize/...
|
||||
no_output_timeout: 15m
|
||||
# Save dependencies to use on subsequent runs.
|
||||
- save_cache:
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo"
|
||||
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
|
||||
setPublicVar COMPONENTS_REPO_BRANCH "master"
|
||||
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
|
||||
setPublicVar COMPONENTS_REPO_COMMIT "7cc42f5d835b7971e9ff73e220b801cf7834d515"
|
||||
setPublicVar COMPONENTS_REPO_COMMIT "d090617912da8e70aa336aa5b4d804b1b535402e"
|
||||
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -5,8 +5,5 @@
|
||||
*.js eol=lf
|
||||
*.ts eol=lf
|
||||
|
||||
# API guardian patch must always use LF for tests to work
|
||||
*.patch eol=lf
|
||||
|
||||
# Must keep Windows line ending to be parsed correctly
|
||||
scripts/windows/packages.txt eol=crlf
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
---
|
||||
name: "Bug Report"
|
||||
about: Report a bug in the Angular Framework
|
||||
---
|
||||
<!--
|
||||
|
||||
Oh hi there!
|
||||
|
||||
To expedite issue processing please search open and closed issues before submitting a new one.
|
||||
Existing issues often contain information about workarounds, resolution, or progress updates.
|
||||
|
||||
-->
|
||||
|
||||
|
||||
# Bug Report
|
||||
|
||||
### Affected Package
|
||||
<!-- Can you pin-point one or more @angular/* packages as the source of the bug? -->
|
||||
<!-- edit: --> The issue is caused by package @angular/....
|
||||
|
||||
|
||||
### Is this a regression?
|
||||
|
||||
<!-- Did this behavior use to work in the previous version? -->
|
||||
<!-- edit: --> Yes, the previous version in which this bug was not present was: ....
|
||||
|
||||
|
||||
### Description
|
||||
<!-- edit: --> A clear and concise description of the problem...
|
||||
|
||||
|
||||
## Minimal Reproduction
|
||||
<!--
|
||||
Please create and share minimal reproduction of the issue starting with this template: https://stackblitz.com/fork/angular-ivy
|
||||
-->
|
||||
<!-- edit: --> https://stackblitz.com/...
|
||||
|
||||
<!--
|
||||
If StackBlitz is not suitable for reproduction of your issue, please create a minimal GitHub repository with the reproduction of the issue.
|
||||
A good way to make a minimal reproduction is to create a new app via `ng new repro-app` and add the minimum possible code to show the problem.
|
||||
Share the link to the repo below along with step-by-step instructions to reproduce the problem, as well as expected and actual behavior.
|
||||
|
||||
Issues that don't have enough info and can't be reproduced will be closed.
|
||||
|
||||
You can read more about issue submission guidelines here: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#submit-issue
|
||||
-->
|
||||
|
||||
## Exception or Error
|
||||
<pre><code>
|
||||
<!-- If the issue is accompanied by an exception or an error, please share it below: -->
|
||||
|
||||
</code></pre>
|
||||
|
||||
|
||||
## Your Environment
|
||||
|
||||
**Angular Version:**
|
||||
<pre><code>
|
||||
<!-- run `ng version` and paste output below -->
|
||||
|
||||
</code></pre>
|
||||
|
||||
**Anything else relevant?**
|
||||
<!-- Is this a browser specific issue? If so, please specify the browser and version. -->
|
||||
|
||||
<!-- Do any of these matter: operating system, IDE, package manager, HTTP server, ...? If so, please mention it below. -->
|
||||
@@ -0,0 +1,84 @@
|
||||
name: Bug Report
|
||||
description: Report a bug in the Angular Framework
|
||||
|
||||
body:
|
||||
- type: dropdown
|
||||
id: affected-packages
|
||||
attributes:
|
||||
label: Which @angular/* package(s) are the source of the bug?
|
||||
options:
|
||||
- animations
|
||||
- bazel
|
||||
- common
|
||||
- compiler-cli
|
||||
- compiler
|
||||
- elements
|
||||
- forms
|
||||
- language-service
|
||||
- localize
|
||||
- platform-browser-dynamic
|
||||
- platform-browser
|
||||
- platform-server
|
||||
- router
|
||||
- service-worker
|
||||
- upgrade
|
||||
- Don't known / other
|
||||
multiple: true
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: is-regression
|
||||
attributes:
|
||||
label: Is this a regression?
|
||||
options:
|
||||
- 'Yes'
|
||||
- 'No'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Please provide a link to a minimal reproduction of the bug
|
||||
|
||||
- type: textarea
|
||||
id: exception-or-error
|
||||
attributes:
|
||||
label: Please provide the exception or error you saw
|
||||
render: true
|
||||
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Please provide the environment you discovered this bug in
|
||||
render: true
|
||||
placeholder: |
|
||||
Angular CLI: 12.0.5
|
||||
Node: 14.17.0
|
||||
Package Manager: yarn 1.22.10
|
||||
OS: linux x64
|
||||
|
||||
Angular: 12.0.5
|
||||
... animations, cli, common, compiler, compiler-cli, core, forms
|
||||
... platform-browser, platform-browser-dynamic, router
|
||||
|
||||
Package Version
|
||||
---------------------------------------------------------
|
||||
@angular-devkit/architect 0.1200.5
|
||||
@angular-devkit/build-angular 12.0.5
|
||||
@angular-devkit/core 12.0.5
|
||||
rxjs 6.6.7
|
||||
typescript 4.2.4
|
||||
|
||||
- type: textarea
|
||||
id: other
|
||||
attributes:
|
||||
label: Anything else?
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
name: "Feature Request"
|
||||
about: Suggest a feature for Angular Framework
|
||||
---
|
||||
<!--
|
||||
|
||||
Oh hi there!
|
||||
|
||||
To expedite issue processing please search open and closed issues before submitting a new one.
|
||||
Existing issues often contain information about workarounds, resolution, or progress updates.
|
||||
|
||||
-->
|
||||
|
||||
|
||||
# Feature Request
|
||||
|
||||
### Relevant Package
|
||||
<!-- Can you pin-point one or more @angular/* packages the are relevant for this feature request? -->
|
||||
<!-- edit: --> This feature request is for @angular/....
|
||||
|
||||
|
||||
### Description
|
||||
<!-- edit: --> A clear and concise description of the problem or missing capability...
|
||||
|
||||
|
||||
### Describe the solution you'd like
|
||||
<!-- edit: --> If you have a solution in mind, please describe it.
|
||||
|
||||
|
||||
### Describe alternatives you've considered
|
||||
<!-- edit: --> Have you considered any alternative solutions or workarounds?
|
||||
@@ -0,0 +1,46 @@
|
||||
name: 'Feature Request'
|
||||
description: Suggest a feature for Angular Framework
|
||||
|
||||
body:
|
||||
- type: dropdown
|
||||
id: affected-packages
|
||||
attributes:
|
||||
label: Which @angular/* package(s) are relevant/releated to the feature request?
|
||||
options:
|
||||
- animations
|
||||
- bazel
|
||||
- common
|
||||
- compiler-cli
|
||||
- compiler
|
||||
- elements
|
||||
- forms
|
||||
- language-service
|
||||
- localize
|
||||
- platform-browser-dynamic
|
||||
- platform-browser
|
||||
- platform-server
|
||||
- router
|
||||
- service-worker
|
||||
- upgrade
|
||||
multiple: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: proposed-solution
|
||||
attributes:
|
||||
label: Proposed solution
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives-considered
|
||||
attributes:
|
||||
label: Alternatives considered
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
name: "Docs or angular.io Bug Report"
|
||||
about: Report an issue in Angular's documentation or angular.io application
|
||||
---
|
||||
<!--
|
||||
|
||||
Oh hi there!
|
||||
|
||||
To expedite issue processing please search open and closed issues before submitting a new one.
|
||||
Existing issues often contain information about workarounds, resolution, or progress updates.
|
||||
|
||||
-->
|
||||
|
||||
# Docs or angular.io Bug Report
|
||||
|
||||
### Description
|
||||
|
||||
<!-- edit: --> A clear and concise description of the problem...
|
||||
|
||||
|
||||
## Minimal Reproduction
|
||||
|
||||
### What's the affected URL?**
|
||||
<!-- edit: --> https://angular.io/...
|
||||
|
||||
### Reproduction Steps**
|
||||
<!-- If applicable please list the steps to take to reproduce the issue -->
|
||||
|
||||
### Expected vs Actual Behavior**
|
||||
<!-- If applicable please describe the difference between the expected and actual behavior after following the repro steps. -->
|
||||
|
||||
## Screenshot
|
||||
<!-- Often a screenshot can help to capture the issue better than a long description. -->
|
||||
<!-- upload a screenshot:-->
|
||||
|
||||
## Exception or Error
|
||||
<pre><code>
|
||||
<!-- If the issue is accompanied by an exception or an error, please share it below: -->
|
||||
|
||||
</code></pre>
|
||||
|
||||
## Your Environment
|
||||
|
||||
### Browser info
|
||||
<!-- Is this a browser specific issue? If so, please specify the device, browser, and version. -->
|
||||
|
||||
### Anything else relevant?
|
||||
<!-- Please provide additional info if necessary. -->
|
||||
@@ -0,0 +1,42 @@
|
||||
name: 'Docs or angular.io Bug Report'
|
||||
description: Report an issue in Angular's documentation or angular.io application
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: affected-url
|
||||
attributes:
|
||||
label: What is the affected URL?
|
||||
|
||||
- type: textarea
|
||||
id: reproduction-steps
|
||||
attributes:
|
||||
label: Please provide the steps to reproduce the issue
|
||||
|
||||
- type: textarea
|
||||
id: expected-vs-actual-behavior
|
||||
attributes:
|
||||
label: Please provide the expected behavior vs the actual behavior you encountered
|
||||
|
||||
- type: textarea
|
||||
id: screenshot
|
||||
attributes:
|
||||
label: Please provide a screenshot if possible
|
||||
|
||||
- type: textarea
|
||||
id: exception-or-error
|
||||
attributes:
|
||||
label: Please provide the exception or error you saw
|
||||
render: true
|
||||
|
||||
- type: textarea
|
||||
id: browser-info
|
||||
attributes:
|
||||
label: Is this a browser-specific issue? If so, please specify the device, browser, and version.
|
||||
render: true
|
||||
@@ -10,6 +10,6 @@ jobs:
|
||||
if: github.repository == 'angular/angular'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/feature-request@8467fd69f068914f8f25e10b1a80c183ee886de4
|
||||
- uses: angular/dev-infra/github-actions/feature-request@f83903fe1ac848407ef81465f66588e5accb6537
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
_
|
||||
@@ -15,5 +15,6 @@ export const caretaker: CaretakerConfig = {
|
||||
name: 'Initial Triage Queue',
|
||||
query: `is:open no:milestone`,
|
||||
}
|
||||
]
|
||||
],
|
||||
caretakerGroup: 'angular-caretaker',
|
||||
};
|
||||
|
||||
+4
-1
@@ -25,6 +25,9 @@ export const merge: DevInfraMergeConfig['merge'] = async api => {
|
||||
'master': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a',
|
||||
'10.0.x': '27b95ba64a5d99757f4042073fd1860e20e3ed24',
|
||||
},
|
||||
targetLabelExemptScopes: ['dev-infra', 'docs-infra']
|
||||
// `dev-infra` and `docs-infra` are not affecting the public NPM packages. Similarly,
|
||||
// the `bazel` package is not considered part of the public API so that features
|
||||
// can land in patch branches.
|
||||
targetLabelExemptScopes: ['dev-infra', 'docs-infra', 'bazel']
|
||||
};
|
||||
};
|
||||
|
||||
+14
-60
@@ -79,7 +79,6 @@ meta:
|
||||
# one are those that appear above it.
|
||||
no-groups-above-this-pending: &no-groups-above-this-pending len(groups.active.pending.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
|
||||
no-groups-above-this-rejected: &no-groups-above-this-rejected len(groups.active.rejected.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
|
||||
no-groups-above-this-active: &no-groups-above-this-active len(groups.active.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
|
||||
|
||||
can-be-global-approved: &can-be-global-approved '"global-approvers" not in groups.approved'
|
||||
can-be-global-docs-approved: &can-be-global-docs-approved '"global-docs-approvers" not in groups.approved'
|
||||
@@ -97,22 +96,23 @@ meta:
|
||||
# https://developer.github.com/v3/previews/#draft-pull-requests
|
||||
github_api_version: 'shadow-cat-preview'
|
||||
|
||||
pullapprove_conditions:
|
||||
# https://docs.pullapprove.com/config/overrides/
|
||||
# Note that overrides are processed in order.
|
||||
overrides:
|
||||
# For PRs which are still being worked on, either still in draft mode or indicated through WIP in
|
||||
# title or label, PullApprove stays in a pending state until its ready for review.
|
||||
- condition: "'WIP' not in title"
|
||||
unmet_status: pending
|
||||
- if: "draft or 'WIP' in title or 'PR state: WIP' in labels"
|
||||
status: pending
|
||||
explanation: 'Waiting to send reviews as PR is WIP'
|
||||
- condition: "'PR state: WIP' not in labels"
|
||||
unmet_status: pending
|
||||
explanation: 'Waiting to send reviews as PR is WIP'
|
||||
- condition: 'not draft'
|
||||
unmet_status: pending
|
||||
explanation: 'Waiting to send reviews as PR is in draft'
|
||||
# Disable PullApprove on specific PRs by adding the `PullApprove: disable` label
|
||||
- condition: "'PullApprove: disable' not in labels"
|
||||
unmet_status: success
|
||||
- if: "'PullApprove: disable' in labels"
|
||||
status: success
|
||||
explanation: "PullApprove skipped because of 'PullApprove: disable' label"
|
||||
# If no file matching based groups are active, report this pull request as failing. Most likely,
|
||||
# the PR author would need to update the PullApprove config, or create new group.
|
||||
- if: len(groups.active.exclude("required-minimum-review").exclude("global-*")) == 0 and len(groups.approved.include("global-*")) == 0
|
||||
status: failure
|
||||
explanation: 'At least one group must match this PR. Please update an existing review group, or create a new group.'
|
||||
|
||||
groups:
|
||||
# =========================================================
|
||||
@@ -246,8 +246,9 @@ groups:
|
||||
])
|
||||
reviewers:
|
||||
users:
|
||||
- alxhub
|
||||
- AndrewKushnir
|
||||
- alxhub
|
||||
- atscott
|
||||
- JoostK
|
||||
|
||||
# =========================================================
|
||||
@@ -1177,7 +1178,6 @@ groups:
|
||||
'tools/source-map-test/**',
|
||||
'tools/symbol-extractor/**',
|
||||
'tools/testing/**',
|
||||
'tools/ts-api-guardian/**',
|
||||
'tools/tslint/**',
|
||||
'tools/utils/**',
|
||||
'tools/yarn/**',
|
||||
@@ -1305,49 +1305,3 @@ groups:
|
||||
- atscott
|
||||
- jelbourn
|
||||
- josephperrott
|
||||
|
||||
# ====================================================
|
||||
# Catch all for if no groups match the code change
|
||||
# ====================================================
|
||||
fallback:
|
||||
<<: *defaults
|
||||
# A group is considered to be `active` for a PR if at least one of group's
|
||||
# conditions matches the PR.
|
||||
#
|
||||
# The PullApprove CI check should fail if a PR has no `active` groups, as
|
||||
# this indicates the PR is modifying a file that has no owner.
|
||||
#
|
||||
# This is enforced through the pullapprove verification check done
|
||||
# as part of the CircleCI lint job. Failures in this lint job should be
|
||||
# fixed as part of the PR. This can be done by updating the
|
||||
# `.pullapprove.yml` file cover the unmatched path.
|
||||
# The pullapprove verification script is part of the ng-dev tool and can be
|
||||
# run locally with the command: `yarn -s ng-dev pullapprove verify`
|
||||
#
|
||||
# For cases in which the verification check fails to ensure coverage, this
|
||||
# group will be active. The expectation is that this should be remedied
|
||||
# before merging the PR as described above. In an emergency situation
|
||||
# `global-approvers` can still approve PRs that match this `fallback` rule,
|
||||
# but that should be an exception and not an expectation.
|
||||
conditions:
|
||||
- *no-groups-above-this-active
|
||||
# When any of the `global-*` groups is approved, they cause other groups to deactivate.
|
||||
# In those cases, the condition above would evaluate to `true` while in reality, only a global
|
||||
# approval has been provided. To ensure we don't activate the fallback group in such cases,
|
||||
# ensure that no explicit global approval has been provided.
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
# PullApprove uses a combination of users defined in the pullapprove configuration and the
|
||||
# number of users who have performed reviews on Github in the recent past if the configuration
|
||||
# does not specify it. Because, as an open source project, anyone on Github can perform a
|
||||
# review we need to ensure that all groups, including the fallback group, have at least one user
|
||||
# or group defined as reviewers.
|
||||
reviewers:
|
||||
users:
|
||||
- josephperrott
|
||||
reviews:
|
||||
request: 0
|
||||
required: 1
|
||||
# Reviewed-for is required on fallback as it should not ever actually be reviewed, requiring
|
||||
# Reviewed-for helps insure an accidental approval doesn't occur on the fallback group.
|
||||
reviewed_for: required
|
||||
|
||||
+3
-3
@@ -36,18 +36,18 @@ filegroup(
|
||||
srcs = [
|
||||
# We also declare the unminified AngularJS files since these can be used for
|
||||
# local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts)
|
||||
"@npm//:node_modules/angular/angular.js",
|
||||
"@npm//:node_modules/angular/angular.min.js",
|
||||
"@npm//:node_modules/angular-1.5/angular.js",
|
||||
"@npm//:node_modules/angular-1.5/angular.min.js",
|
||||
"@npm//:node_modules/angular-1.6/angular.js",
|
||||
"@npm//:node_modules/angular-1.6/angular.min.js",
|
||||
"@npm//:node_modules/angular-1.7/angular.js",
|
||||
"@npm//:node_modules/angular-1.7/angular.min.js",
|
||||
"@npm//:node_modules/angular-mocks/angular-mocks.js",
|
||||
"@npm//:node_modules/angular-mocks-1.5/angular-mocks.js",
|
||||
"@npm//:node_modules/angular-mocks-1.6/angular-mocks.js",
|
||||
"@npm//:node_modules/angular-mocks-1.7/angular-mocks.js",
|
||||
"@npm//:node_modules/angular-mocks-1.8/angular-mocks.js",
|
||||
"@npm//:node_modules/angular-1.8/angular.js",
|
||||
"@npm//:node_modules/angular-1.8/angular.min.js",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -1,3 +1,92 @@
|
||||
<a name="12.1.4"></a>
|
||||
# 12.1.4 (2021-07-28)
|
||||
### compiler-cli
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| [77ae4459d3](https://github.com/angular/angular/commit/77ae4459d34515493ddf62c7ecdf237260176809) | fix(compiler-cli): use correct module resolution context for absolute imports in .d.ts files ([#42879](https://github.com/angular/angular/pull/42879)) |
|
||||
| [f589b01672](https://github.com/angular/angular/commit/f589b01672d13c1a174b880437d00a910899f29d) | perf(compiler-cli): skip analysis in incremental builds for files without Angular behavior ([#42562](https://github.com/angular/angular/pull/42562)) |
|
||||
### core
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| [a779a1029b](https://github.com/angular/angular/commit/a779a1029b90039ea39d579ecbc98812c301080a) | fix(core): correctly handle `null` or `undefined` in `ErrorHandler#handleError()` ([#42881](https://github.com/angular/angular/pull/42881)) |
|
||||
## Special Thanks:
|
||||
Andrew Scott, Daniel Trevino, Dylan Hunn, George Kalpakas, Joey Perrott, JoostK, Paul Gschwendtner, Pete Bacon Darwin, Teri Glover, Vladyslav, Yuvaraj and codebriefcase
|
||||
|
||||
|
||||
<a name="12.1.3"></a>
|
||||
# 12.1.3 (2021-07-21)
|
||||
### animations
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| [3cddc3d6bc](https://github.com/angular/angular/commit/3cddc3d6bc1045b76980ba3e866350d58deb0718) | fix(animations): normalize final styles in buildStyles ([#42763](https://github.com/angular/angular/pull/42763)) |
|
||||
### compiler-cli
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| [d207ea06d1](https://github.com/angular/angular/commit/d207ea06d1bb554e822bab2eadcf12ef50117bd4) | fix(compiler-cli): use correct module import for types behind a `forwardRef` ([#42887](https://github.com/angular/angular/pull/42887)) |
|
||||
| [e6d520f3d9](https://github.com/angular/angular/commit/e6d520f3d9104c63129b94fbf5fe54b392715fc5) | perf(compiler-cli): optimize cycle detection using a persistent cache ([#41271](https://github.com/angular/angular/pull/41271)) |
|
||||
### core
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| [a6db152c78](https://github.com/angular/angular/commit/a6db152c78bc82ef39e529ab5ea55f810b17fa2e) | fix(core): use correct injector when resolving DI tokens from within a directive provider factory ([#42886](https://github.com/angular/angular/pull/42886)) |
|
||||
## Special Thanks:
|
||||
Alan Agius, David Shevitz, George Kalpakas, Joey Perrott, JoostK, Krzysztof Kotowicz, Minko Gechev, Paul Gschwendtner and dario-piotrowicz
|
||||
|
||||
|
||||
<a name="12.1.2"></a>
|
||||
# 12.1.2 (2021-07-14)
|
||||
### bazel
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| [4a8ab4f149](https://github.com/angular/angular/commit/4a8ab4f1499a560bb4373b088785e43f26384e1c) | fix(bazel): enable dts bundling for Ivy packages ([#42728](https://github.com/angular/angular/pull/42728)) |
|
||||
### common
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| [d654c7933a](https://github.com/angular/angular/commit/d654c7933a732c23dc1e68b17766457d2d11a4d8) | fix(common): re-sort output of `KeyValuePipe` when `compareFn` changes ([#42821](https://github.com/angular/angular/pull/42821)) |
|
||||
### compiler
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| [2566cbb48c](https://github.com/angular/angular/commit/2566cbb48cddd23c4b29d01ad557bc71ac716700) | fix(compiler): add mappings for all HTML entities ([#42818](https://github.com/angular/angular/pull/42818)) |
|
||||
| [65330f03a9](https://github.com/angular/angular/commit/65330f03a92f2743719d0e0d4cedcaadbb8c5b21) | fix(compiler): incorrect context object being referenced from listener instructions inside embedded views ([#42755](https://github.com/angular/angular/pull/42755)) |
|
||||
### compiler-cli
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| [17d3de25da](https://github.com/angular/angular/commit/17d3de25daf339612c267001f0c2237533d7c789) | fix(compiler-cli): properly emit literal types when recreating type parameters in a different file ([#42761](https://github.com/angular/angular/pull/42761)) |
|
||||
| [0a17e98ae2](https://github.com/angular/angular/commit/0a17e98ae2813338f03e3e01bb7cfa6024f4632b) | fix(compiler-cli): inline type checking instructions no longer prevent incremental reuse ([#42759](https://github.com/angular/angular/pull/42759)) |
|
||||
| [45116097c1](https://github.com/angular/angular/commit/45116097c1dafd902014e0f680e0d50023467883) | fix(compiler-cli): support reflecting namespace declarations ([#42728](https://github.com/angular/angular/pull/42728)) |
|
||||
| [df5cc1fbbf](https://github.com/angular/angular/commit/df5cc1fbbf1b1f02f1f45603552a2530bb59688d) | fix(compiler-cli): return directives for an element on a microsyntax template ([#42640](https://github.com/angular/angular/pull/42640)) |
|
||||
### core
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| [63013546e1](https://github.com/angular/angular/commit/63013546e130d823db26ad96e1d4cb346e633a03) | fix(core): associate the NgModule scope for an overridden component ([#42817](https://github.com/angular/angular/pull/42817)) |
|
||||
| [9ebd41e39c](https://github.com/angular/angular/commit/9ebd41e39c597ef6df2de2fb63d6458337e2757d) | fix(core): allow proper type inference when `ngFor` is used with a `trackBy` function ([#42692](https://github.com/angular/angular/pull/42692)) |
|
||||
| [41c6877c01](https://github.com/angular/angular/commit/41c6877c013ba84ba5e46309b2310c097ad9e354) | fix(core): error in TestBed if module is reset mid-compilation in ViewEngine ([#42669](https://github.com/angular/angular/pull/42669)) |
|
||||
### language-service
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| [97c18f4527](https://github.com/angular/angular/commit/97c18f4527187206db8b0f5730b80d2220027f9f) | fix(language-service): Do not override TS LS methods not supported by VE NgLS ([#42727](https://github.com/angular/angular/pull/42727)) |
|
||||
### service-worker
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| [d87917542a](https://github.com/angular/angular/commit/d87917542a00ed6f58c7b95e3eba6aeddc5eee01) | fix(service-worker): correctly handle unrecoverable state when a client no longer exists ([#42736](https://github.com/angular/angular/pull/42736)) |
|
||||
| [f2523a8fef](https://github.com/angular/angular/commit/f2523a8fef549fab6ec480ecfc966c9d938a5423) | fix(service-worker): avoid storing redundant metadata for hashed assets ([#42606](https://github.com/angular/angular/pull/42606)) |
|
||||
## Special Thanks:
|
||||
Alan Agius, Andrew Kushnir, Andrew Scott, Arthur Ming, Bastian, Borislav Ivanov, David Gilson, David Shevitz, Gabriele Franchitto, George Kalpakas, Joey Perrott, JoostK, Kristiyan Kostadinov, Mark Goho, Meir Blumenfeld, Paul Gschwendtner, Pete Bacon Darwin, Ryan Andersen, Theoklitos Bampouris, behrooz bozorg chami, dario-piotrowicz, ivanwonder and mgechev
|
||||
|
||||
|
||||
<a name="12.1.1"></a>
|
||||
# 12.1.1 (2021-06-30)
|
||||
### compiler-cli
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| f6b828e292 | fix(compiler-cli): add support for partially evaluating types (#41661) |
|
||||
### platform-browser
|
||||
| Commit | Description |
|
||||
| -- | -- |
|
||||
| d19ddd1a87 | fix(platform-browser): in `Meta.addTag()` do not add duplicate meta tags (#42703) |
|
||||
## Special Thanks:
|
||||
Alan Agius, Dario Piotrowicz, George Kalpakas, George Looshch, Lars Gyrup Brink Nielsen, Paul Gschwendtner, Pete Bacon Darwin, Zach Arend, codebriefcase, dario-piotrowicz, marvinbeckert, mgechev and pavlenko
|
||||
|
||||
|
||||
<a name="12.1.0"></a>
|
||||
# 12.1.0 (2021-06-24)
|
||||
### compiler
|
||||
|
||||
@@ -8,8 +8,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
# Fetch rules_nodejs so we can install our npm dependencies
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
sha256 = "0fa2d443571c9e02fcb7363a74ae591bdcce2dd76af8677a95965edf329d778a",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/3.6.0/rules_nodejs-3.6.0.tar.gz"],
|
||||
sha256 = "5c40083120eadec50a3497084f99bc75a85400ea727e82e0b2f422720573130f",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/4.0.0-beta.0/rules_nodejs-4.0.0-beta.0.tar.gz"],
|
||||
)
|
||||
|
||||
# Check the rules_nodejs version and download npm dependencies
|
||||
@@ -34,11 +34,6 @@ yarn_install(
|
||||
yarn_lock = "//:yarn.lock",
|
||||
)
|
||||
|
||||
# Load angular dependencies
|
||||
load("//packages/bazel:package.bzl", "rules_angular_dev_dependencies")
|
||||
|
||||
rules_angular_dev_dependencies()
|
||||
|
||||
# Load protractor dependencies
|
||||
load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies")
|
||||
|
||||
@@ -52,13 +47,3 @@ web_test_repositories()
|
||||
load("//dev-infra/bazel/browsers:browser_repositories.bzl", "browser_repositories")
|
||||
|
||||
browser_repositories()
|
||||
|
||||
# Setup the rules_sass toolchain
|
||||
load("@io_bazel_rules_sass//:defs.bzl", "sass_repositories")
|
||||
|
||||
sass_repositories()
|
||||
|
||||
# Setup the skydoc toolchain
|
||||
load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
|
||||
|
||||
skydoc_repositories()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This example project is special in that it is not a cli app. To run tests appropriate for this
|
||||
* project, the test command is overwritten in `aio/content/examples/observables/example-config.json`.
|
||||
*
|
||||
* This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.js` runs
|
||||
* This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.mjs` runs
|
||||
* tests for this project.
|
||||
*
|
||||
* TODO: Fix our infrastructure/tooling, so that this hack is not necessary.
|
||||
|
||||
@@ -50,5 +50,5 @@ export class PopupComponent {
|
||||
private _message = '';
|
||||
|
||||
@Output()
|
||||
closed = new EventEmitter();
|
||||
closed = new EventEmitter<void>();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// #docplaster
|
||||
// #docregion import-http
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
// #docregion props
|
||||
import { Product } from './products';
|
||||
// #enddocregion import-http
|
||||
// #enddocregion props, import-http
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
||||
@@ -79,8 +79,7 @@ export class ConfigService {
|
||||
// The backend returned an unsuccessful response code.
|
||||
// The response body may contain clues as to what went wrong.
|
||||
console.error(
|
||||
`Backend returned code ${error.status}, ` +
|
||||
`body was: ${error.error}`);
|
||||
`Backend returned code ${error.status}, body was: `, error.error);
|
||||
}
|
||||
// Return an observable with a user-facing error message.
|
||||
return throwError(
|
||||
|
||||
@@ -14,7 +14,7 @@ import { NoopInterceptor } from './noop-interceptor';
|
||||
// #enddocregion interceptor-providers
|
||||
import { TrimNameInterceptor } from './trim-name-interceptor';
|
||||
import { UploadInterceptor } from './upload-interceptor';
|
||||
|
||||
import { RetryInterceptor } from './retry-interceptor';
|
||||
// #docregion interceptor-providers
|
||||
|
||||
/** Http interceptor providers in outside-in order */
|
||||
|
||||
@@ -39,7 +39,7 @@ export class RetryInterceptor implements HttpInterceptor {
|
||||
tap(null,
|
||||
() => {
|
||||
// An error has occurred, so increment this request's ERROR_COUNT.
|
||||
req.set(ERROR_COUNT, req.get(ERROR_COUNT) + 1);
|
||||
req.context.set(ERROR_COUNT, req.context.get(ERROR_COUNT) + 1);
|
||||
}),
|
||||
// #docregion reading-context
|
||||
// Retry the request a configurable number of times.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This example project is special in that it is not a cli app. To run tests appropriate for this
|
||||
* project, the test command is overwritten in `aio/content/examples/observables-in-angular/example-config.json`.
|
||||
*
|
||||
* This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.js` runs
|
||||
* This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.mjs` runs
|
||||
* tests for this project.
|
||||
*
|
||||
* TODO: Fix our infrastructure/tooling, so that this hack is not necessary.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This example project is special in that it is not a cli app. To run tests appropriate for this
|
||||
* project, the test command is overwritten in `aio/content/examples/observables/example-config.json`.
|
||||
*
|
||||
* This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.js` runs
|
||||
* This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.mjs` runs
|
||||
* tests for this project.
|
||||
*
|
||||
* TODO: Fix our infrastructure/tooling, so that this hack is not necessary.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This example project is special in that it is not a cli app. To run tests appropriate for this
|
||||
* project, the test command is overwritten in `aio/content/examples/practical-observable-usage/example-config.json`.
|
||||
*
|
||||
* This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.js` runs
|
||||
* This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.mjs` runs
|
||||
* tests for this project.
|
||||
*
|
||||
* TODO: Fix our infrastructure/tooling, so that this hack is not necessary.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This example project is special in that it is not a cli app. To run tests appropriate for this
|
||||
* project, the test command is overwritten in `aio/content/examples/rx-libary/example-config.json`.
|
||||
*
|
||||
* This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.js` runs
|
||||
* This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.mjs` runs
|
||||
* tests for this project.
|
||||
*
|
||||
* TODO: Fix our infrastructure/tooling, so that this hack is not necessary.
|
||||
|
||||
@@ -6,11 +6,11 @@ describe('What is Angular', () => {
|
||||
const paragraphs = element.all(by.css('p'));
|
||||
const buttons = element.all(by.css('button'));
|
||||
const templateButton = buttons.get(1);
|
||||
const templateText = paragraphs.get(3);
|
||||
const templateText = paragraphs.get(4);
|
||||
const messageButton = buttons.get(0);
|
||||
const messageText = paragraphs.get(2);
|
||||
const ngIfButton = buttons.get(2);
|
||||
const ngIfText = paragraphs.get(4);
|
||||
const ngIfText = paragraphs.get(5);
|
||||
const diButton = buttons.get(3);
|
||||
|
||||
beforeEach(() => browser.get(''));
|
||||
|
||||
+1
@@ -4,3 +4,4 @@
|
||||
<!-- #docregion bindings -->
|
||||
<p [id]="sayHelloId" [style.color]="fontColor">You can set my color in the component!</p>
|
||||
<!-- #enddocregion -->
|
||||
<p>My color is {{ fontColor }}</p>
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="center-layout-wide">
|
||||
<div class="nf-container l-flex-wrap flex-center">
|
||||
<img src="assets/images/support/angular-404.svg" width="300" height="300"/>
|
||||
<div class="nf-response l-flex-wrap">
|
||||
<div class="nf-response l-flex-wrap center">
|
||||
<h1 class="no-anchor no-toc">Page Not Found</h1>
|
||||
<p>We're sorry. The page you are looking for cannot be found.</p>
|
||||
</div>
|
||||
|
||||
@@ -173,7 +173,7 @@ Define metadata objects with the following limited syntax:
|
||||
<td><code>`pie is ${multiplier} times better than cake`</code></td>
|
||||
<tr>
|
||||
<td>Literal string</td>
|
||||
<td><code>pi</code></td>
|
||||
<td><code>'pi'</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Literal number</td>
|
||||
@@ -558,7 +558,6 @@ In [Angular Ivy](guide/ivy), the template type checker has been completely rewri
|
||||
As a result, templates that previously compiled under View Engine can fail type checking under Ivy. This can happen because Ivy's stricter checking catches genuine errors, or because application code is not typed correctly, or because the application uses libraries in which typings are inaccurate or not specific enough.
|
||||
|
||||
This stricter type checking is not enabled by default in version 9, but can be enabled by setting the `strictTemplates` configuration option.
|
||||
We do expect to make strict type checking the default in the future.
|
||||
|
||||
For more information about type-checking options, and about improvements to template type checking in version 9 and above, see [Template type checking](guide/template-typecheck).
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ See the <live-example></live-example> for a working example containing the code
|
||||
|
||||
It is recommended that you set an element property with a [property binding](guide/property-binding) whenever possible.
|
||||
However, sometimes you don't have an element property to bind.
|
||||
In those situations, you can use attribute binding.
|
||||
In those situations, use attribute binding.
|
||||
|
||||
For example, [ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) and
|
||||
[SVG](https://developer.mozilla.org/en-US/docs/Web/SVG) are purely attributes.
|
||||
@@ -58,7 +58,7 @@ To use attribute binding with the `<td>` attribute `colspan`:
|
||||
1. Specify the `colspan` attribute by using the following syntax: `[attr.colspan]`.
|
||||
1. Set `[attr.colspan]` equal to an expression.
|
||||
|
||||
In the following example, we bind the `colspan` attribute to the expression `1 + 1`.
|
||||
In the following example, you bind the `colspan` attribute to the expression `1 + 1`.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="colspan" header="src/app/app.component.html"></code-example>
|
||||
|
||||
@@ -78,7 +78,7 @@ For more information on how to bind to the `colSpan` property, see the [`colspan
|
||||
{@a class-binding}
|
||||
## Binding to the `class` attribute
|
||||
|
||||
You can use class binding to add and remove CSS class names from an element's `class` attribute.
|
||||
Use class binding to add and remove CSS class names from an element's `class` attribute.
|
||||
|
||||
### Binding to a single CSS `class`
|
||||
|
||||
@@ -161,7 +161,7 @@ The following table summarizes class binding syntax.
|
||||
{@a style-binding}
|
||||
## Binding to the style attribute
|
||||
|
||||
You can use style binding to set styles dynamically.
|
||||
Use style binding to set styles dynamically.
|
||||
|
||||
### Binding to a single style
|
||||
|
||||
@@ -263,7 +263,7 @@ The following table summarizes style binding syntax.
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The [NgStyle](guide/built-in-directives/#ngstyle) directive can be used as an alternative to direct `[style]` bindings.
|
||||
However, using the above style binding syntax without `NgStyle` is preferred because due to improvements in style binding in Angular, `NgStyle` no longer provides significant value, and might eventually be removed in the future.
|
||||
However, using the preceding style binding syntax without `NgStyle` is preferred because due to improvements in style binding in Angular, `NgStyle` no longer provides significant value, and might eventually be removed in the future.
|
||||
|
||||
</div>
|
||||
|
||||
@@ -294,7 +294,7 @@ When there are multiple bindings to the same class name or style property, Angul
|
||||
|
||||
The more specific a class or style binding is, the higher its precedence.
|
||||
|
||||
A binding to a specific class (for example, `[class.foo]`) will take precedence over a generic `[class]` binding, and a binding to a specific style (for example, `[style.bar]`) will take precedence over a generic `[style]` binding.
|
||||
A binding to a specific class (for example, `[class.foo]`) takes precedence over a generic `[class]` binding, and a binding to a specific style (for example, `[style.bar]`) takes precedence over a generic `[style]` binding.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="basic-specificity" header="src/app/app.component.html"></code-example>
|
||||
|
||||
@@ -311,7 +311,7 @@ Directives often augment component behavior, so host bindings from components ha
|
||||
|
||||
In addition, bindings take precedence over static attributes.
|
||||
|
||||
In the following case, `class` and `[class]` have similar specificity, but the `[class]` binding will take precedence because it is dynamic.
|
||||
In the following case, `class` and `[class]` have similar specificity, but the `[class]` binding takes precedence because it is dynamic.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="dynamic-priority" header="src/app/app.component.html"></code-example>
|
||||
|
||||
@@ -319,14 +319,14 @@ In the following case, `class` and `[class]` have similar specificity, but the `
|
||||
### Delegating to styles with lower precedence
|
||||
|
||||
It is possible for higher precedence styles to "delegate" to lower precedence styles using `undefined` values.
|
||||
Whereas setting a style property to `null` ensures the style is removed, setting it to `undefined` will cause Angular to fall back to the next-highest precedence binding to that style.
|
||||
Whereas setting a style property to `null` ensures the style is removed, setting it to `undefined` causes Angular to fall back to the next-highest precedence binding to that style.
|
||||
|
||||
For example, consider the following template:
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="style-delegation" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Imagine that the `dirWithHostBinding` directive and the `comp-with-host-binding` component both have a `[style.width]` host binding.
|
||||
In that case, if `dirWithHostBinding` sets its binding to `undefined`, the `width` property will fall back to the value of the `comp-with-host-binding` host binding.
|
||||
In that case, if `dirWithHostBinding` sets its binding to `undefined`, the `width` property falls back to the value of the `comp-with-host-binding` host binding.
|
||||
However, if `dirWithHostBinding` sets its binding to `null`, the `width` property will be removed entirely.
|
||||
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ The `CountdownLocalVarParentComponent` that hosts the timer component is as foll
|
||||
The parent component cannot data bind to the child's
|
||||
`start` and `stop` methods nor to its `seconds` property.
|
||||
|
||||
You can place a local variable, `#timer`, on the tag `<countdown-timer>` representing the child component.
|
||||
You can place a local variable, `#timer`, on the tag `<app-countdown-timer>` representing the child component.
|
||||
That gives you a reference to the child component and the ability to access
|
||||
*any of its properties or methods* from within the parent template.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Event binding
|
||||
|
||||
Event binding allows you to listen for and respond to user actions such as keystrokes, mouse movements, clicks, and touches.
|
||||
Event binding lets you listen for and respond to user actions such as keystrokes, mouse movements, clicks, and touches.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -26,7 +26,7 @@ The event binding listens for the button's click events and calls the component'
|
||||
|
||||
## Binding to passive events
|
||||
|
||||
Angular also supports passive event listeners. For example, you can use the following steps to make a scroll event passive.
|
||||
Angular also supports passive event listeners. For example, use the following steps to make a scroll event passive.
|
||||
|
||||
1. Create a file `zone-flags.ts` under `src` directory.
|
||||
2. Add the following line into this file.
|
||||
|
||||
@@ -111,20 +111,6 @@ Project-specific [TypeScript](https://www.typescriptlang.org/) configuration fil
|
||||
| `tsconfig.spec.json` | [TypeScript](https://www.typescriptlang.org/) configuration for the application tests. See [TypeScript Configuration](guide/typescript-configuration). |
|
||||
| `tslint.json` | Application-specific [TSLint](https://palantir.github.io/tslint/) configuration. |
|
||||
|
||||
### End-to-end test files
|
||||
|
||||
An `e2e/` folder at the top level contains source files for a set of end-to-end tests that correspond to the root-level application, along with test-specific configuration files.
|
||||
|
||||
For a multi-project workspace, application-specific end-to-end tests are in the project root, under `projects/project-name/e2e/`.
|
||||
|
||||
<code-example language="none">
|
||||
e2e/
|
||||
src/ (end-to-end tests for my-app)
|
||||
app.e2e-spec.ts
|
||||
app.po.ts
|
||||
protractor.conf.js (test-tool config)
|
||||
tsconfig.json (TypeScript config inherits from workspace)
|
||||
</code-example>
|
||||
|
||||
{@a multiple-projects}
|
||||
|
||||
@@ -160,9 +146,6 @@ my-workspace/
|
||||
projects/ (generated applications and libraries)
|
||||
my-first-app/ --(an explicitly generated application)
|
||||
... --(application-specific config)
|
||||
e2e/ ----(corresponding e2e tests)
|
||||
src/ ----(e2e tests source)
|
||||
... ----(e2e-specific config)
|
||||
src/ --(source and support files for application)
|
||||
my-lib/ --(a generated library)
|
||||
... --(library-specific config)
|
||||
@@ -173,7 +156,7 @@ my-workspace/
|
||||
|
||||
When you generate a library using the CLI (with a command such as `ng generate library my-lib`), the generated files go into the `projects/` folder of the workspace. For more information about creating your own libraries, see [Creating Libraries](guide/creating-libraries).
|
||||
|
||||
Libraries (unlike applications and their associated e2e projects) have their own `package.json` configuration file.
|
||||
Libraries unlike applications have their own `package.json` configuration file.
|
||||
|
||||
Under the `projects/` folder, the `my-lib` folder contains your library code.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Sharing data between child and parent directives and components
|
||||
|
||||
A common pattern in Angular is sharing data between a parent component and one or more child components.
|
||||
You can implement this pattern by using the `@Input()` and `@Output()` directives.
|
||||
To implement this pattern use the `@Input()` and `@Output()` decorators.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -21,8 +21,8 @@ Consider the following hierarchy:
|
||||
The `<parent-component>` serves as the context for the `<child-component>`.
|
||||
|
||||
`@Input()` and `@Output()` give a child component a way to communicate with its parent component.
|
||||
`@Input()` allows a parent component to update data in the child component.
|
||||
Conversely, `@Output()` allows the child to send data to a parent component.
|
||||
`@Input()` lets a parent component update data in the child component.
|
||||
Conversely, `@Output()` lets the child send data to a parent component.
|
||||
|
||||
|
||||
{@a input}
|
||||
@@ -80,14 +80,14 @@ The binding source, the part to the right of the equal sign, is the data that th
|
||||
|
||||
### Watching for `@Input()` changes
|
||||
|
||||
To watch for changes on an `@Input()` property, you can use `OnChanges`, one of Angular's [lifecycle hooks](guide/lifecycle-hooks).
|
||||
To watch for changes on an `@Input()` property, use `OnChanges`, one of Angular's [lifecycle hooks](guide/lifecycle-hooks).
|
||||
See the [`OnChanges`](guide/lifecycle-hooks#onchanges) section of the [Lifecycle Hooks](guide/lifecycle-hooks) guide for more details and examples.
|
||||
|
||||
{@a output}
|
||||
|
||||
## Sending data to a parent component
|
||||
|
||||
The `@Output()` decorator in a child component or directive allows data to flow from the child to the parent.
|
||||
The `@Output()` decorator in a child component or directive lets data flow from the child to the parent.
|
||||
|
||||
<div class="lightbox">
|
||||
<img src="generated/images/guide/inputs-outputs/output.svg" alt="Output diagram of the data flow going from child to parent">
|
||||
@@ -118,7 +118,7 @@ The following example features an `<input>` where a user can enter a value and c
|
||||
|
||||
<code-example path="inputs-outputs/src/app/item-output/item-output.component.ts" region="item-output" header="src/app/item-output/item-output.component.ts"></code-example>
|
||||
|
||||
The different parts of the above declaration are as follows:
|
||||
The different parts of the preceding declaration are as follows:
|
||||
|
||||
* `@Output()`—a decorator function marking the property as a way for data to go from the child to the parent
|
||||
* `newItemEvent`—the name of the `@Output()`
|
||||
@@ -166,7 +166,7 @@ The `addItem()` method takes an argument in the form of a string and then adds t
|
||||
|
||||
The `$event` contains the data that the user types into the `<input>` in the child template UI.
|
||||
|
||||
To see the `@Output()` working, you can add the following to the parent's template:
|
||||
To see the `@Output()` working, add the following to the parent's template:
|
||||
|
||||
```html
|
||||
<ul>
|
||||
@@ -180,7 +180,7 @@ The `addItem()` method takes an argument in the form of a string and then adds t
|
||||
|
||||
## Using `@Input()` and `@Output()` together
|
||||
|
||||
You can use `@Input()` and `@Output()` on the same child component as follows:
|
||||
Use `@Input()` and `@Output()` on the same child component as follows:
|
||||
|
||||
<code-example path="inputs-outputs/src/app/app.component.html" region="together" header="src/app/app.component.html"></code-example>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Text interpolation
|
||||
|
||||
Text interpolation allows you to incorporate dynamic string values into your HTML templates.
|
||||
With interpolation, you can dynamically change what appears in an application view, such as displaying a custom greeting that includes the user's name.
|
||||
Text interpolation lets you incorporate dynamic string values into your HTML templates.
|
||||
Use interpolation to dynamically change what appears in an application view, such as displaying a custom greeting that includes the user's name.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -18,7 +18,7 @@ To illustrate how interpolation works, consider an Angular component that contai
|
||||
|
||||
<code-example path="interpolation/src/app/app.component.ts" region="customer" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
You can use interpolation to display the value of this variable in the corresponding component template:
|
||||
Use interpolation to display the value of this variable in the corresponding component template:
|
||||
|
||||
<code-example path="interpolation/src/app/app.component.html" region="interpolation-example1" header="src/app/app.component.html"></code-example>
|
||||
|
||||
@@ -55,7 +55,7 @@ With interpolation, Angular performs the following tasks:
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You can configure the interpolation delimiter with the [interpolation](api/core/Component#interpolation) option in the `@Component()` metadata.
|
||||
Configure the interpolation delimiter with the [interpolation](api/core/Component#interpolation) option in the `@Component()` metadata.
|
||||
|
||||
</div>
|
||||
|
||||
@@ -131,7 +131,7 @@ When using template expressions, follow these best practices:
|
||||
* **Use short expressions**
|
||||
|
||||
Use property names or method calls whenever possible.
|
||||
Keep application and business logic in the component, where it is easier to develop and test.
|
||||
Keep application and business logic in the component, where it is accessible to develop and test.
|
||||
|
||||
* **Quick execution**
|
||||
|
||||
|
||||
+16
-16
@@ -1,7 +1,7 @@
|
||||
# Transforming Data Using Pipes
|
||||
|
||||
Use [pipes](guide/glossary#pipe "Definition of a pipe") to transform strings, currency amounts, dates, and other data for display.
|
||||
Pipes are simple functions you can use in [template expressions](/guide/glossary#template-expression "Definition of template expression") to accept an input value and return a transformed value. Pipes are useful because you can use them throughout your application, while only declaring each pipe once.
|
||||
Pipes are simple functions to use in [template expressions](/guide/glossary#template-expression "Definition of template expression") to accept an input value and return a transformed value. Pipes are useful because you can use them throughout your application, while only declaring each pipe once.
|
||||
For example, you would use a pipe to show a date as **April 15, 1988** rather than the raw string format.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@@ -27,7 +27,7 @@ The following are commonly used built-in pipes for data formatting:
|
||||
|
||||
</div>
|
||||
|
||||
You can also create pipes to encapsulate custom transformations and use your custom pipes in template expressions.
|
||||
Create pipes to encapsulate custom transformations and use your custom pipes in template expressions.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -64,12 +64,12 @@ The component's `birthday` value flows through the pipe operator, `|` to the [`d
|
||||
## Transforming data with parameters and chained pipes
|
||||
|
||||
Use optional parameters to fine-tune a pipe's output.
|
||||
For example, you can use the [`CurrencyPipe`](api/common/CurrencyPipe "API reference") with a country code such as EUR as a parameter.
|
||||
For example, use the [`CurrencyPipe`](api/common/CurrencyPipe "API reference") with a country code such as EUR as a parameter.
|
||||
The template expression `{{ amount | currency:'EUR' }}` transforms the `amount` to currency in euros.
|
||||
Follow the pipe name (`currency`) with a colon (`:`) and the parameter value (`'EUR'`).
|
||||
|
||||
If the pipe accepts multiple parameters, separate the values with colons.
|
||||
For example, `{{ amount | currency:'EUR':'Euros '}}` adds the second parameter, the string literal `'Euros '`, to the output string. You can use any valid template expression as a parameter, such as a string literal or a component property.
|
||||
For example, `{{ amount | currency:'EUR':'Euros '}}` adds the second parameter, the string literal `'Euros '`, to the output string. Use any valid template expression as a parameter, such as a string literal or a component property.
|
||||
|
||||
Some pipes require at least one parameter and allow more optional parameters, such as [`SlicePipe`](/api/common/SlicePipe "API reference for SlicePipe"). For example, `{{ slice:1:5 }}` creates a new array or string containing a subset of the elements starting with element `1` and ending with element `5`.
|
||||
|
||||
@@ -110,7 +110,7 @@ For `date` pipe format options, see [DatePipe](api/common/DatePipe "DatePipe API
|
||||
|
||||
### Example: Applying two formats by chaining pipes
|
||||
|
||||
You can chain pipes so that the output of one pipe becomes the input to the next.
|
||||
Chain pipes so that the output of one pipe becomes the input to the next.
|
||||
|
||||
In the following example, chained pipes first apply a format to a date value, then convert the formatted date to uppercase characters.
|
||||
The first tab for the `src/app/app.component.html` template chains `DatePipe` and `UpperCasePipe` to display the birthday as **APR 15, 1988**.
|
||||
@@ -134,7 +134,7 @@ The second tab for the `src/app/app.component.html` template passes the `fullDat
|
||||
## Creating pipes for custom data transformations
|
||||
|
||||
Create custom pipes to encapsulate transformations that are not provided with the built-in pipes.
|
||||
You can then use your custom pipe in template expressions, the same way you use built-in pipes—to transform input values to output values for display.
|
||||
Then, use your custom pipe in template expressions, the same way you use built-in pipes—to transform input values to output values for display.
|
||||
|
||||
### Marking a class as a pipe
|
||||
|
||||
@@ -160,9 +160,9 @@ Angular invokes the `transform` method with the value of a binding as the first
|
||||
|
||||
### Example: Transforming a value exponentially
|
||||
|
||||
In a game, you may want to implement a transformation that raises a value exponentially to increase a hero's power.
|
||||
In a game, you might want to implement a transformation that raises a value exponentially to increase a hero's power.
|
||||
For example, if the hero's score is 2, boosting the hero's power exponentially by 10 produces a score of 1024.
|
||||
You can use a custom pipe for this transformation.
|
||||
Use a custom pipe for this transformation.
|
||||
|
||||
The following code example shows two component definitions:
|
||||
|
||||
@@ -261,7 +261,7 @@ In other words, given the same input, a pure function should always return the s
|
||||
With a pure pipe, Angular ignores changes within composite objects, such as a newly added element of an existing array, because checking a primitive value or object reference is much faster than performing a deep check for differences within objects.
|
||||
Angular can quickly determine if it can skip executing the pipe and updating the view.
|
||||
|
||||
However, a pure pipe with an array as input may not work the way you want.
|
||||
However, a pure pipe with an array as input might not work the way you want.
|
||||
To demonstrate this issue, change the previous example to filter the list of heroes to just those heroes who can fly.
|
||||
Use the `FlyingHeroesPipe` in the `*ngFor` repeater as shown in the following code.
|
||||
The tabs for the example show the following:
|
||||
@@ -290,16 +290,16 @@ This happens because the code that adds a hero does so by pushing it onto the `h
|
||||
The change detector ignores changes to elements of an array, so the pipe doesn't run.
|
||||
|
||||
The reason Angular ignores the changed array element is that the *reference* to the array hasn't changed.
|
||||
Since the array is the same, Angular does not update the display.
|
||||
Because the array is the same, Angular does not update the display.
|
||||
|
||||
One way to get the behavior you want is to change the object reference itself.
|
||||
You can replace the array with a new array containing the newly changed elements, and then input the new array to the pipe.
|
||||
In the above example, you can create an array with the new hero appended, and assign that to `heroes`. Angular detects the change in the array reference and executes the pipe.
|
||||
Replace the array with a new array containing the newly changed elements, and then input the new array to the pipe.
|
||||
In the preceding example, create an array with the new hero appended, and assign that to `heroes`. Angular detects the change in the array reference and executes the pipe.
|
||||
|
||||
To summarize, if you mutate the input array, the pure pipe doesn't execute.
|
||||
If you *replace* the input array, the pipe executes and the display is updated.
|
||||
|
||||
The above example demonstrates changing a component's code to accommodate a pipe.
|
||||
The preceding example demonstrates changing a component's code to accommodate a pipe.
|
||||
|
||||
To keep your component independent of HTML templates that use pipes, you can, as an alternative, use an *impure* pipe to detect changes within composite objects such as arrays, as described in the next section.
|
||||
|
||||
@@ -341,7 +341,7 @@ The example shows that you don't have to change anything else—the only differe
|
||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" header="src/app/flying-heroes.pipe.ts (filter)" region="filter"></code-example>
|
||||
|
||||
You can derive a `FlyingHeroesImpureComponent` from `FlyingHeroesComponent`.
|
||||
As shown in the code below, only the pipe in the template changes.
|
||||
As shown in the following code, only the pipe in the template changes.
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes-impure.component.html" header="src/app/flying-heroes-impure.component.html (excerpt)" region="template-flying-heroes"></code-example>
|
||||
|
||||
@@ -382,7 +382,7 @@ The following code example binds an observable of message strings
|
||||
To [communicate with backend services using HTTP](/guide/http "Communicating with backend services using HTTP"), the `HttpClient` service uses observables and offers the `HttpClient.get()` method to fetch data from a server.
|
||||
The asynchronous method sends an HTTP request, and returns an observable that emits the requested data for the response.
|
||||
|
||||
As shown in the previous section, you can use the impure `AsyncPipe` to accept an observable as input and subscribe to the input automatically.
|
||||
As shown in the previous section, use the impure `AsyncPipe` to accept an observable as input and subscribe to the input automatically.
|
||||
You can also create an impure pipe to make and cache an HTTP request.
|
||||
|
||||
Impure pipes are called whenever change detection runs for a component, which could be as often as every few milliseconds.
|
||||
@@ -403,7 +403,7 @@ The tabs show the following:
|
||||
</code-pane>
|
||||
</code-tabs>
|
||||
|
||||
In the above example, a breakpoint on the pipe's request for data shows the following:
|
||||
In the preceding example, a breakpoint on the pipe's request for data shows the following:
|
||||
|
||||
* Each binding gets its own pipe instance.
|
||||
* Each pipe instance caches its own URL and data and calls the server only once.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Property binding
|
||||
|
||||
Property binding in Angular helps you set values for properties of HTML elements or directives.
|
||||
With property binding, you can do things such as toggle button functionality, set paths programmatically, and share values between components.
|
||||
Use property binding to do things such as toggle button functionality, set paths programmatically, and share values between components.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -77,7 +77,7 @@ You'd get this error:
|
||||
|
||||
<code-example language="bash">
|
||||
Template parse errors:
|
||||
Can't bind to 'colspan' since it isn't a known native property
|
||||
Can't bind to 'colspan' because it isn't a known built-in property
|
||||
</code-example>
|
||||
|
||||
As the message says, the `<td>` element does not have a `colspan` property. This is true
|
||||
@@ -191,7 +191,7 @@ The following binding pairs do the same thing.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="property-binding-interpolation" header="src/app/app.component.html"></code-example>
|
||||
|
||||
You can use either form when rendering data values as strings, though interpolation is preferable for readability.
|
||||
Use either form when rendering data values as strings, though interpolation is preferable for readability.
|
||||
However, when setting an element property to a non-string data value, you must use property binding.
|
||||
|
||||
## What's next
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# Angular Roadmap
|
||||
|
||||
<p class="roadmap-last-updated">Last updated: 2021-05-19</p>
|
||||
|
||||
Angular receives a large number of feature requests, both from inside Google and from the broader open-source community. At the same time, our list of projects contains plenty of maintenance tasks, code refactorings, and potential performance improvements. We bring together representatives from developer relations, product management, and engineering to prioritize this list. As new projects come into the queue, we regularly position them based on relative priority to other projects. As work gets done, projects will move up in the queue.
|
||||
|
||||
The projects below are not associated with a particular Angular version. We'll release them on completion, and they will be part of a specific version based on our release schedule, following semantic versioning. For example, features are released in the next minor after they are complete, or the next major if they include breaking changes.
|
||||
|
||||
## In Progress
|
||||
## In progress
|
||||
|
||||
### Improve test times and debugging with automatic test environment tear down
|
||||
|
||||
@@ -80,10 +82,16 @@ A long-standing feature request is to add the ability to add directives to host
|
||||
|
||||
A common problem with web applications is their slow initial load time. A way to improve it is to apply more granular code-splitting on a component level. To encourage this practice, we’ll be working on more ergonomic code-splitting APIs.
|
||||
|
||||
<details class="roadmap-done-details" open="true">
|
||||
<summary class="roadmap-done-summary">
|
||||
<h2>Done</h2>
|
||||
<details class="completed-details" open="true">
|
||||
<summary>
|
||||
<h2>Completed</h2>
|
||||
<span class="actions">
|
||||
<span class="action-expand">Show all</span>
|
||||
<span class="action-collapse">Hide all</span>
|
||||
<i class="material-icons expand">expand_more</i>
|
||||
</span>
|
||||
</summary>
|
||||
<div class="details-content">
|
||||
|
||||
### Accelerated debugging and performance profiling with Angular DevTools
|
||||
|
||||
@@ -157,10 +165,11 @@ _Completed Q4 2020_
|
||||
|
||||
With the deprecation of TSLint we will be moving to ESLint. As part of the process, we will work on ensuring backward compatibility with our current recommended TSLint configuration, implement a migration strategy for existing Angular applications and introduce new tooling to the Angular CLI toolchain.
|
||||
|
||||
### Operation Bye Bye Backlog (aka Operation Byelog)
|
||||
### Operation Bye Bye Backlog (aka Operation Byelog)
|
||||
|
||||
_Completed Q4 2020_
|
||||
|
||||
We are actively investing up to 50% of our engineering capacity on triaging issues and PRs until we have a clear understanding of broader community needs. After that, we'll commit up to 20% of our engineering capacity to keep up with new submissions promptly.
|
||||
|
||||
</div>
|
||||
</details>
|
||||
|
||||
@@ -2528,7 +2528,7 @@ Add the `NavigationExtras` object to the `router.navigate()` method that navigat
|
||||
<code-example path="router/src/app/auth/auth.guard.4.ts" header="src/app/auth/auth.guard.ts (v3)"></code-example>
|
||||
|
||||
You can also preserve query parameters and fragments across navigations without having to provide them again when navigating.
|
||||
In the `LoginComponent`, you'll add an *object* as the second argument in the `router.navigateUrl()` function and provide the `queryParamsHandling` and `preserveFragment` to pass along the current query parameters and fragment to the next route.
|
||||
In the `LoginComponent`, you'll add an *object* as the second argument in the `router.navigate()` function and provide the `queryParamsHandling` and `preserveFragment` to pass along the current query parameters and fragment to the next route.
|
||||
|
||||
<code-example path="router/src/app/auth/login/login.component.ts" header="src/app/auth/login/login.component.ts (preserve)" region="preserve"></code-example>
|
||||
|
||||
|
||||
@@ -34,9 +34,14 @@ Before creating a route, you should be familiar with the following:
|
||||
<p>Add more routing features to the Tour of Heroes tutorial.</p>
|
||||
<p class="card-footer">Routing Tour of Heroes</p>
|
||||
</a>
|
||||
<a href="guide/routing-with-urlmatcher" class="docs-card" title="Creating custom route matches tutorial">
|
||||
<section>Creating custom route matches tutorial</section>
|
||||
<p>A tutorial that covers how to use custom matching strategy patterns with Angular routing.</p>
|
||||
<p class="card-footer">Custom route matches tutorial</p>
|
||||
</a>
|
||||
<a href="guide/router-reference" class="docs-card" title="Router reference">
|
||||
<section>Router reference</section>
|
||||
<p>Describes some core router API concepts.</p>
|
||||
<p class="card-footer">Router reference</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -59,6 +59,8 @@ To systematically block XSS bugs, Angular treats all values as untrusted by defa
|
||||
|
||||
Unlike values to be used for rendering, Angular templates are considered trusted by default, and should be treated as executable code. Never generate templates by concatenating user input and template syntax. Doing this would enable attackers to [inject arbitrary code](https://en.wikipedia.org/wiki/Code_injection) into your application. To prevent these vulnerabilities, always use the default [AOT template compiler](/guide/security#offline-template-compiler) in production deployments.
|
||||
|
||||
An additional layer of protection can be provided through the use of Content security policy and Trusted Types. These web platform features operate at the DOM level which is the most effective place to prevent XSS issues because they can't be bypassed using other, lower-level APIs. For this reason, we strongly encourage developers to take advantage of these features by configuring the [content security policy](#content-security-policy) for their application and enabling [trusted types enforcement](#trusted-types).
|
||||
|
||||
### Sanitization and security contexts
|
||||
|
||||
_Sanitization_ is the inspection of an untrusted value, turning it into a value that's safe to insert into
|
||||
@@ -100,7 +102,7 @@ Angular recognizes the value as unsafe and automatically sanitizes it, which rem
|
||||
|
||||
### Direct use of the DOM APIs and explicit sanitization calls
|
||||
|
||||
Built-in browser DOM APIs don't automatically protect you from security vulnerabilities.
|
||||
Unless you enforce Trusted Types, the built-in browser DOM APIs don't automatically protect you from security vulnerabilities.
|
||||
For example, `document`, the node available through `ElementRef`, and many third-party APIs
|
||||
contain unsafe methods. In the same way, if you interact with other libraries that manipulate
|
||||
the DOM, you likely won't have the same automatic sanitization as with Angular interpolations.
|
||||
@@ -169,6 +171,59 @@ technique to prevent XSS. To enable CSP, configure your web server to return an
|
||||
[Web Fundamentals guide](https://developers.google.com/web/fundamentals/security/csp) on the
|
||||
Google Developers website.
|
||||
|
||||
{@a trusted-types}
|
||||
### Enforcing Trusted Types
|
||||
|
||||
We recommend the use of [Trusted Types](https://w3c.github.io/webappsec-trusted-types/dist/spec/) as a way to help secure your applications from cross-site scripting attacks. Trusted Types is a [web platform](https://en.wikipedia.org/wiki/Web_platform)
|
||||
feature that can help you prevent cross-site scripting attacks by enforcing
|
||||
safer coding practices. Trusted Types can also help simplify the auditing of application code.
|
||||
|
||||
<div class="callout is-helpful">
|
||||
|
||||
Trusted Types might not yet be available in all browsers your application targets. In the case your Trusted-Types-enabled application runs in a browser that doesn't support Trusted Types, the functionality of the application will be preserved, and your application will be guarded against XSS by way of Angular's DomSanitizer. See [caniuse.com/trusted-types](https://caniuse.com/trusted-types) for the current browser support.
|
||||
|
||||
</div>
|
||||
|
||||
To enforce Trusted Types for your application, you must configure your application's web server to emit HTTP headers with one of the following Angular policies:
|
||||
|
||||
* `angular` - This policy is used in security-reviewed code that is internal to Angular, and is required for Angular to function when Trusted Types are enforced. Any inline template values or content sanitized by Angular is treated as safe by this policy.
|
||||
* `angular#unsafe-bypass` - This policy is used for applications that use any of the methods in Angular's [DomSanitizer](api/platform-browser/DomSanitizer) that bypass security, such as `bypassSecurityTrustHtml`. Any application that uses these methods must enable this policy.
|
||||
* `angular#unsafe-jit` - This policy is used by the [JIT compiler](api/core/Compiler). You must enable this policy if your application interacts directly with the JIT compiler or is running in JIT mode using the [platform browser dynamic](api/platform-browser-dynamic/platformBrowserDynamic).
|
||||
|
||||
You should configure the HTTP headers for Trusted Types in the following locations:
|
||||
|
||||
* Production serving infrastructure
|
||||
* Angular CLI (`ng serve`), using the `headers` property in the `angular.json` file, for local development and end-to-end testing
|
||||
* Karma (`ng test`), using the `customHeaders` property in the `karma.config.js` file, for unit testing
|
||||
|
||||
The following is an example of a header specifically configured for Trusted Types and Angular:
|
||||
|
||||
<code-example language="html">
|
||||
Content-Security-Policy: trusted-types angular; require-trusted-types-for 'script';
|
||||
</code-example>
|
||||
|
||||
The following is an example of a header specifically configured for Trusted Types and Angular applications that use any of the methods in Angular's [DomSanitizer](api/platform-browser/DomSanitizer) that bypasses security.
|
||||
|
||||
<code-example language="html">
|
||||
Content-Security-Policy: trusted-types angular angular#unsafe-bypass; require-trusted-types-for 'script';
|
||||
</code-example>
|
||||
|
||||
The following is an example of a header specifically configured for Trusted Types and Angular applications using JIT:
|
||||
|
||||
<code-example language="html">
|
||||
Content-Security-Policy: trusted-types angular angular#unsafe-jit; require-trusted-types-for 'script';
|
||||
</code-example>
|
||||
|
||||
<div class="callout is-helpful">
|
||||
|
||||
<header>Community contributions</header>
|
||||
|
||||
To learn more about troubleshooting Trusted Type configurations, the following resource might be helpful:
|
||||
|
||||
[Prevent DOM-based cross-site scripting vulnerabilities with Trusted Types](https://web.dev/trusted-types/#how-to-use-trusted-types)
|
||||
|
||||
</div>
|
||||
|
||||
{@a offline-template-compiler}
|
||||
|
||||
### Use the AOT template compiler
|
||||
|
||||
@@ -6,7 +6,7 @@ It includes information about prerequisites, installing the CLI, creating an ini
|
||||
<div class="callout is-helpful">
|
||||
<header>Try Angular without local setup</header>
|
||||
|
||||
If you are new to Angular, you might want to start with [Try it now!](start), which introduces the essentials of Angular in the context of a ready-made basic online store app that you can examine and modify. This standalone tutorial takes advantage of the interactive [StackBlitz](https://stackblitz.com/) environment for online development. You don't need to set up your local environment until you're ready.
|
||||
If you are new to Angular, you might want to start with [Try it now!](start), which introduces the essentials of Angular in the context of a ready-made basic online store app for you to examine and modify. This standalone tutorial takes advantage of the interactive [StackBlitz](https://stackblitz.com/) environment for online development. You don't need to set up your local environment until you're ready.
|
||||
|
||||
</div>
|
||||
|
||||
@@ -87,7 +87,7 @@ The CLI creates a new workspace and a simple Welcome app, ready to run.
|
||||
|
||||
## Run the application
|
||||
|
||||
The Angular CLI includes a server, so that you can build and serve your app locally.
|
||||
The Angular CLI includes a server, for you to build and serve your app locally.
|
||||
|
||||
1. Navigate to the workspace folder, such as `my-app`.
|
||||
|
||||
@@ -118,6 +118,6 @@ If your installation and setup was successful, you should see a page similar to
|
||||
|
||||
* Work through the [Tour of Heroes Tutorial](tutorial), a complete hands-on exercise that introduces you to the app development process using the Angular CLI and walks through important subsystems.
|
||||
|
||||
* To learn more about using the Angular CLI, see the [CLI Overview](cli "CLI Overview"). In addition to creating the initial workspace and app scaffolding, you can use the CLI to generate Angular code such as components and services. The CLI supports the full development cycle, including building, testing, bundling, and deployment.
|
||||
* To learn more about using the Angular CLI, see the [CLI Overview](cli "CLI Overview"). In addition to creating the initial workspace and app scaffolding, use the CLI to generate Angular code such as components and services. The CLI supports the full development cycle, including building, testing, bundling, and deployment.
|
||||
|
||||
* For more information about the Angular files generated by `ng new`, see [Workspace and Project File Structure](guide/file-structure).
|
||||
|
||||
@@ -53,7 +53,7 @@ The guideline uses the shortcut `hero.component.ts|html|css|spec` to represent t
|
||||
## Single responsibility
|
||||
|
||||
Apply the
|
||||
<a href="https://wikipedia.org/wiki/Single_responsibility_principle"><i>single responsibility principle</i> (SRP)</a>
|
||||
<a href="https://wikipedia.org/wiki/Single_responsibility_principle"><i>single responsibility principle (SRP)</i></a>
|
||||
to all components, services, and other symbols.
|
||||
This helps make the application cleaner, easier to read and maintain, and more testable.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SVG as templates
|
||||
|
||||
You can use SVG files as templates in your Angular applications. When you use an SVG as the template, you are able to use directives and bindings just like with HTML templates. With these features, you can dynamically generate interactive graphics.
|
||||
You can use SVG files as templates in your Angular applications. When you use an SVG as the template, you are able to use directives and bindings just like with HTML templates. Use these features to dynamically generate interactive graphics.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Template variables
|
||||
|
||||
Template variables help you use data from one part of a template in another part of the template.
|
||||
With template variables, you can perform tasks such as respond to user input or finely tune your application's forms.
|
||||
Use template variables to perform tasks such as respond to user input or finely tune your application's forms.
|
||||
|
||||
A template variable can refer to the following:
|
||||
|
||||
@@ -24,7 +24,7 @@ The following template variable, `#phone`, declares a `phone` variable on an `<i
|
||||
|
||||
<code-example path="template-reference-variables/src/app/app.component.html" region="ref-var" header="src/app/app.component.html"></code-example>
|
||||
|
||||
You can refer to a template variable anywhere in the component's template.
|
||||
Refer to a template variable anywhere in the component's template.
|
||||
Here, a `<button>` further down the template refers to the `phone` variable.
|
||||
|
||||
<code-example path="template-reference-variables/src/app/app.component.html" region="ref-phone" header="src/app/app.component.html"></code-example>
|
||||
@@ -59,12 +59,12 @@ There is, however, a difference between a `Component` and a `Directive` in that
|
||||
With `NgForm`, `itemForm` is a reference to the [NgForm](api/forms/NgForm "API: NgForm") directive with the ability to track the value and validity of every control in the form.
|
||||
|
||||
Unlike the native `<form>` element, the `NgForm` directive has a `form` property.
|
||||
The `NgForm` `form` property allows you to disable the submit button if the `itemForm.form.valid` is invalid.
|
||||
The `NgForm` `form` property lets you disable the submit button if the `itemForm.form.valid` is invalid.
|
||||
|
||||
|
||||
## Template variable scope
|
||||
|
||||
You can refer to a template variable anywhere within its surrounding template.
|
||||
Refer to a template variable anywhere within its surrounding template.
|
||||
[Structural directives](guide/built-in-directives), such as `*ngIf` and `*ngFor`, or `<ng-template>` act as a template boundary.
|
||||
You cannot access template variables outside of these boundaries.
|
||||
|
||||
@@ -85,7 +85,7 @@ In the following example, changing the text in the `<input>` changes the value i
|
||||
In this case, there is an implied `<ng-template>` around the `<span>` and the definition of the variable is outside of it.
|
||||
Accessing a template variable from the parent template works because the child template inherits the context from the parent template.
|
||||
|
||||
Rewriting the above code in a more verbose form explicitly shows the `<ng-template>`.
|
||||
Rewriting the preceding code in a more verbose form explicitly shows the `<ng-template>`.
|
||||
|
||||
```html
|
||||
|
||||
@@ -93,7 +93,7 @@ Rewriting the above code in a more verbose form explicitly shows the `<ng-templa
|
||||
|
||||
<!-- New template -->
|
||||
<ng-template [ngIf]="true">
|
||||
<!-- Since the context is inherited, the value is available to the new template -->
|
||||
<!-- Because the context is inherited, the value is available to the new template -->
|
||||
<span>Value: {{ ref1.value }}</span>
|
||||
</ng-template>
|
||||
|
||||
@@ -157,13 +157,13 @@ __proto__: Function
|
||||
{@a template-input-variables}
|
||||
## Template input variable
|
||||
|
||||
A _template input variable_ is a variable you can reference within a single instance of the template.
|
||||
A _template input variable_ is a variable to reference within a single instance of the template.
|
||||
You declare a template input variable using the `let` keyword as in `let hero`.
|
||||
|
||||
There are several such variables in this example: `hero`, `i`, and `odd`.
|
||||
|
||||
The variable's scope is limited to a single instance of the repeated template.
|
||||
You can use the same variable name again in the definition of other structural directives.
|
||||
Use the same variable name again in the definition of other structural directives.
|
||||
|
||||
In contrast, you declare a template variable by prefixing the variable name with `#`, as in `#var`.
|
||||
A template variable refers to its attached element, component, or directive.
|
||||
|
||||
@@ -16,7 +16,7 @@ In the following example, the template statement `deleteHero()` appears in quote
|
||||
|
||||
When the user clicks the **Delete hero** button, Angular calls the `deleteHero()` method in the component class.
|
||||
|
||||
You can use template statements with elements, components, or directives in response to events.
|
||||
Use template statements with elements, components, or directives in response to events.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -63,7 +63,7 @@ In the preceding `deleteHero(hero)`, the `hero` is the template input variable,
|
||||
|
||||
* **Conciseness**
|
||||
|
||||
Keep template statements minimal by using method calls or basic property assignments.
|
||||
Use method calls or basic property assignments to keep template statements minimal.
|
||||
|
||||
* **Work within the context**
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Template syntax
|
||||
|
||||
In Angular, a *template* is a chunk of HTML.
|
||||
Within a template, you can use special syntax to leverage many of Angular's features.
|
||||
Use special syntax within a template to build on many of Angular's features.
|
||||
|
||||
|
||||
## Prerequisites
|
||||
@@ -20,12 +20,12 @@ In Angular, the component plays the part of the controller/viewmodel, and the te
|
||||
|
||||
<hr />
|
||||
|
||||
Each Angular template in your application is a section of HTML that you can include as a part of the page that the browser displays.
|
||||
Each Angular template in your application is a section of HTML to include as a part of the page that the browser displays.
|
||||
An Angular HTML template renders a view, or user interface, in the browser, just like regular HTML, but with a lot more functionality.
|
||||
|
||||
When you generate an Angular application with the Angular CLI, the `app.component.html` file is the default template containing placeholder HTML.
|
||||
|
||||
The template syntax guides show you how you can control the UX/UI by coordinating data between the class and the template.
|
||||
The template syntax guides show you how to control the UX/UI by coordinating data between the class and the template.
|
||||
|
||||
<div class="is-helpful alert">
|
||||
|
||||
@@ -37,12 +37,11 @@ To see all of them working together in one application, see the comprehensive <l
|
||||
|
||||
## Empower your HTML
|
||||
|
||||
With special Angular syntax in your templates, you can extend the HTML vocabulary of your applications.
|
||||
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.
|
||||
However, because an Angular template is part of an overall webpage, and not the entire page, you don't need to include elements such as `<html>`, `<body>`, or `<base>`.
|
||||
You can focus exclusively on the part of the page you are developing.
|
||||
However, because an Angular template is part of an overall webpage, and not the entire page, you don't need to include elements such as `<html>`, `<body>`, or `<base>`, and can focus exclusively on the part of the page you are developing.
|
||||
|
||||
|
||||
<div class="alert is-important">
|
||||
@@ -56,7 +55,7 @@ For more information, see the [Security](guide/security) page.
|
||||
|
||||
## More on template syntax
|
||||
|
||||
You may also be interested in the following:
|
||||
You might also be interested in the following:
|
||||
|
||||
* [Interpolation](guide/interpolation)—learn how to use interpolation and expressions in HTML.
|
||||
* [Template statements](guide/template-statements)—respond to events in your templates.
|
||||
|
||||
@@ -344,6 +344,6 @@ After you've set up your application for testing, you may find the following tes
|
||||
* [Basics of testing components](guide/testing-components-basics)—discover the basics of testing Angular components.
|
||||
* [Component testing scenarios](guide/testing-components-scenarios)—read about the various kinds of component testing scenarios and use cases.
|
||||
* [Testing attribute directives](guide/testing-attribute-directives)—learn about how to test your attribute directives.
|
||||
* [Testing pipes](guide/testing-pipes)—find out how to test attribute directives.
|
||||
* [Testing pipes](guide/testing-pipes)—find out how to test pipes.
|
||||
* [Debugging tests](guide/test-debugging)—uncover common testing bugs.
|
||||
* [Testing utility APIs](guide/testing-utility-apis)—get familiar with Angular testing features.
|
||||
|
||||
@@ -38,7 +38,7 @@ For example, if the `@Input()` property is `size`, the `@Output()` property must
|
||||
|
||||
The following `sizerComponent` has a `size` value property and a `sizeChange` event.
|
||||
The `size` property is an `@Input()`, so data can flow into the `sizerComponent`.
|
||||
The `sizeChange` event is an `@Output()`, which allows data to flow out of the `sizerComponent` to the parent component.
|
||||
The `sizeChange` event is an `@Output()`, which lets data flow out of the `sizerComponent` to the parent component.
|
||||
|
||||
Next, there are two methods, `dec()` to decrease the font size and `inc()` to increase the font size.
|
||||
These two methods use `resize()` to change the value of the `size` property within min/max value constraints, and to emit an event that conveys the new `size` value.
|
||||
@@ -75,7 +75,7 @@ Angular assigns the `$event` value to the `AppComponent.fontSizePx` when the use
|
||||
|
||||
<header>Two-way binding in forms</header>
|
||||
|
||||
Because no native HTML element follows the `x` value and `xChange` event pattern, two-way binding with form elements requires `NgModel`.
|
||||
Because no built-in HTML element follows the `x` value and `xChange` event pattern, two-way binding with form elements requires `NgModel`.
|
||||
For more information on how to use two-way binding in forms, see Angular [NgModel](guide/built-in-directives#ngModel).
|
||||
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@ Angular is a development platform, built on [TypeScript](https://www.typescriptl
|
||||
* A collection of well-integrated libraries that cover a wide variety of features, including routing, forms management, client-server communication, and more
|
||||
* A suite of developer tools to help you develop, build, test, and update your code
|
||||
|
||||
With Angular, you're taking advantage of a platform that can scale from single-developer projects to enterprise-level applications. Angular is designed to make updating as easy as possible, so you can take advantage of the latest developments with a minimum of effort. Best of all, the Angular ecosystem consists of a diverse group of over 1.7 million developers, library authors, and content creators.
|
||||
With Angular, you're taking advantage of a platform that can scale from single-developer projects to enterprise-level applications. Angular is designed to make updating as straightforward as possible, so take advantage of the latest developments with a minimum of effort. Best of all, the Angular ecosystem consists of a diverse group of over 1.7 million developers, library authors, and content creators.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -43,9 +43,9 @@ When Angular renders this component, the resulting DOM looks like this:
|
||||
|
||||
<code-example path="what-is-angular/src/app/hello-world-example.html" language="html"></code-example>
|
||||
|
||||
Angular's component model offers strong encapsulation and an intuitive application structure. Components also make your application easier to unit test and can improve the overall readability of your code.
|
||||
Angular's component model offers strong encapsulation and an intuitive application structure. Components also make your application painless to unit test and can improve the overall readability of your code.
|
||||
|
||||
For more information on what you can do with components, see the [Components](guide/component-overview) section.
|
||||
For more information on what to do with components, see the [Components](guide/component-overview) section.
|
||||
|
||||
{@a templates}
|
||||
### Templates
|
||||
@@ -74,7 +74,7 @@ Angular also supports property bindings, to help you set values for properties a
|
||||
|
||||
Notice the use of the square brackets--that syntax indicates that you're binding the property or attribute to a value in the component class.
|
||||
|
||||
You can also declare event listeners to listen for and respond to user actions such as keystrokes, mouse movements, clicks, and touches. You declare an event listener by specifying the event name in parentheses:
|
||||
Declare event listeners to listen for and respond to user actions such as keystrokes, mouse movements, clicks, and touches. You declare an event listener by specifying the event name in parentheses:
|
||||
|
||||
<code-example path="what-is-angular/src/app/hello-world-bindings/hello-world-bindings.component.html" region="event-binding"></code-example>
|
||||
|
||||
@@ -82,7 +82,7 @@ The preceding example calls a method, which is defined in the component class:
|
||||
|
||||
<code-example path="what-is-angular/src/app/hello-world-bindings/hello-world-bindings.component.ts" region="method"></code-example>
|
||||
|
||||
The following is an example of interpolation and bindings within an Angular template:
|
||||
The following is a combined example of Interpolation, Property Binding and Event Binding within an Angular template:
|
||||
|
||||
<code-tabs linenums="true">
|
||||
<code-pane
|
||||
@@ -96,7 +96,7 @@ The following is an example of interpolation and bindings within an Angular temp
|
||||
</code-pane>
|
||||
</code-tabs>
|
||||
|
||||
You can add additional functionality to your templates through the use of [directives](guide/built-in-directives). The most popular directives in Angular are `*ngIf` and `*ngFor`. You can use directives to perform a variety of tasks, such as dynamically modifying the DOM structure. And you can also create your own custom directives to create great user experiences.
|
||||
Add additional functionality to your templates through the use of [directives](guide/built-in-directives). The most popular directives in Angular are `*ngIf` and `*ngFor`. Use directives to perform a variety of tasks, such as dynamically modifying the DOM structure. And create your own custom directives to create great user experiences.
|
||||
|
||||
The following code is an example of the `*ngIf` directive.
|
||||
|
||||
@@ -112,14 +112,14 @@ The following code is an example of the `*ngIf` directive.
|
||||
</code-pane>
|
||||
</code-tabs>
|
||||
|
||||
Angular's declarative templates allow you to cleanly separate your application's logic from its presentation. Templates are based on standard HTML, so they're easy to build, maintain, and update.
|
||||
Angular's declarative templates let you cleanly separate your application's logic from its presentation. Templates are based on standard HTML, for ease in building, maintaining, and updating.
|
||||
|
||||
For more information on what you can do with templates, see the [Templates](guide/template-syntax) section.
|
||||
For more information on templates, see the [Templates](guide/template-syntax) section.
|
||||
|
||||
{@a di}
|
||||
### Dependency injection
|
||||
|
||||
Dependency injection allows you to declare the dependencies of your TypeScript classes without taking care of their instantiation. Instead, Angular handles the instantiation for you. This design pattern allows you to write more testable and flexible code. Even though understanding dependency injection is not critical to start using Angular, we strongly recommend it as a best practice and many aspects of Angular take advantage of it to some degree.
|
||||
Dependency injection lets you declare the dependencies of your TypeScript classes without taking care of their instantiation. Instead, Angular handles the instantiation for you. This design pattern lets you write more testable and flexible code. Even though understanding dependency injection is not critical to start using Angular, we strongly recommend it as a best practice and many aspects of Angular take advantage of it to some degree.
|
||||
|
||||
To illustrate how dependency injection works, consider the following example. The first file, `logger.service.ts`, defines a `Logger` class. This class contains a `writeCount` function that logs a number to the console.
|
||||
|
||||
@@ -135,7 +135,7 @@ For more information about dependency injection and Angular, see the [Dependency
|
||||
|
||||
## Angular CLI
|
||||
|
||||
The Angular CLI is the fastest, easiest, and recommended way to develop Angular applications. The Angular CLI makes a number of tasks easy. Here are some examples:
|
||||
The Angular CLI is the fastest, straightforward, and recommended way to develop Angular applications. The Angular CLI makes a number of tasks trouble-free. Here are some examples:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
@@ -167,7 +167,7 @@ For more information about the Angular CLI, see the [CLI Reference](/cli) sectio
|
||||
{@a 1p-libraries}
|
||||
## First-party libraries
|
||||
|
||||
The section, [Angular applications: The essentials](#essentials), provides a brief overview of a couple of the key architectural elements you'll use when building Angular applications. But the many benefits of Angular really become apparent when your application grows and you want to add additional functions such as site navigation or user input. That's when you can leverage the Angular platform to incorporate one of the many first-party libraries that Angular provides.
|
||||
The section, [Angular applications: The essentials](#essentials), provides a brief overview of a couple of the key architectural elements you'll use when building Angular applications. But the many benefits of Angular really become apparent when your application grows and you want to add additional functions such as site navigation or user input. Use the Angular platform to incorporate one of the many first-party libraries that Angular provides.
|
||||
|
||||
Some of the libraries available to you include:
|
||||
<table>
|
||||
@@ -196,15 +196,15 @@ Some of the libraries available to you include:
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
These libraries expand your application's functionality while also allowing you to focus more on the features that make your application unique. And you can add these libraries knowing that they're designed to integrate seamlessly into and update simultaneously with the Angular framework.
|
||||
These libraries expand your application's functionality while also letting you focus more on the features that make your application unique. Add these libraries knowing that they're designed to integrate seamlessly into and update simultaneously with the Angular framework.
|
||||
|
||||
These libraries are only required if and when they can help you add functionality to your applications or solve a particular problem.
|
||||
|
||||
## Next steps
|
||||
|
||||
This topic is intended to give you a brief overview of what Angular is, the advantages it provides, and what you can expect as you start to build your applications.
|
||||
This topic is intended to give you a brief overview of what Angular is, the advantages it provides, and what to expect as you start to build your applications.
|
||||
|
||||
To see Angular in action, see our [Getting Started](https://angular.io/start) tutorial. This tutorial uses [stackblitz.com](https://stackblitz.com/), so you can explore a working example of Angular without any installation requirements.
|
||||
To see Angular in action, see our [Getting Started](https://angular.io/start) tutorial. This tutorial uses [stackblitz.com](https://stackblitz.com/), for you to explore a working example of Angular without any installation requirements.
|
||||
|
||||
To explore Angular's capabilities further, we recommend reading through the sections, Understanding Angular and Developer Guides.
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ See [Build target](#build-target) below for more information.
|
||||
|
||||
* The `architect/test` section overrides build-option defaults for test builds and supplies additional test-running defaults for the `ng test` command.
|
||||
|
||||
* The `architect/lint` section configures defaults for options of the `ng lint` command, which performs code analysis on project source files. The default linting tool for Angular is [TSLint](https://palantir.github.io/tslint/).
|
||||
* The `architect/lint` section configures defaults for options of the `ng lint` command, which performs code analysis on project source files.
|
||||
|
||||
* The `architect/extract-i18n` section configures defaults for options of the `ng extract-i18n` command, which extracts marked message strings from source code and outputs translation files.
|
||||
|
||||
|
||||
@@ -55,13 +55,19 @@
|
||||
"Podcasts": {
|
||||
"order": 3,
|
||||
"resources": {
|
||||
"sdfjkdkfj": {
|
||||
"adventures-in-angular": {
|
||||
"desc": "Adventures in Angular is a weekly podcast dedicated to the Angular platform and related technologies, tools, languages, and practices.",
|
||||
"logo": "",
|
||||
"title": "Adventures in Angular",
|
||||
"url": "https://devchat.tv/adv-in-angular/"
|
||||
},
|
||||
"sdlkfjsldfkj": {
|
||||
"angular-show": {
|
||||
"desc": "ng-conf's official all-Angular podcast.",
|
||||
"logo": "",
|
||||
"title": "The Angular Show",
|
||||
"url": "https://www.spreaker.com/show/angular-show"
|
||||
},
|
||||
"angular-air": {
|
||||
"desc": "Weekly video podcast hosted by Jeff Whelpley with all the latest and greatest happenings in the wild world of Angular.",
|
||||
"logo": "",
|
||||
"title": "AngularAir",
|
||||
@@ -73,10 +79,10 @@
|
||||
"title": "Angular Rocks",
|
||||
"url": "https://angularrocks.com"
|
||||
},
|
||||
"sdlkfjsldfkz": {
|
||||
"happy-angular": {
|
||||
"desc": "A weekly German podcast for Angular on the go",
|
||||
"logo": "",
|
||||
"title": "Happy Angular Podcast",
|
||||
"title": "Happy Angular",
|
||||
"url": "https://happy-angular.de/"
|
||||
},
|
||||
"ngruair": {
|
||||
@@ -84,12 +90,6 @@
|
||||
"logo": "",
|
||||
"title": "NgRuAir",
|
||||
"url": "https://github.com/ngRuAir/ngruair"
|
||||
},
|
||||
"the-deep-dive": {
|
||||
"desc": "The advanced web development podcast about Angular, RxJS, TypeScript and other technologies. English, audio only.",
|
||||
"logo": "https://i.imgur.com/mmE5Feq.jpg",
|
||||
"title": "The Deep Dive",
|
||||
"url": "https://thedeepdive.simplecast.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,11 +202,6 @@
|
||||
"title": "Template reference variables",
|
||||
"tooltip": "Introductory guide to referring to DOM elements within a template."
|
||||
},
|
||||
{
|
||||
"url": "guide/inputs-outputs",
|
||||
"title": "Inputs and Outputs",
|
||||
"tooltip": "Introductory guide to sharing data between parent and child directives or components."
|
||||
},
|
||||
{
|
||||
"url": "guide/svg-in-templates",
|
||||
"title": "SVG as templates",
|
||||
|
||||
@@ -1,3 +1,36 @@
|
||||
Angular's `<ng-template>` element defines a template that doesn't render anything by default.
|
||||
Angular's `<ng-template>` element defines a template that is not rendered by default.
|
||||
|
||||
With `<ng-template>`, you can render the content manually for full control over how the content displays.
|
||||
With `<ng-template>`, you can define template content that is only being rendered by Angular when you, whether directly or indirectly, specifically instruct it to do so, allowing you to have full control over how and when the content is displayed.
|
||||
|
||||
@usageNotes
|
||||
|
||||
### Structural Directives
|
||||
|
||||
One of the main uses for `<ng-template>` is to hold template content that will be used by [Structural directives](/guide/structural-directives). Those directives can add and remove copies of the template content based on their own logic.
|
||||
|
||||
When using the [structural directive shorthand](/guide/structural-directives#structural-directive-shorthand), Angular creates an `<ng-template>` element behind the scenes.
|
||||
|
||||
### TemplateRef
|
||||
|
||||
`<ng-template>` elements are represented as instances of the `TemplateRef` class.
|
||||
|
||||
To add copies of the template to the DOM, pass this object to the `ViewContainerRef` method `createEmbeddedView()`.
|
||||
|
||||
### Template Variables
|
||||
|
||||
`<ng-template>` elements can be referenced in templates using [standard template variables](/guide/template-reference-variables#how-angular-assigns-values-to-template-variables).
|
||||
|
||||
_This is how `<ng-template>` elements are used as `ngIf` else clauses._
|
||||
|
||||
Such template variables can be used in conjunction with `ngTemplateOutlet` directives to render the content defined inside `<ng-template>` tags.
|
||||
|
||||
|
||||
### Querying
|
||||
|
||||
A [Query](/api/core/Query) (such as `ViewChild`) can find the `TemplateRef` associated to an `<ng-template>` element so that it can be used programmatically; for instance, to pass it to the `ViewContainerRef` method `createEmbeddedView()`.
|
||||
|
||||
|
||||
### Context
|
||||
Inside the `<ng-template>` tags you can reference variables present in the surrounding outer template.
|
||||
Additionally, a context object can be associated with `<ng-template>` elements.
|
||||
Such an object contains variables that can be accessed from within the template contents via template (`let` and `as`) declarations.
|
||||
@@ -219,6 +219,13 @@ The Phone XL price is over $700, so the **Notify Me** button appears on that pro
|
||||
To make the **Notify Me** button work, the child component needs to notify and pass the data to the parent component.
|
||||
The `ProductAlertsComponent` needs to emit an event when the user clicks **Notify Me** and the `ProductListComponent` needs to respond to the event.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
In new components, the Angular Generator includes an empty `constructor()`, the `OnInit` interface, and the `ngOnInit()` method.
|
||||
Since these steps don't use them, the following code examples omit them for brevity.
|
||||
|
||||
</div>
|
||||
|
||||
1. In `product-alerts.component.ts`, import `Output` and `EventEmitter` from `@angular/core`.
|
||||
|
||||
<code-example header="src/app/product-alerts/product-alerts.component.ts" path="getting-started/src/app/product-alerts/product-alerts.component.ts" region="imports"></code-example>
|
||||
@@ -228,13 +235,6 @@ The `ProductAlertsComponent` needs to emit an event when the user clicks **Notif
|
||||
|
||||
<code-example path="getting-started/src/app/product-alerts/product-alerts.component.ts" header="src/app/product-alerts/product-alerts.component.ts" region="input-output"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
In new components, the Angular Generator includes an empty `constructor()`, the `OnInit` interface, and the `ngOnInit()` method.
|
||||
Since these steps don't use them, the following code example omits them for brevity.
|
||||
|
||||
</div>
|
||||
|
||||
1. In `product-alerts.component.html`, update the **Notify Me** button with an event binding to call the `notify.emit()` method.
|
||||
|
||||
<code-example header="src/app/product-alerts/product-alerts.component.html" path="getting-started/src/app/product-alerts/product-alerts.component.html"></code-example>
|
||||
|
||||
@@ -30,6 +30,7 @@ This section walks you through adding a **Buy** button and setting up a cart ser
|
||||
|
||||
<code-example header="src/app/cart.service.ts" path="getting-started/src/app/cart.service.1.ts"></code-example>
|
||||
|
||||
1. Import the `Product` interface from `./products.js`.
|
||||
1. In the `CartService` class, define an `items` property to store the array of the current products in the cart.
|
||||
|
||||
<code-example path="getting-started/src/app/cart.service.ts" header="src/app/cart.service.ts" region="props"></code-example>
|
||||
|
||||
@@ -22,7 +22,7 @@ To set up your development environment, follow the instructions in [Local Enviro
|
||||
|
||||
## Create a new workspace and an initial application
|
||||
|
||||
You develop applications in the context of an Angular [workspace](guide/glossary#workspace). A workspace contains the files for one or more [projects](guide/glossary#project). A project is the set of files that comprise an app, a library, or end-to-end (e2e) tests. For this tutorial, you will create a new workspace.
|
||||
You develop applications in the context of an Angular [workspace](guide/glossary#workspace). A workspace contains the files for one or more [projects](guide/glossary#project). A project is the set of files that comprise an application or a library. For this tutorial, you will create a new workspace.
|
||||
|
||||
To create a new workspace and an initial application project:
|
||||
|
||||
|
||||
+20
-20
@@ -18,7 +18,7 @@
|
||||
"build-local": "yarn ~~build",
|
||||
"prebuild-local-ci": "yarn setup-local-ci",
|
||||
"build-local-ci": "yarn ~~build --progress=false",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 18bbd044d",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 0ab046223",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
||||
"test": "yarn check-env && ng test",
|
||||
"pree2e": "yarn check-env && yarn update-webdriver",
|
||||
@@ -39,7 +39,7 @@
|
||||
"test-a11y-score-localhost": "run-p --race \"~~light-server -s dist -p 4200 --quiet\" \"test-a11y-score http://localhost:4200\" --",
|
||||
"test-pwa-score": "run-s \"~~audit-web-app {1} all:0,pwa:{2} {3}\" --",
|
||||
"test-pwa-score-localhost": "run-p --race \"~~light-server -s dist -p 4200 --quiet\" \"test-pwa-score http://localhost:4200 {1} {2}\" --",
|
||||
"example-e2e": "yarn example-check-local && node ./tools/examples/run-example-e2e",
|
||||
"example-e2e": "yarn example-check-local && node ./tools/examples/run-example-e2e.mjs",
|
||||
"example-lint": "tslint --config \"content/examples/tslint.json\" \"content/examples/**/*.ts\" --exclude \"content/examples/styleguide/**/*.avoid.ts\"",
|
||||
"example-use-local": "node tools/ng-packages-installer overwrite ./tools/examples/shared --debug --force",
|
||||
"example-use-npm": "node tools/ng-packages-installer restore ./tools/examples/shared",
|
||||
@@ -63,8 +63,8 @@
|
||||
"boilerplate:add": "node ./tools/examples/example-boilerplate add",
|
||||
"boilerplate:remove": "node ./tools/examples/example-boilerplate remove",
|
||||
"boilerplate:test": "node tools/examples/test.js",
|
||||
"generate-stackblitz": "node ./tools/stackblitz-builder/generateStackblitz",
|
||||
"generate-zips": "node ./tools/example-zipper/generateZips",
|
||||
"generate-stackblitz": "node ./tools/stackblitz-builder/generateStackblitz.mjs",
|
||||
"generate-zips": "node ./tools/example-zipper/generateZips.mjs",
|
||||
"create-example": "node ./tools/examples/create-example.js",
|
||||
"build-404-page": "node scripts/build-404-page",
|
||||
"update-webdriver": "node ../scripts/webdriver-manager-update.js",
|
||||
@@ -86,27 +86,27 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "12.0.5",
|
||||
"@angular/cdk": "12.0.5",
|
||||
"@angular/common": "12.0.5",
|
||||
"@angular/compiler": "12.0.5",
|
||||
"@angular/core": "12.0.5",
|
||||
"@angular/elements": "12.0.5",
|
||||
"@angular/forms": "12.0.5",
|
||||
"@angular/material": "12.0.5",
|
||||
"@angular/platform-browser": "12.0.5",
|
||||
"@angular/platform-browser-dynamic": "12.0.5",
|
||||
"@angular/router": "12.0.5",
|
||||
"@angular/service-worker": "12.0.5",
|
||||
"@angular/animations": "12.1.1",
|
||||
"@angular/cdk": "12.1.1",
|
||||
"@angular/common": "12.1.1",
|
||||
"@angular/compiler": "12.1.1",
|
||||
"@angular/core": "12.1.1",
|
||||
"@angular/elements": "12.1.1",
|
||||
"@angular/forms": "12.1.1",
|
||||
"@angular/material": "12.1.1",
|
||||
"@angular/platform-browser": "12.1.1",
|
||||
"@angular/platform-browser-dynamic": "12.1.1",
|
||||
"@angular/router": "12.1.1",
|
||||
"@angular/service-worker": "12.1.1",
|
||||
"@webcomponents/custom-elements": "1.4.3",
|
||||
"rxjs": "^6.6.7",
|
||||
"tslib": "^2.2.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "12.0.4",
|
||||
"@angular/cli": "12.0.4",
|
||||
"@angular/compiler-cli": "12.0.5",
|
||||
"@angular-devkit/build-angular": "12.1.1",
|
||||
"@angular/cli": "12.1.1",
|
||||
"@angular/compiler-cli": "12.1.1",
|
||||
"@swc/cli": "^0.1.36",
|
||||
"@swc/core": "^1.2.51",
|
||||
"@types/jasmine": "~3.7.0",
|
||||
@@ -132,7 +132,7 @@
|
||||
"find-free-port": "^2.0.0",
|
||||
"firebase-tools": "^9.8.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"globby": "^11.0.3",
|
||||
"globby": "^12.0.0",
|
||||
"hast-util-is-element": "^1.1.0",
|
||||
"hast-util-to-string": "^1.0.4",
|
||||
"html": "^1.0.0",
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="content">
|
||||
<div class="nf-container l-flex-wrap flex-center">
|
||||
<img src="assets/images/support/angular-404.svg" width="300" height="300" />
|
||||
<div class="nf-response l-flex-wrap">
|
||||
<div class="nf-response l-flex-wrap center">
|
||||
<h1 class="no-toc" id="page-not-found">Resource Not Found</h1>
|
||||
<p>We're sorry. The resource you are looking for cannot be found.</p>
|
||||
</div>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<mat-sidenav-container class="sidenav-container" [class.starting]="isStarting" [class.has-floating-toc]="hasFloatingToc" role="main">
|
||||
|
||||
<mat-sidenav [ngClass]="{'collapsed': !dockSideNav}" #sidenav class="sidenav" [mode]="mode" [opened]="isOpened" (openedChange)="updateHostClasses()">
|
||||
<aio-nav-menu *ngIf="!showTopMenu" [nodes]="topMenuNarrowNodes" [currentNode]="currentNodes?.TopBarNarrow" [isWide]="false"></aio-nav-menu>
|
||||
<aio-nav-menu *ngIf="!showTopMenu" [nodes]="topMenuNarrowNodes" [currentNode]="currentNodes?.TopBarNarrow" [isWide]="dockSideNav"></aio-nav-menu>
|
||||
<aio-nav-menu [nodes]="sideNavNodes" [currentNode]="currentNodes?.SideNav" [isWide]="dockSideNav"></aio-nav-menu>
|
||||
|
||||
<div class="doc-version">
|
||||
@@ -61,11 +61,11 @@
|
||||
<aio-dt *ngIf="dtOn" [(doc)]="currentDocument"></aio-dt>
|
||||
</main>
|
||||
|
||||
</mat-sidenav-container>
|
||||
<div *ngIf="hasFloatingToc" class="toc-container no-print" [style.max-height.px]="tocMaxHeight" (wheel)="restrainScrolling($event)">
|
||||
<aio-lazy-ce selector="aio-toc"></aio-lazy-ce>
|
||||
</div>
|
||||
|
||||
<div *ngIf="hasFloatingToc" class="toc-container no-print" [style.max-height.px]="tocMaxHeight" (wheel)="restrainScrolling($event)">
|
||||
<aio-lazy-ce selector="aio-toc"></aio-lazy-ce>
|
||||
</div>
|
||||
</mat-sidenav-container>
|
||||
|
||||
<footer class="no-print">
|
||||
<aio-footer [nodes]="footerNodes" [versionInfo]="versionInfo"></aio-footer>
|
||||
|
||||
@@ -14,7 +14,7 @@ import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||
import { first, map } from 'rxjs/operators';
|
||||
|
||||
const sideNavView = 'SideNav';
|
||||
export const showTopMenuWidth = 1048;
|
||||
export const showTopMenuWidth = 1150;
|
||||
export const dockSideNavWidth = 992;
|
||||
export const showFloatingTocWidth = 800;
|
||||
|
||||
@@ -79,7 +79,7 @@ export class AppComponent implements OnInit {
|
||||
private currentUrl: string;
|
||||
|
||||
get isOpened() { return this.dockSideNav && this.isSideNavDoc; }
|
||||
get mode() { return this.dockSideNav && (this.isSideNavDoc || this.showTopMenu) ? 'side' : 'over'; }
|
||||
get mode() { return this.isOpened ? 'side' : 'over'; }
|
||||
|
||||
// Search related properties
|
||||
showSearchResults = false;
|
||||
|
||||
@@ -30,7 +30,7 @@ export class ApiService implements OnDestroy {
|
||||
private apiBase = DOC_CONTENT_URL_PREFIX + 'api/';
|
||||
private apiListJsonDefault = 'api-list.json';
|
||||
private firstTime = true;
|
||||
private onDestroy = new Subject();
|
||||
private onDestroy = new Subject<void>();
|
||||
private sectionsSubject = new ReplaySubject<ApiSection[]>(1);
|
||||
private _sections = this.sectionsSubject.pipe(takeUntil(this.onDestroy));
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ import { SearchService } from 'app/search/search.service';
|
||||
@Component({
|
||||
selector: 'aio-file-not-found-search',
|
||||
template:
|
||||
`<p>Let's see if any of these search results help...</p>
|
||||
`<div class="alert is-helpful">
|
||||
<p>Let's see if any of these search results help...</p>
|
||||
</div>
|
||||
<aio-search-results class="embedded" [searchResults]="searchResults | async"></aio-search-results>`
|
||||
})
|
||||
export class FileNotFoundSearchComponent implements OnInit {
|
||||
|
||||
@@ -19,7 +19,7 @@ export class TocComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
isCollapsed = true;
|
||||
isEmbedded = false;
|
||||
@ViewChildren('tocItem') private items: QueryList<ElementRef>;
|
||||
private onDestroy = new Subject();
|
||||
private onDestroy = new Subject<void>();
|
||||
primaryMax = 4;
|
||||
tocList: TocItem[];
|
||||
|
||||
|
||||
@@ -70,13 +70,16 @@ describe('DocumentService', () => {
|
||||
it('should encode the request path to be case-insensitive', () => {
|
||||
const { docService, locationService } = getServices('initial/Doc');
|
||||
docService.currentDocument.subscribe();
|
||||
httpMock.expectOne(CONTENT_URL_PREFIX + 'initial/d_oc.json').flush({});
|
||||
locationService.go('NEW/Doc');
|
||||
httpMock.expectOne(CONTENT_URL_PREFIX + 'n_e_w_/d_oc.json').flush({});
|
||||
locationService.go('doc_with_underscores');
|
||||
httpMock.expectOne(CONTENT_URL_PREFIX + 'doc__with__underscores.json').flush({});
|
||||
locationService.go('DOC_WITH_UNDERSCORES');
|
||||
httpMock.expectOne(CONTENT_URL_PREFIX + 'd_o_c___w_i_t_h___u_n_d_e_r_s_c_o_r_e_s_.json').flush({});
|
||||
|
||||
expect(() => {
|
||||
httpMock.expectOne(CONTENT_URL_PREFIX + 'initial/d_oc.json').flush({});
|
||||
locationService.go('NEW/Doc');
|
||||
httpMock.expectOne(CONTENT_URL_PREFIX + 'n_e_w_/d_oc.json').flush({});
|
||||
locationService.go('doc_with_underscores');
|
||||
httpMock.expectOne(CONTENT_URL_PREFIX + 'doc__with__underscores.json').flush({});
|
||||
locationService.go('DOC_WITH_UNDERSCORES');
|
||||
httpMock.expectOne(CONTENT_URL_PREFIX + 'd_o_c___w_i_t_h___u_n_d_e_r_s_c_o_r_e_s_.json').flush({});
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should emit the not-found document if the document is not found on the server', () => {
|
||||
|
||||
@@ -18,10 +18,11 @@ export const DOC_CONTENT_URL_PREFIX = CONTENT_URL_PREFIX + 'docs/';
|
||||
const FETCHING_ERROR_CONTENTS = (path: string) => `
|
||||
<div class="nf-container l-flex-wrap flex-center">
|
||||
<div class="nf-icon material-icons">error_outline</div>
|
||||
<div class="nf-response l-flex-wrap">
|
||||
<h1 class="no-toc">Request for document failed.</h1>
|
||||
<div class="nf-response l-flex-wrap center">
|
||||
<h1 class="no-toc">Request for document failed</h1>
|
||||
<p>
|
||||
We are unable to retrieve the "${path}" page at this time.
|
||||
<br/>
|
||||
Please check your connection and try again later.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -260,10 +260,13 @@ export class DocViewerComponent implements OnDestroy {
|
||||
* (See https://github.com/angular/angular/issues/28114.)
|
||||
*/
|
||||
async function printSwDebugInfo(): Promise<void> {
|
||||
console.log(`\nServiceWorker: ${navigator.serviceWorker?.controller?.state ?? 'N/A'}`);
|
||||
const sep = '\n----------';
|
||||
const swState = navigator.serviceWorker?.controller?.state ?? 'N/A';
|
||||
|
||||
console.log(`\nServiceWorker: ${swState}`);
|
||||
|
||||
if (typeof caches === 'undefined') {
|
||||
console.log('\nCaches: N/A');
|
||||
console.log(`${sep}\nCaches: N/A`);
|
||||
} else {
|
||||
const allCacheNames = await caches.keys();
|
||||
const swCacheNames = allCacheNames.filter(name => name.startsWith('ngsw:/:'));
|
||||
@@ -273,11 +276,28 @@ async function printSwDebugInfo(): Promise<void> {
|
||||
await findCachesAndPrintEntries(swCacheNames, 'assets:app-shell:meta', true);
|
||||
}
|
||||
|
||||
if (swState === 'activated') {
|
||||
console.log(sep);
|
||||
await fetchAndPrintSwInternalDebugInfo();
|
||||
}
|
||||
|
||||
console.warn(
|
||||
'\nIf you see this error, please report an issue at ' +
|
||||
`${sep}\nIf you see this error, please report an issue at ` +
|
||||
'https://github.com/angular/angular/issues/new?template=3-docs-bug.md including the above logs.');
|
||||
|
||||
// Internal helpers
|
||||
async function fetchAndPrintSwInternalDebugInfo() {
|
||||
try {
|
||||
const res = await fetch('/ngsw/state');
|
||||
if (!res.ok) {
|
||||
throw new Error(`Response ${res.status} ${res.statusText}`);
|
||||
}
|
||||
console.log(await res.text());
|
||||
} catch (err) {
|
||||
console.log(`Failed to retrieve debug info from '/ngsw/state': ${err.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function findCachesAndPrintEntries(
|
||||
swCacheNames: string[], nameSuffix: string, includeValues: boolean,
|
||||
ignoredKeys: string[] = []): Promise<void> {
|
||||
|
||||
@@ -23,7 +23,7 @@ export class NotificationComponent implements OnInit {
|
||||
@Input() dismissOnContentClick: boolean;
|
||||
@Input() notificationId: string;
|
||||
@Input() expirationDate: string;
|
||||
@Output() dismissed = new EventEmitter();
|
||||
@Output() dismissed = new EventEmitter<void>();
|
||||
|
||||
@HostBinding('@hideAnimation')
|
||||
showNotification: 'show'|'hide';
|
||||
|
||||
@@ -56,10 +56,40 @@ describe('ReportingErrorHandler service', () => {
|
||||
expect(onerrorSpy).toHaveBeenCalledWith(error.message, undefined, undefined, undefined, error);
|
||||
});
|
||||
|
||||
it('should send a non-error object to window.onerror', () => {
|
||||
const error = {reason: 'this is an error message'};
|
||||
handler.handleError(error);
|
||||
expect(onerrorSpy).toHaveBeenCalledWith(JSON.stringify(error));
|
||||
});
|
||||
|
||||
it('should send a non-error object with circular references to window.onerror', () => {
|
||||
const error = {
|
||||
reason: 'this is an error message',
|
||||
get self() { return this; },
|
||||
toString() { return `{reason: ${this.reason}}`; },
|
||||
};
|
||||
handler.handleError(error);
|
||||
expect(onerrorSpy).toHaveBeenCalledWith('{reason: this is an error message}');
|
||||
});
|
||||
|
||||
it('should send an error string to window.onerror', () => {
|
||||
const error = 'this is an error message';
|
||||
handler.handleError(error);
|
||||
expect(onerrorSpy).toHaveBeenCalledWith(error);
|
||||
});
|
||||
|
||||
it('should send a non-object, non-string error stringified to window.onerror', () => {
|
||||
handler.handleError(404);
|
||||
handler.handleError(false);
|
||||
handler.handleError(null);
|
||||
handler.handleError(undefined);
|
||||
|
||||
expect(onerrorSpy.calls.allArgs()).toEqual([
|
||||
['404'],
|
||||
['false'],
|
||||
['null'],
|
||||
['undefined'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,8 +17,7 @@ export class ReportingErrorHandler extends ErrorHandler {
|
||||
* Send error info to Google Analytics, in addition to the default handling.
|
||||
* @param error Information about the error.
|
||||
*/
|
||||
handleError(error: string | Error) {
|
||||
|
||||
handleError(error: any) {
|
||||
try {
|
||||
super.handleError(error);
|
||||
} catch (e) {
|
||||
@@ -27,12 +26,19 @@ export class ReportingErrorHandler extends ErrorHandler {
|
||||
this.reportError(error);
|
||||
}
|
||||
|
||||
private reportError(error: string | Error) {
|
||||
private reportError(error: unknown) {
|
||||
if (this.window.onerror) {
|
||||
if (typeof error === 'string') {
|
||||
this.window.onerror(error);
|
||||
} else {
|
||||
if (error instanceof Error) {
|
||||
this.window.onerror(error.message, undefined, undefined, undefined, error);
|
||||
} else {
|
||||
if (typeof error === 'object') {
|
||||
try {
|
||||
error = JSON.stringify(error);
|
||||
} catch {
|
||||
// Ignore the error and just let it be stringified.
|
||||
}
|
||||
}
|
||||
this.window.onerror(`${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ export class ScrollSpiedElementGroup {
|
||||
@Injectable()
|
||||
export class ScrollSpyService {
|
||||
private spiedElementGroups: ScrollSpiedElementGroup[] = [];
|
||||
private onStopListening = new Subject();
|
||||
private onStopListening = new Subject<void>();
|
||||
private resizeEvents = fromEvent(window, 'resize').pipe(auditTime(300), takeUntil(this.onStopListening));
|
||||
private scrollEvents = fromEvent(window, 'scroll').pipe(auditTime(10), takeUntil(this.onStopListening));
|
||||
private lastContentHeight: number;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApplicationRef, Injector } from '@angular/core';
|
||||
import { ApplicationRef, ErrorHandler, Injector } from '@angular/core';
|
||||
import { discardPeriodicTasks, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { SwUpdate } from '@angular/service-worker';
|
||||
import { Subject } from 'rxjs';
|
||||
@@ -13,6 +13,7 @@ import { SwUpdatesService } from './sw-updates.service';
|
||||
describe('SwUpdatesService', () => {
|
||||
let injector: Injector;
|
||||
let appRef: MockApplicationRef;
|
||||
let errorHandler: ErrorHandler;
|
||||
let location: MockLocationService;
|
||||
let service: SwUpdatesService;
|
||||
let swu: MockSwUpdate;
|
||||
@@ -27,13 +28,15 @@ describe('SwUpdatesService', () => {
|
||||
const setup = (isSwUpdateEnabled: boolean) => {
|
||||
injector = Injector.create({providers: [
|
||||
{ provide: ApplicationRef, useClass: MockApplicationRef, deps: [] },
|
||||
{ provide: ErrorHandler, useValue: {handleError: jasmine.createSpy('handlerError')} },
|
||||
{ provide: LocationService, useFactory: () => new MockLocationService(''), deps: [] },
|
||||
{ provide: Logger, useClass: MockLogger, deps: [] },
|
||||
{ provide: SwUpdate, useFactory: () => new MockSwUpdate(isSwUpdateEnabled), deps: [] },
|
||||
{ provide: SwUpdatesService, deps: [ApplicationRef, LocationService, Logger, SwUpdate] }
|
||||
{ provide: SwUpdatesService, deps: [ApplicationRef, ErrorHandler, LocationService, Logger, SwUpdate] }
|
||||
]});
|
||||
|
||||
appRef = injector.get(ApplicationRef) as unknown as MockApplicationRef;
|
||||
errorHandler = injector.get(ErrorHandler);
|
||||
location = injector.get(LocationService) as unknown as MockLocationService;
|
||||
service = injector.get(SwUpdatesService);
|
||||
swu = injector.get(SwUpdate) as unknown as MockSwUpdate;
|
||||
@@ -129,6 +132,24 @@ describe('SwUpdatesService', () => {
|
||||
expect(location.reloadPage).toHaveBeenCalledTimes(2);
|
||||
}));
|
||||
|
||||
it('should notify the `ErrorHandler` when an unrecoverable state has been detected', run(() => {
|
||||
expect(errorHandler.handleError).not.toHaveBeenCalled();
|
||||
|
||||
swu.$$unrecoverableSubj.next({reason: 'Something bad happened'});
|
||||
expect(errorHandler.handleError).toHaveBeenCalledBefore(location.reloadPage);
|
||||
expect(errorHandler.handleError)
|
||||
.toHaveBeenCalledWith('Unrecoverable state: Something bad happened');
|
||||
|
||||
(errorHandler.handleError as jasmine.Spy).calls.reset();
|
||||
location.reloadPage.calls.reset();
|
||||
|
||||
swu.$$unrecoverableSubj.next({reason: 'Something worse happened'});
|
||||
expect(errorHandler.handleError).toHaveBeenCalledBefore(location.reloadPage);
|
||||
expect(errorHandler.handleError)
|
||||
.toHaveBeenCalledWith('Unrecoverable state: Something worse happened');
|
||||
|
||||
}));
|
||||
|
||||
describe('when `SwUpdate` is not enabled', () => {
|
||||
const runDeactivated = (specFn: VoidFunction) => run(specFn, false);
|
||||
|
||||
@@ -165,6 +186,7 @@ describe('SwUpdatesService', () => {
|
||||
swu.$$unrecoverableSubj.next({reason: 'Something bad happened'});
|
||||
swu.$$unrecoverableSubj.next({reason: 'Something worse happened'});
|
||||
|
||||
expect(errorHandler.handleError).not.toHaveBeenCalled();
|
||||
expect(location.reloadPage).not.toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
@@ -221,12 +243,15 @@ describe('SwUpdatesService', () => {
|
||||
|
||||
it('should stop requesting page reloads when unrecoverable states are detected', run(() => {
|
||||
swu.$$unrecoverableSubj.next({reason: 'Something bad happened'});
|
||||
expect(errorHandler.handleError).toHaveBeenCalledTimes(1);
|
||||
expect(location.reloadPage).toHaveBeenCalledTimes(1);
|
||||
|
||||
service.ngOnDestroy();
|
||||
(errorHandler.handleError as jasmine.Spy).calls.reset();
|
||||
location.reloadPage.calls.reset();
|
||||
|
||||
swu.$$unrecoverableSubj.next({reason: 'Something worse happened'});
|
||||
expect(errorHandler.handleError).not.toHaveBeenCalled();
|
||||
expect(location.reloadPage).not.toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApplicationRef, Injectable, OnDestroy } from '@angular/core';
|
||||
import { ApplicationRef, ErrorHandler, Injectable, OnDestroy } from '@angular/core';
|
||||
import { SwUpdate } from '@angular/service-worker';
|
||||
import { concat, interval, Subject } from 'rxjs';
|
||||
import { first, takeUntil, tap } from 'rxjs/operators';
|
||||
@@ -21,8 +21,8 @@ export class SwUpdatesService implements OnDestroy {
|
||||
private onDestroy = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
appRef: ApplicationRef, location: LocationService, private logger: Logger,
|
||||
private swu: SwUpdate) {
|
||||
appRef: ApplicationRef, errorHandler: ErrorHandler, location: LocationService,
|
||||
private logger: Logger, private swu: SwUpdate) {
|
||||
if (!swu.isEnabled) {
|
||||
return;
|
||||
}
|
||||
@@ -55,7 +55,11 @@ export class SwUpdatesService implements OnDestroy {
|
||||
// Request an immediate page reload once an unrecoverable state has been detected.
|
||||
this.swu.unrecoverable
|
||||
.pipe(
|
||||
tap(evt => this.log(`Unrecoverable state: ${evt.reason}\nReloading...`)),
|
||||
tap(evt => {
|
||||
const errorMsg = `Unrecoverable state: ${evt.reason}`;
|
||||
errorHandler.handleError(errorMsg);
|
||||
this.log(`${errorMsg}\nReloading...`);
|
||||
}),
|
||||
takeUntil(this.onDestroy),
|
||||
)
|
||||
.subscribe(() => location.reloadPage());
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@
|
||||
function formatError(msg, url, line, col, e) {
|
||||
var stack;
|
||||
msg = msg.replace(/^Error: /, '');
|
||||
if (e) {
|
||||
if (e && e.stack) {
|
||||
stack = e.stack
|
||||
// strip the leading "Error: " from the stack trace
|
||||
.replace(/^Error: /, '')
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
mat-sidenav-container.sidenav-container {
|
||||
min-height: 100%;
|
||||
height: auto !important;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
transform: none;
|
||||
|
||||
&.has-floating-toc {
|
||||
max-width: 82%;
|
||||
.mat-sidenav-content {
|
||||
margin-right: 18vw;
|
||||
}
|
||||
}
|
||||
|
||||
.sidenav-content {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@use '../../mixins';
|
||||
|
||||
// VARIABLES
|
||||
$showTopMenuWidth: 1048px;
|
||||
$showTopMenuWidth: 1150px;
|
||||
$hideTopMenuWidth: $showTopMenuWidth - 1;
|
||||
$hamburgerShownMargin: 0 8px 0 0;
|
||||
$hamburgerHiddenMargin: 0 16px 0 -64px;
|
||||
@@ -132,7 +132,10 @@ mat-toolbar.app-toolbar {
|
||||
|
||||
.nav-link-inner {
|
||||
border-radius: 4px;
|
||||
font-size: 1.3rem;
|
||||
font-size: clamp(10px, 1.6rem, 20px);
|
||||
padding: 8px 16px;
|
||||
padding: 8px clamp(5px, 0.7vw, 16px);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@@ -183,7 +186,7 @@ mat-toolbar.app-toolbar {
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
@media (max-width: 515px) {
|
||||
width: 150px;
|
||||
}
|
||||
}
|
||||
@@ -200,11 +203,7 @@ mat-toolbar.app-toolbar {
|
||||
.toolbar-external-icons-container {
|
||||
a {
|
||||
@media screen and (max-width: 480px) {
|
||||
margin: 0;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: -8px;
|
||||
}
|
||||
margin: 0 -8px 0 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 420px) {
|
||||
|
||||
@@ -1,254 +0,0 @@
|
||||
@use 'sass:color';
|
||||
@use '../constants';
|
||||
@use '../mixins';
|
||||
|
||||
code-example,
|
||||
code-tabs {
|
||||
clear: both;
|
||||
display: block;
|
||||
|
||||
code {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.mat-card {
|
||||
padding: 0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
code-example {
|
||||
&:not(.no-box) {
|
||||
background-color: rgba($backgroundgray, 0.2);
|
||||
border: 0.5px solid $lightgray;
|
||||
border-radius: 5px;
|
||||
color: $darkgray;
|
||||
margin: 16px auto;
|
||||
}
|
||||
|
||||
&.no-box {
|
||||
pre.prettyprint {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
// TERMINAL / SHELL TEXT STYLES
|
||||
&[language=sh], &[language=bash] {
|
||||
background-color: $darkgray;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: $accentblue;
|
||||
border-radius: 5px 5px 0 0;
|
||||
color: $offwhite;
|
||||
@include mixins.font-size(16);
|
||||
padding: 8px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
code-tabs {
|
||||
margin: 16px 0;
|
||||
|
||||
.code-tab-group {
|
||||
.mat-tab-label {
|
||||
&:hover {
|
||||
background: rgba(black, 0.04);
|
||||
}
|
||||
}
|
||||
|
||||
.mat-tab-body {
|
||||
overflow-y: hidden;
|
||||
|
||||
.mat-tab-body-content {
|
||||
height: auto;
|
||||
transform: none;
|
||||
|
||||
.fadeIn {
|
||||
animation: opacity 2s ease-in;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
code-example.avoid header,
|
||||
code-example.avoidFile header {
|
||||
border: 2px solid $anti-pattern;
|
||||
background: $anti-pattern;
|
||||
}
|
||||
|
||||
code-example.avoid,
|
||||
code-example.avoidFile,
|
||||
code-tabs.avoid mat-tab-body,
|
||||
code-tabs.avoidFile mat-tab-body {
|
||||
border: 0.5px solid $anti-pattern;
|
||||
}
|
||||
|
||||
aio-code {
|
||||
pre.prettyprint {
|
||||
position: relative;
|
||||
display: flex;
|
||||
min-height: 32px;
|
||||
margin: 16px 24px;
|
||||
white-space: pre-wrap;
|
||||
align-items: center;
|
||||
|
||||
code {
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
span {
|
||||
@include mixins.line-height(24);
|
||||
}
|
||||
|
||||
ol.linenums {
|
||||
margin: 0;
|
||||
color: color.adjust($mediumgray, $lightness: 25%);
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
font-family: $code-font;
|
||||
font-size: 90%;
|
||||
@include mixins.line-height(24);
|
||||
|
||||
&::marker {
|
||||
color: color.adjust($mediumgray, $lightness: 25%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.code-missing {
|
||||
color: $darkred;
|
||||
}
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
right: -19px;
|
||||
padding: 0;
|
||||
overflow: visible; // This is required for the button to be displayed correctly in IE11.
|
||||
|
||||
color: $blue-grey-200;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: $mediumgray;
|
||||
}
|
||||
}
|
||||
|
||||
&.lang-sh, &.lang-bash {
|
||||
.copy-button {
|
||||
color: $mediumgray;
|
||||
|
||||
&:hover {
|
||||
color: $lightgray;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidenav-content {
|
||||
code {
|
||||
a {
|
||||
color: $darkblue;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not(pre) {
|
||||
> code {
|
||||
background-color: rgba($lightgray, 0.3);
|
||||
border-radius: 4px;
|
||||
color: $deepgray;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
&:not(a) > code {
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.page-guide-cheatsheet & {
|
||||
td:first-of-type,
|
||||
th {
|
||||
code {
|
||||
background-color: inherit;
|
||||
padding: 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.code-anchor {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
font-size: inherit;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* PRETTY PRINTING STYLES for prettify.js. */
|
||||
|
||||
/* The following class|color styles are derived from https://github.com/google/code-prettify/blob/master/src/prettify.css*/
|
||||
|
||||
/* SPAN elements with the classes below are added by prettyprint. */
|
||||
.pln { color: #000 } /* plain text */
|
||||
|
||||
@media screen {
|
||||
.str { color: #800 } /* string content */
|
||||
.kwd { color: #00f } /* a keyword */
|
||||
.com { color: #060 } /* a comment */
|
||||
.typ { color: red } /* a type name */
|
||||
.lit { color: #08c } /* a literal value */
|
||||
/* punctuation, lisp open bracket, lisp close bracket */
|
||||
.pun, .opn, .clo { color: #660 }
|
||||
.tag { color: #008 } /* a markup tag name */
|
||||
.atn { color: #606 } /* a markup attribute name */
|
||||
.atv { color: #800 } /* a markup attribute value */
|
||||
.dec, .var { color: #606 } /* a declaration; a variable name */
|
||||
.fun { color: red } /* a function name */
|
||||
}
|
||||
|
||||
/* Use higher contrast and text-weight for printable form. */
|
||||
@media print, projection {
|
||||
.str { color: #060 }
|
||||
.kwd { color: #006; font-weight: bold }
|
||||
.com { color: #600; font-style: italic }
|
||||
.typ { color: #404; font-weight: bold }
|
||||
.lit { color: #044 }
|
||||
.pun, .opn, .clo { color: #440 }
|
||||
.tag { color: #006; font-weight: bold }
|
||||
.atn { color: #404 }
|
||||
.atv { color: #060 }
|
||||
}
|
||||
|
||||
/* SHELL / TERMINAL CODE BLOCKS */
|
||||
|
||||
code-example {
|
||||
&[language=sh], &[language=bash] {
|
||||
.pnk, .blk, .pln, .otl, .kwd, .typ, .tag, .str, .atv, .atn, .com, .lit, .pun, .dec {
|
||||
color: $codegreen;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,53 +10,23 @@
|
||||
details.overloads {
|
||||
box-shadow: none;
|
||||
|
||||
.icon-action-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
> summary {
|
||||
justify-content: space-between;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-contents {
|
||||
padding: 0;
|
||||
border-radius: 2px;
|
||||
box-shadow: none;
|
||||
|
||||
> *:not(hr) {
|
||||
margin: 16px 24px;
|
||||
}
|
||||
}
|
||||
|
||||
summary {
|
||||
height: inherit;
|
||||
padding: 0;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
clear: left;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
@include mixins.font-size(14);
|
||||
}
|
||||
.show-all {
|
||||
display: initial;
|
||||
}
|
||||
.collapse-all {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&[open] > summary {
|
||||
.show-all {
|
||||
display: none;
|
||||
}
|
||||
.collapse-all {
|
||||
display: initial;
|
||||
> .details-content {
|
||||
padding: 0;
|
||||
border-radius: 2px;
|
||||
box-shadow: none;
|
||||
|
||||
> *:not(hr) {
|
||||
margin: 16px 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.cli-name {
|
||||
font-weight: bold;
|
||||
|
||||
.kwd { color: initial } /* override code format */
|
||||
.kwd { color: inherit; } /* override code format */
|
||||
}
|
||||
|
||||
.cli-option-syntax {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
}
|
||||
|
||||
// TERMINAL / SHELL TEXT STYLES
|
||||
&.code-shell,
|
||||
&[language="sh"],
|
||||
&[language="bash"] {
|
||||
background-color: constants.$darkgray;
|
||||
@@ -96,7 +95,7 @@
|
||||
|
||||
:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not(pre) {
|
||||
> code {
|
||||
background-color: if($is-dark-theme, constants.$darkgray, rgba(constants.$lightgray, 0.5));
|
||||
background-color: if($is-dark-theme, constants.$darkgray, rgba(constants.$lightgray, 0.3));
|
||||
color: if($is-dark-theme, constants.$white, constants.$darkgray);
|
||||
}
|
||||
}
|
||||
@@ -200,7 +199,6 @@
|
||||
/* SHELL / TERMINAL CODE BLOCKS */
|
||||
|
||||
code-example {
|
||||
&.code-shell,
|
||||
&[language="sh"],
|
||||
&[language="bash"] {
|
||||
.pnk,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@use 'sass:math';
|
||||
@use '~@angular/cdk' as cdk;
|
||||
@use '~@angular/material' as mat;
|
||||
|
||||
@@ -12,13 +13,13 @@ aio-cookies-popup {
|
||||
position: fixed;
|
||||
margin: 24px;
|
||||
max-width: 430px;
|
||||
padding: $inner-spacing $inner-spacing $inner-spacing / 2;
|
||||
padding: $inner-spacing $inner-spacing math.div($inner-spacing, 2);
|
||||
z-index: cdk.$overlay-container-z-index + 1;
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin: $inner-spacing $inner-spacing / -2 0 0;
|
||||
margin: $inner-spacing math.div($inner-spacing, -2) 0 0;
|
||||
|
||||
.mat-button {
|
||||
text-transform: uppercase;
|
||||
|
||||
@@ -3,9 +3,5 @@
|
||||
@mixin theme($theme) {
|
||||
details {
|
||||
box-shadow: 0 1px 4px 0 rgba(constants.$black, 0.37);
|
||||
|
||||
> summary {
|
||||
color: constants.$black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,19 @@
|
||||
* </div>
|
||||
* </details>
|
||||
* ```
|
||||
*
|
||||
* Optionally, you can use an `.actions` container inside `<summary>` to show expand/collapse
|
||||
* actions and/or a rotating icon:
|
||||
* ```html
|
||||
* <summary>
|
||||
* <span>Some title</span>
|
||||
* <span class="actions">
|
||||
* <span class="action-expand">Show more</span>
|
||||
* <span class="action-collapse">Show less</span>
|
||||
* <i class="material-icons expand">expand_more</i>
|
||||
* </span>
|
||||
* </summary>
|
||||
* ```
|
||||
*/
|
||||
|
||||
details {
|
||||
@@ -21,33 +34,57 @@ details {
|
||||
cursor: pointer;
|
||||
@include mixins.font-size(16);
|
||||
position: relative;
|
||||
padding: 16px 24px;
|
||||
height: 16px;
|
||||
display: block; // Remove the built in details marker in FF
|
||||
padding: 16px;
|
||||
height: inherit;
|
||||
display: flex; // Remove the built in details marker in FF
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
&::-webkit-details-marker {
|
||||
display: none; // Remove the built in details marker in webkit
|
||||
}
|
||||
|
||||
// Rotate the icon
|
||||
i.material-icons.expand {
|
||||
@include mixins.rotate(0deg);
|
||||
|
||||
@at-root #{selector.replace(&, 'details', 'details[open]')} {
|
||||
@include mixins.rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
> h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.actions {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@include mixins.font-size(14);
|
||||
|
||||
// Show/hide expand and collapse actions.
|
||||
.action-expand {
|
||||
display: initial;
|
||||
|
||||
@at-root #{selector.replace(&, 'details', 'details[open]')} {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.action-collapse {
|
||||
display: none;
|
||||
|
||||
@at-root #{selector.replace(&, 'details', 'details[open]')} {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate the expand/collapse icon.
|
||||
.material-icons.expand {
|
||||
@include mixins.rotate(0deg);
|
||||
|
||||
@at-root #{selector.replace(&, 'details', 'details[open]')} {
|
||||
@include mixins.rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-contents {
|
||||
.details-content {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
@use 'sass:color';
|
||||
@use 'sass:map';
|
||||
@use '../../constants';
|
||||
|
||||
@mixin theme($theme) {
|
||||
$is-dark-theme: map.get($theme, is-dark);
|
||||
|
||||
.roadmap-done-summary::marker {
|
||||
color: if($is-dark-theme, constants.$offwhite, color.adjust(constants.$darkgray, $lightness: 10%));
|
||||
.page-guide-roadmap {
|
||||
.completed-details {
|
||||
border-top: 1px solid if($is-dark-theme, constants.$mediumgray, constants.$lightgray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
.roadmap-done-details {
|
||||
padding: 0 20px 20px;
|
||||
margin: 0 -20px;
|
||||
}
|
||||
.page-guide-roadmap {
|
||||
.completed-details {
|
||||
box-shadow: none;
|
||||
margin-top: 4rem;
|
||||
padding: 4rem 0 2rem;
|
||||
|
||||
.roadmap-done-summary {
|
||||
margin: 0 -20px;
|
||||
font-size: 3.2rem;
|
||||
height: 40px;
|
||||
padding-bottom: 0;
|
||||
display: list-item;
|
||||
user-select: none;
|
||||
> summary {
|
||||
justify-content: space-between;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
> .details-content {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.roadmap-last-updated {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@use 'sass:math';
|
||||
@use '../../mixins';
|
||||
|
||||
$tocItemLineHeight: 24;
|
||||
@@ -9,15 +10,15 @@ $tocMarkerSize: 6;
|
||||
border-radius: 50%;
|
||||
content: "";
|
||||
height: #{$tocMarkerSize}px;
|
||||
left: -#{($tocMarkerSize - $tocMarkerRailSize) / 2}px;
|
||||
left: -#{math.div($tocMarkerSize - $tocMarkerRailSize, 2)}px;
|
||||
position: absolute;
|
||||
top: calc(#{$tocItemTopPadding}px + #{$tocItemLineHeight / 2}px - #{$tocMarkerSize / 2}px);
|
||||
top: calc(#{$tocItemTopPadding}px + #{$tocItemLineHeight / 2 / 10}rem - #{$tocMarkerSize / 2}px);
|
||||
top: calc(#{$tocItemTopPadding}px + #{math.div($tocItemLineHeight, 2)}px - #{math.div($tocMarkerSize, 2)}px);
|
||||
top: calc(#{$tocItemTopPadding}px + #{math.div($tocItemLineHeight, 2 * 10)}rem - #{math.div($tocMarkerSize, 2)}px);
|
||||
width: #{$tocMarkerSize}px;
|
||||
}
|
||||
|
||||
.toc-container {
|
||||
width: 18%;
|
||||
width: 18vw;
|
||||
position: fixed;
|
||||
top: 76px;
|
||||
right: 0;
|
||||
@@ -158,13 +159,13 @@ aio-toc {
|
||||
}
|
||||
|
||||
&:first-child:before {
|
||||
top: calc(#{$tocItemTopPadding}px + #{$tocItemLineHeight / 2}px - #{$tocMarkerSize / 2}px);
|
||||
top: calc(#{$tocItemTopPadding}px + #{$tocItemLineHeight / 2 / 10}rem - #{$tocMarkerSize / 2}px);
|
||||
top: calc(#{$tocItemTopPadding}px + #{math.div($tocItemLineHeight, 2)}px - #{math.div($tocMarkerSize, 2)}px);
|
||||
top: calc(#{$tocItemTopPadding}px + #{math.div($tocItemLineHeight, 2 * 10)}rem - #{math.div($tocMarkerSize, 2)}px);
|
||||
}
|
||||
|
||||
&:last-child:before {
|
||||
bottom: calc(100% - (#{$tocItemTopPadding}px + #{$tocItemLineHeight / 2}px + #{$tocMarkerSize / 2}px));
|
||||
bottom: calc(100% - (#{$tocItemTopPadding}px + #{$tocItemLineHeight / 2 / 10}rem + #{$tocMarkerSize / 2}px));
|
||||
bottom: calc(100% - (#{$tocItemTopPadding}px + #{math.div($tocItemLineHeight, 2)}px + #{math.div($tocMarkerSize, 2)}px));
|
||||
bottom: calc(100% - (#{$tocItemTopPadding}px + #{math.div($tocItemLineHeight, 2 * 10)}rem + #{math.div($tocMarkerSize, 2)}px));
|
||||
}
|
||||
|
||||
&:not(.active):hover {
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
@use 'sass:list';
|
||||
@use 'sass:math';
|
||||
@use 'sass:selector';
|
||||
@use './constants';
|
||||
|
||||
// REM Font Adjustments
|
||||
@mixin font-size($sizeValue) {
|
||||
font-size: ($sizeValue) + px;
|
||||
font-size: ($sizeValue / 10) + rem;
|
||||
font-size: math.div($sizeValue, 10) + rem;
|
||||
}
|
||||
|
||||
@mixin letter-spacing($spacingValue) {
|
||||
letter-spacing: ($spacingValue) + px;
|
||||
letter-spacing: ($spacingValue / 10) + rem;
|
||||
letter-spacing: math.div($spacingValue, 10) + rem;
|
||||
}
|
||||
|
||||
@mixin line-height($heightValue) {
|
||||
line-height: ($heightValue) + px;
|
||||
line-height: ($heightValue / 10) + rem;
|
||||
line-height: math.div($heightValue, 10) + rem;
|
||||
}
|
||||
|
||||
// PLACEHOLDER
|
||||
|
||||
@@ -5,7 +5,9 @@ describe('site App', () => {
|
||||
let page: SitePage;
|
||||
|
||||
beforeEach(async () => {
|
||||
await SitePage.setWindowWidth(1050); // Make the window wide enough to show the SideNav side-by-side.
|
||||
// Make the window wide enough to show the SideNav side-by-side
|
||||
// (bigger than the app component's showTopMenuWidth).
|
||||
await SitePage.setWindowWidth(1200);
|
||||
page = new SitePage();
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ This `examples` tool folder contains three utilities:
|
||||
|
||||
* example-boilerplate.js - install/remove the npm dependencies and boilerplate files into/from each of the
|
||||
examples' subfolders.
|
||||
* run-example-e2e.js - run the e2e tests for one or more examples
|
||||
* run-example-e2e.mjs - run the e2e tests for one or more examples
|
||||
* create-example.js - create a new example from the `example-scaffold/` directory or by importing files from a CLI project.
|
||||
|
||||
See the [README.md](examples/README.md) for more details.
|
||||
|
||||
@@ -5,7 +5,7 @@ These zip files are generated by the utility in this folder.
|
||||
|
||||
## Example zipper
|
||||
|
||||
The `exampleZipper.js` tool is very similar to the Stackblitz's `builder.js`.
|
||||
The `exampleZipper.mjs` tool is very similar to the Stackblitz's `builder.mjs`.
|
||||
The latter creates an HTML file with all the examples' files and the former creates a zip file instead.
|
||||
They both use the `stackblitz.json` file to flag an example as something to stackblitz or zip.
|
||||
For example:
|
||||
@@ -35,7 +35,7 @@ These example types have separate boilerplate directories with the files that ar
|
||||
There are appropriate `package.json` files for each type of example in the boilerplate directories.
|
||||
If there is no special `package.json` file for an example type, the one from the `cli` boilerplate directory will be used instead.
|
||||
|
||||
The `exampleZipper.js` won't include any `System.js` related files for CLI-based projects.
|
||||
The `exampleZipper.mjs` won't include any `System.js` related files for CLI-based projects.
|
||||
|
||||
## The zipper.json
|
||||
|
||||
@@ -46,7 +46,7 @@ In those cases, you can create a `zipper.json` file with the same syntax. It wil
|
||||
|
||||
## Executing the zip generation
|
||||
|
||||
`generateZips.js` will create a zip for each `stackblitz.json` or `zipper.json` it finds.
|
||||
`generateZips.mjs` will create a zip for each `stackblitz.json` or `zipper.json` it finds.
|
||||
|
||||
Where? At `src/generated/zips/`.
|
||||
|
||||
|
||||
+16
-14
@@ -1,16 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
// Canonical path provides a consistent path (i.e. always forward slashes) across different OSes
|
||||
const path = require('canonical-path');
|
||||
const archiver = require('archiver');
|
||||
const fs = require('fs-extra');
|
||||
const globby = require('globby');
|
||||
import archiver from 'archiver';
|
||||
import path from 'canonical-path';
|
||||
import fs from 'fs-extra';
|
||||
import {globbySync} from 'globby';
|
||||
import {fileURLToPath} from 'url';
|
||||
|
||||
const regionExtractor = require('../transforms/examples-package/services/region-parser');
|
||||
import regionExtractor from '../transforms/examples-package/services/region-parser.js';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const EXAMPLE_CONFIG_NAME = 'example-config.json';
|
||||
|
||||
class ExampleZipper {
|
||||
export class ExampleZipper {
|
||||
constructor(sourceDirName, outputDirName) {
|
||||
this.examplesPackageJson = path.join(__dirname, '../examples/shared/package.json');
|
||||
this.examplesSystemjsConfig = path.join(__dirname, '../examples/shared/boilerplate/systemjs/src/systemjs.config.js');
|
||||
@@ -19,7 +19,7 @@ class ExampleZipper {
|
||||
|
||||
let gpathStackblitz = path.join(sourceDirName, '**/*stackblitz.json');
|
||||
let gpathZipper = path.join(sourceDirName, '**/zipper.json');
|
||||
let configFileNames = globby.sync([gpathStackblitz, gpathZipper], { ignore: ['**/node_modules/**'] });
|
||||
let configFileNames = globbySync([gpathStackblitz, gpathZipper], { ignore: ['**/node_modules/**'] });
|
||||
configFileNames.forEach((configFileName) => {
|
||||
this._zipExample(configFileName, sourceDirName, outputDirName);
|
||||
});
|
||||
@@ -46,12 +46,16 @@ class ExampleZipper {
|
||||
_getExampleType(sourceFolder) {
|
||||
const filePath = path.join(sourceFolder, EXAMPLE_CONFIG_NAME);
|
||||
try {
|
||||
return require(filePath, 'utf-8').projectType || 'cli';
|
||||
return this._loadJson(filePath).projectType || 'cli';
|
||||
} catch (err) { // empty file, so it is cli
|
||||
return 'cli';
|
||||
}
|
||||
}
|
||||
|
||||
_loadJson(filePath) {
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
}
|
||||
|
||||
// rename a custom main.ts or index.html file
|
||||
_renameFile(file, exampleType) {
|
||||
if (/src\/main[-.]\w+\.ts$/.test(file) && exampleType !== 'universal') {
|
||||
@@ -66,7 +70,7 @@ class ExampleZipper {
|
||||
}
|
||||
|
||||
_zipExample(configFileName, sourceDirName, outputDirName) {
|
||||
let json = require(configFileName, 'utf-8');
|
||||
let json = this._loadJson(configFileName);
|
||||
const basePath = json.basePath || '';
|
||||
const jsonFileName = configFileName.replace(/^.*[\\\/]/, '');
|
||||
let relativeDirName = path.dirname(path.relative(sourceDirName, configFileName));
|
||||
@@ -149,7 +153,7 @@ class ExampleZipper {
|
||||
|
||||
gpaths.push(...alwaysExcludes);
|
||||
|
||||
let fileNames = globby.sync(gpaths, { ignore: ['**/node_modules/**'] });
|
||||
let fileNames = globbySync(gpaths, { ignore: ['**/node_modules/**'] });
|
||||
|
||||
let zip = this._createZipArchive(outputFileName);
|
||||
fileNames.forEach((fileName) => {
|
||||
@@ -176,5 +180,3 @@ class ExampleZipper {
|
||||
zip.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ExampleZipper;
|
||||
+4
-2
@@ -1,6 +1,8 @@
|
||||
const ExampleZipper = require('./exampleZipper');
|
||||
const path = require('canonical-path');
|
||||
import path from 'canonical-path';
|
||||
import {fileURLToPath} from 'url';
|
||||
import {ExampleZipper} from './exampleZipper.mjs';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const EXAMPLES_PATH = path.join(__dirname, '../../content/examples');
|
||||
const ZIPS_PATH = path.join(__dirname, '../../src/generated/zips');
|
||||
|
||||
@@ -135,9 +135,9 @@ It will remove all files that are not tracked by git, **including any new files
|
||||
So, be sure to commit your work before removing the boilerplate.
|
||||
|
||||
|
||||
### `run-example-e2e.js`
|
||||
### `run-example-e2e.mjs`
|
||||
|
||||
The [run-example-e2e.js](./run-example-e2e.js) script will find and run the e2e tests for all examples.
|
||||
The [run-example-e2e.mjs](./run-example-e2e.mjs) script will find and run the e2e tests for all examples.
|
||||
Although it only runs e2e tests by default, it can be configured to run any test command (for CLI-based examples) by using the `tests` property of the [example-config.json](#example-config) file.
|
||||
It is named `*-e2e` for historical reasons, but it is not limited to running e2e tests.
|
||||
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
const path = require('canonical-path');
|
||||
const fs = require('fs-extra');
|
||||
const argv = require('yargs').argv;
|
||||
const globby = require('globby');
|
||||
const xSpawn = require('cross-spawn');
|
||||
const treeKill = require('tree-kill');
|
||||
const shelljs = require('shelljs');
|
||||
const findFreePort = require('find-free-port');
|
||||
import path from 'canonical-path';
|
||||
import {spawn} from 'cross-spawn';
|
||||
import findFreePort from 'find-free-port';
|
||||
import fs from 'fs-extra';
|
||||
import {globby} from 'globby';
|
||||
import puppeteer from 'puppeteer';
|
||||
import shelljs from 'shelljs';
|
||||
import treeKill from 'tree-kill';
|
||||
import {fileURLToPath} from 'url';
|
||||
import yargs from 'yargs';
|
||||
import {hideBin} from 'yargs/helpers'
|
||||
|
||||
shelljs.set('-e');
|
||||
|
||||
// Set `CHROME_BIN` as an environment variable for Karma to pick up in unit tests.
|
||||
process.env.CHROME_BIN = require('puppeteer').executablePath();
|
||||
process.env.CHROME_BIN = puppeteer.executablePath();
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const {argv} = yargs(hideBin(process.argv));
|
||||
|
||||
const AIO_PATH = path.join(__dirname, '../../');
|
||||
const EXAMPLES_PATH = path.join(AIO_PATH, './content/examples/');
|
||||
@@ -25,9 +31,15 @@ const IGNORED_EXAMPLES = [];
|
||||
* Run Protractor End-to-End Tests for Doc Samples
|
||||
*
|
||||
* Flags
|
||||
* --filter to filter/select _example app subdir names
|
||||
* --filter to filter/select example app subdir names
|
||||
* Can be used multiple times to include multiple patterns.
|
||||
* e.g. --filter=foo // all example apps with 'foo' in their folder names.
|
||||
*
|
||||
* --exclude to exclude example app subdir names
|
||||
* Can be used multiple times to exclude multiple patterns.
|
||||
* NOTE: `--exclude` is always cosidered after `--filter`.
|
||||
* e.g. --exclude=bar // Exclude all example apps with 'bar' in their folder names.
|
||||
*
|
||||
* --setup to run yarn install, copy boilerplate and update webdriver
|
||||
* e.g. --setup
|
||||
*
|
||||
@@ -60,7 +72,7 @@ function runE2e() {
|
||||
return Promise.resolve()
|
||||
.then(
|
||||
() => findAndRunE2eTests(
|
||||
argv.filter, outputFile, argv.shard,
|
||||
argv.filter, argv.exclude, outputFile, argv.shard,
|
||||
argv.cliSpecsConcurrency || DEFAULT_CLI_SPECS_CONCURRENCY, argv.retry || 1))
|
||||
.then((status) => {
|
||||
reportStatus(status, outputFile);
|
||||
@@ -76,7 +88,9 @@ function runE2e() {
|
||||
|
||||
// Finds all of the *e2e-spec.tests under the examples folder along with the corresponding apps
|
||||
// that they should run under. Then run each app/spec collection sequentially.
|
||||
function findAndRunE2eTests(filter, outputFile, shard, cliSpecsConcurrency, maxAttempts) {
|
||||
function findAndRunE2eTests(
|
||||
includeFilter, excludeFilter, outputFile, shard, cliSpecsConcurrency, maxAttempts) {
|
||||
const filter = {include: includeFilter, exclude: excludeFilter};
|
||||
const shardParts = shard ? shard.split('/') : [0, 1];
|
||||
const shardModulo = parseInt(shardParts[0], 10);
|
||||
const shardDivider = parseInt(shardParts[1], 10);
|
||||
@@ -84,7 +98,8 @@ function findAndRunE2eTests(filter, outputFile, shard, cliSpecsConcurrency, maxA
|
||||
// create an output file with header.
|
||||
const startTime = new Date().getTime();
|
||||
let header = `Doc Sample Protractor Results on ${new Date().toLocaleString()}\n`;
|
||||
header += ` Filter: ${filter ? filter : 'All tests'}\n\n`;
|
||||
header += ` Include: ${filter.include || 'All tests'}\n`;
|
||||
header += ` Exclude: ${filter.exclude || 'No tests'}\n\n`;
|
||||
fs.writeFileSync(outputFile, header);
|
||||
|
||||
const status = {passed: [], failed: []};
|
||||
@@ -331,7 +346,7 @@ function spawnExt(
|
||||
let processOutput = '';
|
||||
printMessage(`running: ${descr}\n`);
|
||||
try {
|
||||
proc = xSpawn.spawn(command, args, options);
|
||||
proc = spawn(command, args, options);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
reject(e);
|
||||
@@ -378,16 +393,23 @@ function getE2eSpecs(basePath, filter) {
|
||||
// Find all e2e specs in a given example folder.
|
||||
function getE2eSpecsFor(basePath, specFile, filter) {
|
||||
// Only get spec file at the example root.
|
||||
// The formatter doesn't understand nested template string expressions (honestly, neither do I).
|
||||
// clang-format off
|
||||
const e2eSpecGlob = `${filter ? `*${filter}*` : '*'}/${specFile}`;
|
||||
// clang-format on
|
||||
const e2eSpecGlob = [
|
||||
`${filter.include ? filterToGlob(filter.include) : '*'}/${specFile}`,
|
||||
`!${filter.exclude ? filterToGlob(filter.exclude) : ''}/${specFile}`,
|
||||
];
|
||||
return globby(e2eSpecGlob, {cwd: basePath, nodir: true})
|
||||
.then(
|
||||
paths => paths.filter(file => !IGNORED_EXAMPLES.some(ignored => file.startsWith(ignored)))
|
||||
.map(file => path.join(basePath, file)));
|
||||
}
|
||||
|
||||
function filterToGlob(filter) {
|
||||
// `filter` can be either a string (if there is one occurrence of the corresponding option) or an
|
||||
// array (if there are two or more occurrences of the corresponding option). In other words, if
|
||||
// `filter` is an array, it will have more than one element.
|
||||
return Array.isArray(filter) ? `*{${filter.join(',')}}*` : `*${filter}*`;
|
||||
}
|
||||
|
||||
// Load configuration for an example. Used for SystemJS
|
||||
function loadExampleConfig(exampleFolder) {
|
||||
// Default config.
|
||||
@@ -4,11 +4,11 @@
|
||||
In AIO we use it to share one or more runnable versions of our examples.
|
||||
|
||||
Stackblitz can be used both on a separate page and in an embedded form.
|
||||
* `generateStackblitz.js` - executes each of the StackblitzBuilder to generate a stackblitz file for each example.
|
||||
* `generateStackblitz.mjs` - executes each of the StackblitzBuilder to generate a stackblitz file for each example.
|
||||
|
||||
## Stackblitz generation
|
||||
|
||||
Both forms are created within `builder.js`.
|
||||
Both forms are created within `builder.mjs`.
|
||||
How is a stackblitz created?
|
||||
What is the process from a directory with files to a link with a stackblitz.
|
||||
|
||||
@@ -17,13 +17,13 @@ It contains an `<input>` element for each file we need in the stackblitz.
|
||||
|
||||
The form will be submitted on load, so you can either double click the HTML file or open it with an anchor tag to open the stackblitz.
|
||||
|
||||
So the `builder.js` job is to get all the needed files from an example and build this HTML file for you.
|
||||
So the `builder.mjs` job is to get all the needed files from an example and build this HTML file for you.
|
||||
|
||||
## Customizing the generation per example basis
|
||||
|
||||
How does this tool know what is an example and what is not?
|
||||
It will look for all folders containing a `stackblitz.json` file.
|
||||
If found, all files within the folder and subfolders will be used in the stackblitz, with a few generic exceptions that you can find at `builder.js`.
|
||||
If found, all files within the folder and subfolders will be used in the stackblitz, with a few generic exceptions that you can find at `builder.mjs`.
|
||||
|
||||
You can use the `stackblitz.json` to customize the stackblitz generation.
|
||||
For example:
|
||||
@@ -44,7 +44,7 @@ Here you can specify a description for the stackblitz, some tags and also a file
|
||||
|
||||
## Executing the stackblitz generation
|
||||
|
||||
`generateStackblitz.js` will create a stackblitz for each `stackblitz.json` it finds.
|
||||
`generateStackblitz.mjs` will create a stackblitz for each `stackblitz.json` it finds.
|
||||
|
||||
Where?
|
||||
At `src/generated/live-examples/`.
|
||||
|
||||
+16
-15
@@ -1,15 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
// Canonical path provides a consistent path (i.e. always forward slashes) across different OSes
|
||||
const path = require('canonical-path');
|
||||
const fs = require('fs-extra');
|
||||
const globby = require('globby');
|
||||
const jsdom = require('jsdom');
|
||||
const json5 = require('json5');
|
||||
import path from 'canonical-path';
|
||||
import fs from 'fs-extra';
|
||||
import {globbySync} from 'globby';
|
||||
import jsdom from 'jsdom';
|
||||
import json5 from 'json5';
|
||||
import {fileURLToPath} from 'url';
|
||||
|
||||
const regionExtractor = require('../transforms/examples-package/services/region-parser');
|
||||
import regionExtractor from '../transforms/examples-package/services/region-parser.js';
|
||||
|
||||
class StackblitzBuilder {
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export class StackblitzBuilder {
|
||||
constructor(basePath, destPath) {
|
||||
this.basePath = basePath;
|
||||
this.destPath = destPath;
|
||||
@@ -24,7 +25,7 @@ class StackblitzBuilder {
|
||||
// When testing it sometimes helps to look a just one example directory like so:
|
||||
// const stackblitzPaths = path.join(this.basePath, '**/testing/*stackblitz.json');
|
||||
const stackblitzPaths = path.join(this.basePath, '**/*stackblitz.json');
|
||||
const fileNames = globby.sync(stackblitzPaths, { ignore: ['**/node_modules/**'] });
|
||||
const fileNames = globbySync(stackblitzPaths, { ignore: ['**/node_modules/**'] });
|
||||
let failed = false;
|
||||
fileNames.forEach((configFileName) => {
|
||||
try {
|
||||
@@ -64,7 +65,9 @@ class StackblitzBuilder {
|
||||
_getBoilerplatePackageJson(exampleType) {
|
||||
if (!this._boilerplatePackageJsons.hasOwnProperty(exampleType)) {
|
||||
const pkgJsonPath = `${__dirname}/../examples/shared/boilerplate/${exampleType}/package.json`;
|
||||
this._boilerplatePackageJsons[exampleType] = fs.existsSync(pkgJsonPath) ? require(pkgJsonPath) : null;
|
||||
this._boilerplatePackageJsons[exampleType] = fs.existsSync(pkgJsonPath)
|
||||
? JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'))
|
||||
: null;
|
||||
}
|
||||
|
||||
return this._boilerplatePackageJsons[exampleType];
|
||||
@@ -123,7 +126,7 @@ class StackblitzBuilder {
|
||||
_checkForOutdatedConfig() {
|
||||
// Ensure that nobody is trying to use the old config filenames (i.e. `plnkr.json`).
|
||||
const plunkerPaths = path.join(this.basePath, '**/*plnkr.json');
|
||||
const fileNames = globby.sync(plunkerPaths, { ignore: ['**/node_modules/**'] });
|
||||
const fileNames = globbySync(plunkerPaths, { ignore: ['**/node_modules/**'] });
|
||||
|
||||
if (fileNames.length) {
|
||||
const readmePath = path.join(__dirname, 'README.md');
|
||||
@@ -325,7 +328,7 @@ class StackblitzBuilder {
|
||||
|
||||
gpaths.push(...defaultExcludes);
|
||||
|
||||
config.fileNames = globby.sync(gpaths, { ignore: ['**/node_modules/**'] });
|
||||
config.fileNames = globbySync(gpaths, { ignore: ['**/node_modules/**'] });
|
||||
|
||||
return config;
|
||||
}
|
||||
@@ -341,5 +344,3 @@ class StackblitzBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StackblitzBuilder;
|
||||
@@ -1,8 +0,0 @@
|
||||
const path = require('path');
|
||||
const StackblitzBuilder = require('./builder');
|
||||
|
||||
const EXAMPLES_PATH = path.join(__dirname, '../../content/examples');
|
||||
const LIVE_EXAMPLES_PATH = path.join(__dirname, '../../src/generated/live-examples');
|
||||
|
||||
new StackblitzBuilder(EXAMPLES_PATH, LIVE_EXAMPLES_PATH).build();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user