Compare commits
353 Commits
zone.js-0.
...
8.0.3
Author | SHA1 | Date | |
---|---|---|---|
9b812dc729 | |||
2eee8d59fe | |||
f0dc8cfc05 | |||
79471f7610 | |||
54f68c342b | |||
86bfb0172b | |||
52186165ee | |||
e9ae885982 | |||
8a1bd2c401 | |||
018b695ef2 | |||
1d193df360 | |||
0a46b2acfb | |||
b63aa4e7ef | |||
6aae19633b | |||
00242e8d6d | |||
4ea231fdbe | |||
6bad2ca586 | |||
dabf1325ed | |||
987b18596b | |||
f470e69fba | |||
980bcaf176 | |||
48f7f65bf4 | |||
7fe95b1882 | |||
266871baf4 | |||
bc54ecf66e | |||
274833ee94 | |||
ff5934597a | |||
09b60ed36a | |||
0bb83b2ba6 | |||
de04741863 | |||
8ea91887ca | |||
ff184b56eb | |||
15d138fdd3 | |||
1699b54860 | |||
07f6b2c018 | |||
4a193580fe | |||
9c222350f9 | |||
c36edffe2a | |||
13b984d72b | |||
f00eff5916 | |||
31ee45efdc | |||
b68f2d6fa5 | |||
d8def6d728 | |||
e8f464409c | |||
a73b8a62c8 | |||
2fe8f2b1e0 | |||
f1c08c6918 | |||
bf9de8ca53 | |||
df37c47ac5 | |||
788d19c036 | |||
8492499fe7 | |||
48174010a1 | |||
1460e46ba1 | |||
a7ff7d8d27 | |||
eb970b4626 | |||
18d301258a | |||
6a01fa532c | |||
e190a7e9de | |||
53c6425954 | |||
c198dc6c19 | |||
d1b26dd3a1 | |||
ce7131f3fb | |||
934702892e | |||
8a830a72e2 | |||
2abb54c4a1 | |||
44632bb700 | |||
e33b382ab4 | |||
c4da400db4 | |||
c40e6b2edc | |||
978fc27314 | |||
b9f2bbb7d1 | |||
9654d646ae | |||
7f214499e8 | |||
a3d55a9d27 | |||
e222b8761e | |||
1435c0b3a9 | |||
0a7aebbcf5 | |||
bd7f91feac | |||
3bbc89b958 | |||
52d98e563d | |||
b279f8bd62 | |||
954e34cc91 | |||
e1f6d15387 | |||
cbb3794931 | |||
77a5790a9d | |||
4ca401c394 | |||
3dcd5ebdbc | |||
49307f0595 | |||
762fc28fee | |||
338e58c278 | |||
7e2ed89208 | |||
56a3dcf44c | |||
501bacdcf6 | |||
c5f2979a87 | |||
fe02462b5f | |||
3000d19ad1 | |||
dcf9f05c9a | |||
ba56f3c15b | |||
865ad56e9a | |||
5cda2a041b | |||
6a484853a9 | |||
e6ac289518 | |||
4e8614bb92 | |||
9ace748d3a | |||
08c38a1f99 | |||
9aeef0afdb | |||
8aef446373 | |||
f7ee91a17c | |||
8ae0afd3f8 | |||
be82270493 | |||
4cef2c1236 | |||
26e3615e19 | |||
65f20107fe | |||
5a7bcd1862 | |||
152ea36c80 | |||
ea2adb104b | |||
19caace2ab | |||
ccc1c27461 | |||
97268b95f7 | |||
7c41abe64a | |||
991e138650 | |||
d00b421402 | |||
06ffed5141 | |||
5a15126520 | |||
9e4b2f1a4f | |||
393529d3b5 | |||
81b75590d7 | |||
151f0f4b2c | |||
f88f941008 | |||
0e17958640 | |||
e82b45c8a7 | |||
8422ef2a93 | |||
cdf586d0d2 | |||
3828c89792 | |||
0f352b6350 | |||
06bdecffb9 | |||
99ddec152b | |||
31aba96fcb | |||
facce2c9fd | |||
edaf058548 | |||
4bff2d0c8c | |||
cfc608aaf3 | |||
6d9f5fcddb | |||
b19a05c25a | |||
bd75b3b7b3 | |||
1c67b9063b | |||
9a9ae60e0e | |||
ae6fa9260a | |||
f8fa2f2a6c | |||
f3ee9a6144 | |||
d076c51455 | |||
38a7e2a775 | |||
c61c14a127 | |||
652b0959a8 | |||
002fbddcf1 | |||
4761d40a41 | |||
608e0f0d67 | |||
45718b3b21 | |||
b5756bcc39 | |||
60f0462049 | |||
9fd22959d3 | |||
d5dd907396 | |||
5a6bf7de0e | |||
d135bb5e6e | |||
8b9a3e49f2 | |||
44e55fe2e4 | |||
063cc308b6 | |||
c567135dd5 | |||
361f181c8c | |||
b40844523b | |||
c8af830ec8 | |||
e2c9ddb33e | |||
04196ec2d0 | |||
7323072c5c | |||
f0c5400cab | |||
3f2f937c9f | |||
256fe2421a | |||
e6f27bcb0c | |||
8abcf04a5e | |||
bf38df4eb9 | |||
79a025306a | |||
62c936aa49 | |||
77e1b34d91 | |||
26cb128b3c | |||
fdbe07982a | |||
c0e3209915 | |||
70880f0cea | |||
221a5ba634 | |||
68b08cf782 | |||
10560a0872 | |||
561e01ddfd | |||
d1345c78f0 | |||
276f9067c8 | |||
0e90a4263b | |||
e4cc1398aa | |||
8d1f993ceb | |||
df1ab49893 | |||
df03fec8a6 | |||
c01cae22e1 | |||
5a46f94987 | |||
5716605d52 | |||
8043e3131c | |||
54f7245081 | |||
5de24b6dfe | |||
4dc4d7f30a | |||
fcea1a3c22 | |||
8bc4da8665 | |||
a5a2d525ae | |||
4690dcecac | |||
722b2fa6ed | |||
124d1abf19 | |||
736d3ef820 | |||
9b0ad347e1 | |||
d2598ace0a | |||
4a25e4cf95 | |||
3f67bf208d | |||
d85476fd8c | |||
9763edf829 | |||
b6ae54c547 | |||
3de26a84ff | |||
8022d3691b | |||
581336a918 | |||
effc58086c | |||
766615c8a1 | |||
ede4246663 | |||
bb8a6abbf2 | |||
32daa930d0 | |||
b6aa99d3a7 | |||
e10b213784 | |||
a67cf99b0c | |||
04d04fd147 | |||
17361d2b2c | |||
9b88920aa9 | |||
b12e76d1d3 | |||
af001a8cbd | |||
db64b014f8 | |||
7e34975bb0 | |||
0fa48e8c00 | |||
fde3f467e2 | |||
7b378a6920 | |||
3136d9ff2e | |||
325e6cf557 | |||
381d7c4e44 | |||
1b6f3c1ead | |||
c38349127c | |||
a4817729a2 | |||
61c343e3eb | |||
24e6c1e80d | |||
42fc5c9b33 | |||
33e7b285ca | |||
74afdc37da | |||
3c1ffba0ad | |||
14c0017db8 | |||
4878f4890b | |||
b7edef0cd3 | |||
39fa937ab9 | |||
2977829c67 | |||
dbe845e048 | |||
e562acc884 | |||
e295c6a0ae | |||
10b43355f8 | |||
d4e7587bd8 | |||
cabe03cf6d | |||
0cefa9e342 | |||
e073daa48e | |||
994d48a96e | |||
806f8118c8 | |||
dd299f9eb2 | |||
49ec3f312c | |||
71eba450e6 | |||
192f108b0f | |||
dd8651db73 | |||
5bbbe3f684 | |||
a71d8a837b | |||
e8ceae14e1 | |||
c3246e6f16 | |||
685753361e | |||
6b07711f96 | |||
12fb639b7d | |||
644925fd0c | |||
bcc72b0924 | |||
e1091b2ba8 | |||
08841e31d9 | |||
d1fcc2bc13 | |||
fac00442d2 | |||
b8cbcbcf49 | |||
1ed45bd783 | |||
82fd1920b1 | |||
f6d7271ec7 | |||
c1f3faf1df | |||
97202278f9 | |||
132f01c5ca | |||
6a987f1b9c | |||
548b003ed3 | |||
48dc41de01 | |||
817c2b49bc | |||
fed07c735c | |||
390cac6874 | |||
8eb0b8bd40 | |||
d7283c6085 | |||
3fe3a84a4b | |||
28e4187bd6 | |||
7cbc69c890 | |||
1dc134bc6b | |||
6a61d37f95 | |||
8d2e92bcfe | |||
5038f5c909 | |||
6eeca70043 | |||
9f68c35fa9 | |||
21418ea109 | |||
02d8b4ed3c | |||
6748392edc | |||
d9fd301157 | |||
71c5d80ce7 | |||
9798229fde | |||
4b2fcfd5dc | |||
b4d291aa7a | |||
b0ecafdc2f | |||
6816bb62d7 | |||
4b05b8cea0 | |||
a0728aedf7 | |||
ea96f6112a | |||
b706800ea8 | |||
c6f95b1d70 | |||
d896126604 | |||
a20da5ddcc | |||
18878600ba | |||
d7e10f3f7e | |||
4d044ea5b2 | |||
e2d1e0cd98 | |||
4e056580bb | |||
e4c2e6a904 | |||
a50989832d | |||
525307b6a3 | |||
a3ab76b216 | |||
f2265d4b46 | |||
d3ac709b99 | |||
908d43a5bb | |||
71cdb0a08e | |||
c7fbbdfa99 | |||
10e4ab7712 | |||
5114c23c21 | |||
c3e585d7eb | |||
0776daec88 | |||
615e1a58b2 | |||
606758357e | |||
ba2a3595c6 | |||
a50bfe5054 | |||
c8983bc367 | |||
8d6d2c6704 | |||
6711f22e62 | |||
8dd9192fe3 | |||
870e0dab48 |
@ -1,4 +1,3 @@
|
||||
.git
|
||||
node_modules
|
||||
dist
|
||||
aio/content
|
||||
|
42
.bazelrc
42
.bazelrc
@ -1,3 +1,14 @@
|
||||
###############################
|
||||
# Typescript / Angular / Sass #
|
||||
###############################
|
||||
|
||||
# Make compilation fast, by keeping a few copies of the compilers
|
||||
# running as daemons, and cache SourceFile AST's to reduce parse time.
|
||||
build --strategy=AngularTemplateCompile=worker
|
||||
# TODO(alexeagle): re-enable after fixing worker instability with rxjs typings
|
||||
# build --strategy=TypeScriptCompile=worker
|
||||
build --strategy=TypeScriptCompile=standalone
|
||||
|
||||
# Enable debugging tests with --config=debug
|
||||
test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test_strategy=exclusive --test_timeout=9999 --nocache_test_results
|
||||
|
||||
@ -74,6 +85,12 @@ query --output=label_kind
|
||||
# By default, failing tests don't print any output, it goes to the log file
|
||||
test --test_output=errors
|
||||
|
||||
# Show which actions are run under workers,
|
||||
# and print all the actions running in parallel.
|
||||
# Helps to demonstrate that bazel uses all the cores on the machine.
|
||||
build --experimental_ui
|
||||
test --experimental_ui
|
||||
|
||||
################################
|
||||
# Settings for CircleCI #
|
||||
################################
|
||||
@ -137,31 +154,6 @@ build:remote --bes_results_url="https://source.cloud.google.com/results/invocati
|
||||
# This allows us to avoid installing a second copy of node_modules
|
||||
common --experimental_allow_incremental_repository_updates
|
||||
|
||||
# This option is changed to true in Bazel 0.27 and exposes a possible
|
||||
# regression in Bazel 0.27.0.
|
||||
# Error observed is in npm_package target `//packages/common/locales:package`:
|
||||
# ```
|
||||
# ERROR: /home/circleci/ng/packages/common/locales/BUILD.bazel:13:1: Assembling
|
||||
# npm package packages/common/locales/package failed: No usable spawn strategy found
|
||||
# for spawn with mnemonic SkylarkAction. Your --spawn_strategyor --strategy flags
|
||||
# are probably too strict. Visit https://github.com/bazelbuild/bazel/issues/7480 for
|
||||
# migration advises
|
||||
# ```
|
||||
# Suspect is https://github.com/bazelbuild/rules_nodejs/blob/master/internal/npm_package/npm_package.bzl#L75-L82:
|
||||
# ```
|
||||
# execution_requirements = {
|
||||
# # Never schedule this action remotely because it's not computationally expensive.
|
||||
# # It just copies files into a directory; it's not worth copying inputs and outputs to a remote worker.
|
||||
# # Also don't run it in a sandbox, because it resolves an absolute path to the bazel-out directory
|
||||
# # allowing the .pack and .publish runnables to work with no symlink_prefix
|
||||
# # See https://github.com/bazelbuild/rules_nodejs/issues/187
|
||||
# "local": "1",
|
||||
# },
|
||||
# ```
|
||||
build --incompatible_list_based_execution_strategy_selection=false
|
||||
test --incompatible_list_based_execution_strategy_selection=false
|
||||
run --incompatible_list_based_execution_strategy_selection=false
|
||||
|
||||
####################################################
|
||||
# User bazel configuration
|
||||
# NOTE: This needs to be the *last* entry in the config.
|
||||
|
42
.buildkite/Dockerfile
Normal file
42
.buildkite/Dockerfile
Normal file
@ -0,0 +1,42 @@
|
||||
# Heavily based on https://github.com/StefanScherer/dockerfiles-windows/ images.
|
||||
# Combines the node windowsservercore image with the Bazel Prerequisites (https://docs.bazel.build/versions/master/install-windows.html).
|
||||
# msys install taken from https://github.com/StefanScherer/dockerfiles-windows/issues/30
|
||||
# VS redist install taken from https://github.com/StefanScherer/dockerfiles-windows/blob/master/apache/Dockerfile
|
||||
# The nanoserver image won't work because MSYS2 does not run in it https://github.com/Alexpux/MSYS2-packages/issues/1493
|
||||
|
||||
# Before building this image, you must locally build node-windows:10.13.0-windowsservercore-1803.
|
||||
# Clone https://github.com/StefanScherer/dockerfiles-windows/commit/4ce7101a766b9b880ac262479dd9126b64d656cf and build using
|
||||
# docker build -t node-windows:10.13.0-windowsservercore-1803 --build-arg core=microsoft/windowsservercore:1803 --build-arg target=microsoft/windowsservercore:1803 .
|
||||
FROM node-windows:10.13.0-windowsservercore-1803
|
||||
|
||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
||||
|
||||
# Install 7zip to extract msys2
|
||||
RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe
|
||||
# For some reason the last letter in the destination directory is lost. So '/D=C:\\7zip0' will extract to '/D=C:\\7zip'.
|
||||
RUN Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait
|
||||
|
||||
# Extract msys2
|
||||
RUN Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz
|
||||
RUN Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait
|
||||
RUN Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait
|
||||
RUN Remove-Item msys2.tar.xz
|
||||
RUN Remove-Item msys2.tar
|
||||
RUN Remove-Item 7z.exe
|
||||
RUN Remove-Item -Recurse 7zip
|
||||
|
||||
# Add MSYS2 to PATH, and set BAZEL_SH
|
||||
RUN [Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine)
|
||||
RUN [Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine)
|
||||
|
||||
# Install Microsoft Visual C++ Redistributable for Visual Studio 2015
|
||||
RUN Invoke-WebRequest -UseBasicParsing 'https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe' -OutFile vc_redist.x64.exe
|
||||
RUN Start-Process 'c:\\vc_redist.x64.exe' -ArgumentList '/Install', '/Passive', '/NoRestart' -NoNewWindow -Wait
|
||||
RUN Remove-Item vc_redist.x64.exe
|
||||
|
||||
# Add a fix for https://github.com/docker/for-win/issues/2920 as entry point to the container.
|
||||
SHELL ["cmd", "/c"]
|
||||
COPY "fix-msys64.cmd" "C:\\fix-msys64.cmd"
|
||||
ENTRYPOINT cmd /C C:\\fix-msys64.cmd && cmd /c
|
||||
|
||||
CMD ["cmd.exe"]
|
96
.buildkite/README.md
Normal file
96
.buildkite/README.md
Normal file
@ -0,0 +1,96 @@
|
||||
# BuildKite configuration
|
||||
|
||||
This folder contains configuration for the [BuildKite](https://buildkite.com) based CI checks for
|
||||
this repository.
|
||||
|
||||
BuildKite is a CI provider that provides build coordination and reports while we provide the
|
||||
infrastructure.
|
||||
|
||||
CI runs are triggered by new PRs and will show up on the GitHub checks interface, along with the
|
||||
other current CI solutions.
|
||||
|
||||
Currently it is only used for tests on Windows platforms.
|
||||
|
||||
|
||||
## The build pipeline
|
||||
|
||||
BuildKite uses a pipeline for each repository. The `pipeline.yml` file defines pipeline
|
||||
[build steps](https://buildkite.com/docs/pipelines/defining-steps) for this repository.
|
||||
|
||||
Run results can be seen in the GitHub checks interface and in the
|
||||
[pipeline dashboard](https://buildkite.com/angular/angular).
|
||||
|
||||
Although most configuration is done via `pipeline.yml`, some options are only available
|
||||
in the online [pipeline settings](https://buildkite.com/angular/angular/settings).
|
||||
|
||||
|
||||
## Infrastructure
|
||||
|
||||
BuildKite does not provide the host machines where the builds runs, providing instead the
|
||||
[BuildKite Agent](https://buildkite.com/docs/agent/v3) that should be run our own infrastructure.
|
||||
|
||||
|
||||
### Agents
|
||||
|
||||
This agent polls the BuildKite API for builds, runs them, and reports back the results.
|
||||
Agents are the unit of concurrency: each agent can run one build at any given time.
|
||||
Adding agents allows more builds to be ran at the same time.
|
||||
|
||||
Individual agents can have tags, and pipeline steps can target only agents with certain tags via the
|
||||
`agents` field in `pipeline.yml`.
|
||||
For example: agents on Windows machines are tagged as `windows`, and the Windows specific build
|
||||
steps list `windows: true` in their `agents` field.
|
||||
|
||||
You can see the current agent pool, along with their tags, in the
|
||||
[agents list](https://buildkite.com/organizations/angular/agents).
|
||||
|
||||
|
||||
### Our host machines
|
||||
|
||||
We use [Google Cloud](https://cloud.google.com/) as our cloud provider, under the
|
||||
[Angular project](https://console.cloud.google.com/home/dashboard?project=internal-200822).
|
||||
To access this project you need need to be logged in with a Google account that's a member of
|
||||
team@angular.io.
|
||||
For googlers this may be your google.com account, for others it is an angular.io account.
|
||||
|
||||
In this project we have a number of Windows VMs running, each of them with several agents.
|
||||
The `provision-windows-buildkite.ps1` file contains instructions on how to create new host VMs that
|
||||
are fully configured to run the BuildKite agents as services.
|
||||
|
||||
Our pipeline uses [docker-buildkite-plugin](https://github.com/buildkite-plugins/docker-buildkite-plugin)
|
||||
to run build steps inside docker containers.
|
||||
This way we achieve isolation and hermeticity.
|
||||
|
||||
The `Dockerfile` file describes a custom Docker image that includes NodeJs, Yarn, and the Bazel
|
||||
pre-requisites on Windows.
|
||||
|
||||
To upload a new version of the docker image, follow any build instructions in `Dockerfile` and then
|
||||
run `docker build -t angular/node-bazel-windows:NEW_VERSION`, followed by
|
||||
`docker push angular/node-bazel-windows:NEW_VERSION`.
|
||||
After being pushed it should be available online, and you can use the new version in `pipeline.yml`.
|
||||
|
||||
|
||||
## Caretaker
|
||||
|
||||
BuildKite status can be found at https://www.buildkitestatus.com/.
|
||||
|
||||
Issues related to the BuildKite setup should be escalated to the Tools Team via the current
|
||||
caretaker, followed by Alex Eagle and Filipe Silva.
|
||||
|
||||
Support requests should be submitted via email to support@buildkite.com and cc Igor, Misko, Alex,
|
||||
Jeremy and Manu
|
||||
|
||||
|
||||
## Rollout strategy
|
||||
|
||||
At the moment our BuildKite CI uses 1 host VM running 4 agents, thus being capable of 4 concurrent
|
||||
builds.
|
||||
The only test running is `bazel test //tools/ts-api-guardian:all`, and the PR check is not
|
||||
mandatory.
|
||||
|
||||
In the future we should add cache support to speed up the initial `yarn` install, and also Bazel
|
||||
remote caching to speed up Bazel builds.
|
||||
|
||||
After the current setup is verified as stable and reliable the GitHub PR check can become mandatory.
|
||||
|
||||
The tests ran should also be expanded to cover most, if not all, of the Bazel tests.
|
6
.buildkite/fix-msys64.cmd
Normal file
6
.buildkite/fix-msys64.cmd
Normal file
@ -0,0 +1,6 @@
|
||||
@echo off
|
||||
REM Fix for https://github.com/docker/for-win/issues/2920
|
||||
REM echo "Fixing msys64 folder..."
|
||||
REM Touch all .dll files inside C:\msys64\
|
||||
forfiles /p C:\msys64\ /s /m *.dll /c "cmd /c Copy /B @path+,, >NUL"
|
||||
REM echo "Fixed msys64 folder."
|
10
.buildkite/pipeline.yml
Normal file
10
.buildkite/pipeline.yml
Normal file
@ -0,0 +1,10 @@
|
||||
steps:
|
||||
- label: windows-test
|
||||
commands:
|
||||
- "yarn install --frozen-lockfile --non-interactive --network-timeout 100000"
|
||||
- "yarn bazel test //tools/ts-api-guardian:all --noshow_progress"
|
||||
plugins:
|
||||
- docker#v2.1.0:
|
||||
image: "filipesilva/node-bazel-windows:0.0.2"
|
||||
agents:
|
||||
windows: true
|
94
.buildkite/provision-windows-buildkite.ps1
Normal file
94
.buildkite/provision-windows-buildkite.ps1
Normal file
@ -0,0 +1,94 @@
|
||||
# PowerShell script to provision a Windows Server with BuildKite
|
||||
# This script follows https://buildkite.com/docs/agent/v3/windows.
|
||||
|
||||
# Instructions
|
||||
|
||||
# VM creation:
|
||||
# In Google Cloud Platform, create a Compute Engine instance.
|
||||
# We recommend machine type n1-standard-16 (16 vCPUs, 60 GB memory).
|
||||
# Use a recent windows boot disk with container support such as
|
||||
# "Windows Server version 1803 Datacenter Core for Containers", and add a 128GB SSD disk.
|
||||
# Give it a name, then click "Create".
|
||||
|
||||
# VM setup:
|
||||
# In the Compute Engine menu, select "VM Instances". Click on the VM name you chose before.
|
||||
# Click "Set Windows Password" to choose a username and password.
|
||||
# Click RDP to open a remote desktop via browser, using the username and password.
|
||||
# In the Windows command prompt start an elevated powershell by inputing
|
||||
# "powershell -Command "Start-Process PowerShell -Verb RunAs" followed by Enter.
|
||||
# Download and execute this script from GitHub, passing the token (mandatory), tags (optional)
|
||||
# and number of agents (optional) as args:
|
||||
# ```
|
||||
# Invoke-WebRequest -Uri https://raw.githubusercontent.com/angular/angular/master/.buildkite/provision-windows-buildkite.ps1 -OutFile provision.ps1
|
||||
# .\provision.ps1 -token "MY_TOKEN" -tags "windows=true,another_tag=true" -agents 4
|
||||
# ```
|
||||
# The VM should restart and be fully configured.
|
||||
|
||||
# Creating extra VMs
|
||||
# You can create an image of the current VM by following the instructions below.
|
||||
# https://cloud.google.com/compute/docs/instances/windows/creating-windows-os-image
|
||||
# Then create a new VM and choose "Custom images".
|
||||
|
||||
|
||||
# Script proper.
|
||||
|
||||
# Get the token and tags from arguments.
|
||||
param (
|
||||
[Parameter(Mandatory=$true)][string]$token,
|
||||
[string]$tags = "",
|
||||
[Int]$agents = 1
|
||||
)
|
||||
|
||||
# Allow HTTPS
|
||||
[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
|
||||
|
||||
# Helper to add to PATH.
|
||||
# Will take current PATH so avoid running it after anything to modifies only the powershell session path.
|
||||
function Add-Path ([string]$newPathItem) {
|
||||
$Env:Path+= ";" + $newPathItem + ";"
|
||||
[Environment]::SetEnvironmentVariable("Path",$env:Path, [System.EnvironmentVariableTarget]::Machine)
|
||||
}
|
||||
|
||||
# Install Git for Windows
|
||||
Write-Host "Installing Git for Windows."
|
||||
Invoke-WebRequest -Uri https://github.com/git-for-windows/git/releases/download/v2.19.1.windows.1/Git-2.19.1-64-bit.exe -OutFile git.exe
|
||||
.\git.exe /VERYSILENT /NORESTART /NOCANCEL /SP- /CLOSEAPPLICATIONS /RESTARTAPPLICATIONS /COMPONENTS="icons,ext\reg\shellhere,assoc,assoc_sh" /DIR="C:\git"
|
||||
Add-Path "C:\git\bin"
|
||||
# Sleep for 15s while git is installed. Trying to remove the git.exe before it finishes install causes an error.
|
||||
Start-Sleep -s 15
|
||||
Remove-Item git.exe
|
||||
|
||||
# Download NSSM (https://nssm.cc/) to run the BuildKite agent as a service.
|
||||
Write-Host "Downloading NSSM."
|
||||
Invoke-WebRequest -Uri https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip -OutFile nssm.zip
|
||||
Expand-Archive -Path nssm.zip -DestinationPath C:\nssm
|
||||
Add-Path "C:\nssm\nssm-2.24-101-g897c7ad\win64"
|
||||
Remove-Item nssm.zip
|
||||
|
||||
# Run the BuildKite agent install script
|
||||
Write-Host "Installing BuildKite agent."
|
||||
$env:buildkiteAgentToken = $token
|
||||
$env:buildkiteAgentTags = $tags
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/buildkite/agent/master/install.ps1'))
|
||||
|
||||
# Configure the BuildKite agent clone and timestamp behavior
|
||||
Add-Content C:\buildkite-agent\buildkite-agent.cfg "`ngit-clone-flags=--config core.autocrlf=input --config core.eol=lf --config core.longpaths=true --config core.symlinks=true`n"
|
||||
Add-Content C:\buildkite-agent\buildkite-agent.cfg "`ntimestamp-lines=true`n"
|
||||
|
||||
# Register the BuildKite agent service using NSSM, so that it persists through restarts and is
|
||||
# restarted if the process dies.
|
||||
for ($i=1; $i -le $agents; $i++)
|
||||
{
|
||||
$agentName = "buildkite-agent-$i"
|
||||
Write-Host "Registering $agentName as a service."
|
||||
nssm.exe install $agentName "C:\buildkite-agent\bin\buildkite-agent.exe" "start"
|
||||
nssm.exe set $agentName AppStdout "C:\buildkite-agent\$agentName.log"
|
||||
nssm.exe set $agentName AppStderr "C:\buildkite-agent\$agentName.log"
|
||||
nssm.exe status $agentName
|
||||
nssm.exe start $agentName
|
||||
nssm.exe status $agentName
|
||||
}
|
||||
|
||||
# Restart the machine.
|
||||
Restart-Computer
|
@ -28,14 +28,3 @@ test --flaky_test_attempts=2
|
||||
|
||||
# More details on failures
|
||||
build --verbose_failures=true
|
||||
|
||||
# We have seen some flakiness in using TS workers on CircleCI
|
||||
# https://angular-team.slack.com/archives/C07DT5M6V/p1562693245183400
|
||||
# > failures like `ERROR: /home/circleci/ng/packages/core/test/BUILD.bazel:5:1:
|
||||
# > Compiling TypeScript (devmode) //packages/core/test:test_lib failed: Worker process did not return a WorkResponse:`
|
||||
# > I saw that issue a couple times today.
|
||||
# > Example job: https://circleci.com/gh/angular/angular/385517
|
||||
# We expect that TypeScript compilations will parallelize wider than the number of local cores anyway
|
||||
# so we should saturate remote workers with TS compilations
|
||||
build --strategy=TypeScriptCompile=standalone
|
||||
build --strategy=AngularTemplateCompile=standalone
|
||||
|
@ -58,7 +58,17 @@ var_5: &setup_bazel_remote_execution
|
||||
# cause decryption failures based on the openssl version. https://stackoverflow.com/a/39641378/4317734
|
||||
openssl aes-256-cbc -d -in .circleci/gcp_token -md md5 -k "$CI_REPO_NAME" -out /home/circleci/.gcp_credentials
|
||||
echo "export GOOGLE_APPLICATION_CREDENTIALS=/home/circleci/.gcp_credentials" >> $BASH_ENV
|
||||
./.circleci/setup-rbe.sh .bazelrc.user
|
||||
touch .bazelrc.user
|
||||
sudo bash -c "echo -e 'build --config=remote\n' >> .bazelrc.user"
|
||||
sudo bash -c "echo -e 'build:remote --remote_accept_cached=true\n' >> .bazelrc.user"
|
||||
echo "Reading from remote cache for bazel remote jobs."
|
||||
if [[ "$CI_PULL_REQUEST" == "false" ]]; then
|
||||
sudo bash -c "echo -e 'build:remote --remote_upload_local_results=true\n' >> .bazelrc.user"
|
||||
echo "Uploading local build results to remote cache."
|
||||
else
|
||||
sudo bash -c "echo -e 'build:remote --remote_upload_local_results=false\n' >> .bazelrc.user"
|
||||
echo "Not uploading local build results to remote cache."
|
||||
fi
|
||||
|
||||
# Settings common to each job
|
||||
var_6: &job_defaults
|
||||
@ -128,7 +138,7 @@ var_13: ¬ify_caretaker_on_fail
|
||||
# `$SLACK_CARETAKER_WEBHOOK_URL` is a secret env var defined in CircleCI project settings.
|
||||
# The URL comes from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
|
||||
command: |
|
||||
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job for $CIRCLE_BRANCH branch failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
|
||||
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
|
||||
curl --request POST --header "Content-Type: application/json" --data "$notificationJson" $SLACK_CARETAKER_WEBHOOK_URL
|
||||
|
||||
var_14: ¬ify_dev_infra_on_fail
|
||||
@ -138,14 +148,9 @@ var_14: ¬ify_dev_infra_on_fail
|
||||
# `$SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL` is a secret env var defined in CircleCI project settings.
|
||||
# The URL comes from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
|
||||
command: |
|
||||
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job for $CIRCLE_BRANCH branch failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
|
||||
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
|
||||
curl --request POST --header "Content-Type: application/json" --data "$notificationJson" $SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
|
||||
# Cache key for the Material unit tests job. **Note** when updating the SHA in the cache keys,
|
||||
# also update the SHA for the "MATERIAL_REPO_COMMIT" environment variable.
|
||||
var_15: &material_unit_tests_cache_key v4-angular-material-701302dc482d7e4b77990b24e3b5ab330bbf1aa5
|
||||
var_16: &material_unit_tests_cache_key_short v4-angular-material
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
setup:
|
||||
@ -198,13 +203,6 @@ jobs:
|
||||
# Setup remote execution and run RBE-compatible tests.
|
||||
- *setup_bazel_remote_execution
|
||||
- run: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
|
||||
- run: mkdir ~/testlogs
|
||||
- run: cp -Lr dist/testlogs/* ~/testlogs
|
||||
- store_test_results:
|
||||
# Bazel always writes test.xml files under this directory
|
||||
path: ~/testlogs
|
||||
- store_artifacts:
|
||||
path: ~/testlogs
|
||||
|
||||
# Temporary job to test what will happen when we flip the Ivy flag to true
|
||||
test_ivy_aot:
|
||||
@ -240,11 +238,6 @@ jobs:
|
||||
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.br
|
||||
destination: core/todo/bundle.br
|
||||
|
||||
# This job is currently a PoC for running tests on SauceLabs via bazel. It runs a subset of the
|
||||
# tests in `legacy-unit-tests-saucelabs` (see
|
||||
# [BUILD.bazel](https://github.com/angular/angular/blob/ef44f51d5/BUILD.bazel#L66-L92)).
|
||||
#
|
||||
# NOTE: This is currently limited to master builds only. See the `default_workflow` configuration.
|
||||
test_saucelabs_bazel:
|
||||
<<: *job_defaults
|
||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
||||
@ -293,8 +286,6 @@ jobs:
|
||||
- run: yarn --cwd aio e2e --configuration=ci
|
||||
# Run PWA-score tests
|
||||
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
|
||||
# Run accessibility tests
|
||||
- run: yarn --cwd aio test-a11y-score-localhost
|
||||
# Check the bundle sizes.
|
||||
- run: yarn --cwd aio payload-size
|
||||
# Run unit tests for Firebase redirects
|
||||
@ -403,7 +394,7 @@ jobs:
|
||||
# Run examples tests with ivy. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
||||
# Since the parallelism is set to "3", there will be three parallel CircleCI containers
|
||||
# with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument.
|
||||
- run: yarn --cwd aio example-e2e --setup --local --ivy --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL}
|
||||
- run: yarn --cwd aio example-e2e --setup --local --ivy --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL}
|
||||
|
||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||
aio_preview:
|
||||
@ -620,55 +611,44 @@ jobs:
|
||||
resource_class: xlarge
|
||||
docker:
|
||||
- image: *browsers_docker_image
|
||||
# The Material unit tests support splitting the browsers across multiple CircleCI
|
||||
# instances. Since by default this job launches two browsers, we run each browser
|
||||
# in its own container instance.
|
||||
# https://github.com/angular/material2/blob/7baeaa797b19da2d2998f0d26f6fede3c8a13714/test/karma.conf.js#L107-L110
|
||||
parallelism: 2
|
||||
environment:
|
||||
# The Material unit tests also support launching the same browser multiple times by
|
||||
# sharding individual specs across the defined multiple instances.
|
||||
# See: https://github.com/angular/material2/blob/7baeaa797b19da2d2998f0d26f6fede3c8a13714/test/karma.conf.js#L113-L116
|
||||
KARMA_PARALLEL_BROWSERS: 3
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Although RBE is configured below for the Material repo, also setup RBE in the Angular repo
|
||||
# to provision Angular's GCP token into the environment variables.
|
||||
- *setup_bazel_remote_execution
|
||||
# Restore the cache before cloning the repository because the clone script re-uses
|
||||
# the restored repository if present. This reduces the amount of times the components
|
||||
# repository needs to be cloned (this is slow and increases based on commits in the repo).
|
||||
- restore_cache:
|
||||
keys:
|
||||
- *material_unit_tests_cache_key
|
||||
- *material_unit_tests_cache_key_short
|
||||
- run:
|
||||
name: "Fetching Material repository"
|
||||
name: "Cloning Material repository"
|
||||
command: ./scripts/ci/clone_angular_material_repo.sh
|
||||
- restore_cache:
|
||||
# Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable.
|
||||
# It needs to be hardcoded here, because env variables interpolation is not supported.
|
||||
keys:
|
||||
- v2-angular-material-{{ checksum "/tmp/material2/yarn.lock" }}
|
||||
- v2-angular-material-
|
||||
- run:
|
||||
# Run yarn install to fetch the Bazel binaries as used in the Material repo.
|
||||
name: Installing Material dependencies.
|
||||
command: yarn --cwd ${MATERIAL_REPO_TMP_DIR} install --frozen-lockfile --non-interactive
|
||||
# Save the cache before we run the Material unit tests script. This is necessary
|
||||
# because we don't want to cache the node modules which have been modified to contain
|
||||
# the attached Ivy package output.
|
||||
- save_cache:
|
||||
key: *material_unit_tests_cache_key
|
||||
# Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable.
|
||||
# It needs to be hardcoded here, because env variables interpolation is not supported.
|
||||
key: v2-angular-material-{{ checksum "/tmp/material2/yarn.lock" }}
|
||||
paths:
|
||||
# Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable.
|
||||
# It needs to be hardcoded here, because env variables interpolation is not supported.
|
||||
- "/tmp/material2"
|
||||
- run:
|
||||
name: "Setup Bazel RBE remote execution in Material repo"
|
||||
command: |
|
||||
./.circleci/setup-rbe.sh "${MATERIAL_REPO_TMP_DIR}/.bazelrc.user"
|
||||
- "/tmp/material2/node_modules"
|
||||
- run:
|
||||
name: "Running Material unit tests"
|
||||
command: ./scripts/ci/run_angular_material_unit_tests.sh
|
||||
|
||||
test_zonejs:
|
||||
<<: *job_defaults
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Install
|
||||
- run: yarn --cwd packages/zone.js install --frozen-lockfile --non-interactive
|
||||
# Run zone.js tools tests
|
||||
- run: yarn --cwd packages/zone.js promisetest
|
||||
- run: yarn --cwd packages/zone.js promisefinallytest
|
||||
- run: yarn bazel build //packages/zone.js:npm_package &&
|
||||
cp dist/bin/packages/zone.js/npm_package/dist/zone-mix.js ./packages/zone.js/test/extra/ &&
|
||||
cp dist/bin/packages/zone.js/npm_package/dist/zone-patch-electron.js ./packages/zone.js/test/extra/ &&
|
||||
yarn --cwd packages/zone.js electrontest
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
default_workflow:
|
||||
@ -676,41 +656,31 @@ workflows:
|
||||
- setup
|
||||
- lint:
|
||||
requires:
|
||||
- setup
|
||||
- setup
|
||||
- test:
|
||||
requires:
|
||||
- setup
|
||||
- setup
|
||||
- test_ivy_aot:
|
||||
requires:
|
||||
- setup
|
||||
- setup
|
||||
- build-npm-packages:
|
||||
requires:
|
||||
- setup
|
||||
- setup
|
||||
- build-ivy-npm-packages:
|
||||
requires:
|
||||
- setup
|
||||
- legacy-misc-tests:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- legacy-unit-tests-saucelabs:
|
||||
requires:
|
||||
- setup
|
||||
- test_saucelabs_bazel:
|
||||
requires:
|
||||
- setup
|
||||
# This job is currently a PoC and a subset of `legacy-unit-tests-saucelabs`. Running on
|
||||
# master only to avoid wasting resources.
|
||||
#
|
||||
# TODO: Run this job on all branches (including PRs) as soon as it is not a PoC.
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- setup
|
||||
- test_aio:
|
||||
requires:
|
||||
- setup
|
||||
- setup
|
||||
- legacy-unit-tests-saucelabs:
|
||||
requires:
|
||||
- setup
|
||||
- deploy_aio:
|
||||
requires:
|
||||
- test_aio
|
||||
- legacy-misc-tests:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- test_aio_local:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
@ -763,9 +733,22 @@ workflows:
|
||||
- material-unit-tests:
|
||||
requires:
|
||||
- build-ivy-npm-packages
|
||||
- test_zonejs:
|
||||
|
||||
saucelabs_tests:
|
||||
jobs:
|
||||
- setup
|
||||
- test_saucelabs_bazel:
|
||||
requires:
|
||||
- setup
|
||||
triggers:
|
||||
- schedule:
|
||||
# Runs the Saucelabs legacy tests every hour. We still want to run Saucelabs
|
||||
# frequently as the caretaker needs up-to-date results when merging PRs or creating
|
||||
# a new release. Also we primarily moved the Saucelabs job into a cronjob that doesn't
|
||||
# run for PRs, in order to ensure that PRs are not affected by Saucelabs flakiness or
|
||||
# incidents. This is still guaranteed (even if we run the job every hour).
|
||||
cron: "0 * * * *"
|
||||
filters: *publish_branches_filter
|
||||
|
||||
aio_monitoring:
|
||||
jobs:
|
||||
|
@ -77,9 +77,7 @@ setPublicVar SAUCE_READY_FILE_TIMEOUT 120
|
||||
# their separate build setups.
|
||||
setPublicVar MATERIAL_REPO_TMP_DIR "/tmp/material2"
|
||||
setPublicVar MATERIAL_REPO_URL "https://github.com/angular/material2.git"
|
||||
setPublicVar MATERIAL_REPO_BRANCH "master"
|
||||
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI "config.yml".
|
||||
setPublicVar MATERIAL_REPO_COMMIT "701302dc482d7e4b77990b24e3b5ab330bbf1aa5"
|
||||
setPublicVar MATERIAL_REPO_BRANCH "ivy-2019"
|
||||
|
||||
# Source `$BASH_ENV` to make the variables available immediately.
|
||||
source $BASH_ENV;
|
||||
|
Binary file not shown.
@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -e -o pipefail
|
||||
|
||||
# The path of the .bazelrc.user file to update should be passed as first parameter to this script.
|
||||
# This allows to setup RBE for both the Angular repo and the Material repo.
|
||||
bazelrc_user="$1"
|
||||
|
||||
echo "Writing RBE configuration to ${bazelrc_user}"
|
||||
|
||||
touch ${bazelrc_user}
|
||||
echo -e 'build --config=remote\n' >> ${bazelrc_user}
|
||||
echo -e 'build:remote --remote_accept_cached=true\n' >> ${bazelrc_user}
|
||||
echo "Reading from remote cache for bazel remote jobs."
|
||||
if [[ "$CI_PULL_REQUEST" == "false" ]]; then
|
||||
echo -e 'build:remote --remote_upload_local_results=true\n' >> ${bazelrc_user}
|
||||
echo "Uploading local build results to remote cache."
|
||||
else
|
||||
echo -e 'build:remote --remote_upload_local_results=false\n' >> ${bazelrc_user}
|
||||
echo "Not uploading local build results to remote cache."
|
||||
fi
|
@ -1,5 +1,3 @@
|
||||
# escape=`
|
||||
|
||||
ARG core=mcr.microsoft.com/windows/servercore:1809
|
||||
ARG target=mcr.microsoft.com/powershell:windowsservercore-1809
|
||||
|
||||
@ -12,57 +10,57 @@ SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPref
|
||||
|
||||
ENV GPG_VERSION 2.3.4
|
||||
|
||||
RUN Invoke-WebRequest $('https://files.gpg4win.org/gpg4win-vanilla-{0}.exe' -f $env:GPG_VERSION) -OutFile 'gpg4win.exe' -UseBasicParsing ; `
|
||||
RUN Invoke-WebRequest $('https://files.gpg4win.org/gpg4win-vanilla-{0}.exe' -f $env:GPG_VERSION) -OutFile 'gpg4win.exe' -UseBasicParsing ; \
|
||||
Start-Process .\gpg4win.exe -ArgumentList '/S' -NoNewWindow -Wait
|
||||
|
||||
RUN @( `
|
||||
'94AE36675C464D64BAFA68DD7434390BDBE9B9C5', `
|
||||
'FD3A5288F042B6850C66B31F09FE44734EB7990E', `
|
||||
'71DCFD284A79C3B38668286BC97EC7A07EDE3FC1', `
|
||||
'DD8F2338BAE7501E3DD5AC78C273792F7D83545D', `
|
||||
'C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8', `
|
||||
'B9AE9905FFD7803F25714661B63B535A4C206CA9', `
|
||||
'77984A986EBC2AA786BC0F66B01FBB92821C587A', `
|
||||
'8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600', `
|
||||
'4ED778F539E3634C779C87C6D7062848A1AB005C', `
|
||||
'A48C2BEE680E841632CD4E44F07496B3EB3C1762', `
|
||||
'B9E2F5981AA6E0CD28160D9FF13993A75599653C' `
|
||||
) | foreach { `
|
||||
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys $_ ; `
|
||||
RUN @( \
|
||||
'94AE36675C464D64BAFA68DD7434390BDBE9B9C5', \
|
||||
'FD3A5288F042B6850C66B31F09FE44734EB7990E', \
|
||||
'71DCFD284A79C3B38668286BC97EC7A07EDE3FC1', \
|
||||
'DD8F2338BAE7501E3DD5AC78C273792F7D83545D', \
|
||||
'C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8', \
|
||||
'B9AE9905FFD7803F25714661B63B535A4C206CA9', \
|
||||
'77984A986EBC2AA786BC0F66B01FBB92821C587A', \
|
||||
'8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600', \
|
||||
'4ED778F539E3634C779C87C6D7062848A1AB005C', \
|
||||
'A48C2BEE680E841632CD4E44F07496B3EB3C1762', \
|
||||
'B9E2F5981AA6E0CD28160D9FF13993A75599653C' \
|
||||
) | foreach { \
|
||||
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys $_ ; \
|
||||
}
|
||||
|
||||
ENV NODE_VERSION=$node_version
|
||||
|
||||
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/SHASUMS256.txt.asc' -f $env:NODE_VERSION) -OutFile 'SHASUMS256.txt.asc' -UseBasicParsing ; `
|
||||
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/SHASUMS256.txt.asc' -f $env:NODE_VERSION) -OutFile 'SHASUMS256.txt.asc' -UseBasicParsing ; \
|
||||
gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc
|
||||
|
||||
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/node-v{0}-win-x64.zip' -f $env:NODE_VERSION) -OutFile 'node.zip' -UseBasicParsing ; `
|
||||
$sum = $(cat SHASUMS256.txt.asc | sls $(' node-v{0}-win-x64.zip' -f $env:NODE_VERSION)) -Split ' ' ; `
|
||||
if ((Get-FileHash node.zip -Algorithm sha256).Hash -ne $sum[0]) { Write-Error 'SHA256 mismatch' } ; `
|
||||
Expand-Archive node.zip -DestinationPath C:\ ; `
|
||||
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/node-v{0}-win-x64.zip' -f $env:NODE_VERSION) -OutFile 'node.zip' -UseBasicParsing ; \
|
||||
$sum = $(cat SHASUMS256.txt.asc | sls $(' node-v{0}-win-x64.zip' -f $env:NODE_VERSION)) -Split ' ' ; \
|
||||
if ((Get-FileHash node.zip -Algorithm sha256).Hash -ne $sum[0]) { Write-Error 'SHA256 mismatch' } ; \
|
||||
Expand-Archive node.zip -DestinationPath C:\ ; \
|
||||
Rename-Item -Path $('C:\node-v{0}-win-x64' -f $env:NODE_VERSION) -NewName 'C:\nodejs'
|
||||
|
||||
ENV YARN_VERSION=$yarn_version
|
||||
|
||||
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; `
|
||||
Invoke-WebRequest $('https://yarnpkg.com/downloads/{0}/yarn-{0}.msi' -f $env:YARN_VERSION) -OutFile yarn.msi -UseBasicParsing ; `
|
||||
$sig = Get-AuthenticodeSignature yarn.msi ; `
|
||||
if ($sig.Status -ne 'Valid') { Write-Error 'Authenticode signature is not valid' } ; `
|
||||
Write-Output $sig.SignerCertificate.Thumbprint ; `
|
||||
if (@( `
|
||||
'7E253367F8A102A91D04829E37F3410F14B68A5F', `
|
||||
'AF764E1EA56C762617BDC757C8B0F3780A0CF5F9' `
|
||||
) -notcontains $sig.SignerCertificate.Thumbprint) { Write-Error 'Unknown signer certificate' } ; `
|
||||
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; \
|
||||
Invoke-WebRequest $('https://yarnpkg.com/downloads/{0}/yarn-{0}.msi' -f $env:YARN_VERSION) -OutFile yarn.msi -UseBasicParsing ; \
|
||||
$sig = Get-AuthenticodeSignature yarn.msi ; \
|
||||
if ($sig.Status -ne 'Valid') { Write-Error 'Authenticode signature is not valid' } ; \
|
||||
Write-Output $sig.SignerCertificate.Thumbprint ; \
|
||||
if (@( \
|
||||
'7E253367F8A102A91D04829E37F3410F14B68A5F', \
|
||||
'AF764E1EA56C762617BDC757C8B0F3780A0CF5F9' \
|
||||
) -notcontains $sig.SignerCertificate.Thumbprint) { Write-Error 'Unknown signer certificate' } ; \
|
||||
Start-Process msiexec.exe -ArgumentList '/i', 'yarn.msi', '/quiet', '/norestart' -NoNewWindow -Wait
|
||||
|
||||
ENV GIT_VERSION 2.20.1
|
||||
ENV GIT_DOWNLOAD_URL https://github.com/git-for-windows/git/releases/download/v${GIT_VERSION}.windows.1/MinGit-${GIT_VERSION}-busybox-64-bit.zip
|
||||
ENV GIT_SHA256 9817ab455d9cbd0b09d8664b4afbe4bbf78d18b556b3541d09238501a749486c
|
||||
|
||||
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; `
|
||||
Invoke-WebRequest -UseBasicParsing $env:GIT_DOWNLOAD_URL -OutFile git.zip; `
|
||||
if ((Get-FileHash git.zip -Algorithm sha256).Hash -ne $env:GIT_SHA256) {exit 1} ; `
|
||||
Expand-Archive git.zip -DestinationPath C:\git; `
|
||||
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; \
|
||||
Invoke-WebRequest -UseBasicParsing $env:GIT_DOWNLOAD_URL -OutFile git.zip; \
|
||||
if ((Get-FileHash git.zip -Algorithm sha256).Hash -ne $env:GIT_SHA256) {exit 1} ; \
|
||||
Expand-Archive git.zip -DestinationPath C:\git; \
|
||||
Remove-Item git.zip
|
||||
|
||||
FROM $target as baseimage
|
||||
@ -76,51 +74,30 @@ COPY --from=download /git /git
|
||||
ARG SETX=/M
|
||||
RUN setx %SETX% PATH "%PATH%;C:\nodejs;C:\yarn\bin;C:\git\cmd;C:\git\mingw64\bin;C:\git\usr\bin"
|
||||
|
||||
CMD [ "node.exe" ]
|
||||
CMD [ "node.exe" ]
|
||||
|
||||
FROM baseimage
|
||||
|
||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
||||
|
||||
# Install Bazel prereqs on Windows (https://docs.bazel.build/versions/master/install-windows.html)
|
||||
RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe; \
|
||||
Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait; \
|
||||
Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz; \
|
||||
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait; \
|
||||
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait; \
|
||||
Remove-Item msys2.tar.xz; \
|
||||
Remove-Item msys2.tar; \
|
||||
Remove-Item 7z.exe; \
|
||||
Remove-Item -Recurse 7zip; \
|
||||
[Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine); \
|
||||
[Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine); \
|
||||
Invoke-WebRequest -UseBasicParsing 'https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe' -OutFile vc_redist.x64.exe; \
|
||||
Start-Process 'c:\\vc_redist.x64.exe' -ArgumentList '/Install', '/Passive', '/NoRestart' -NoNewWindow -Wait; \
|
||||
Remove-Item vc_redist.x64.exe
|
||||
|
||||
# Install MSYS2
|
||||
RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe; `
|
||||
Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait; `
|
||||
Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz; `
|
||||
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait; `
|
||||
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait; `
|
||||
Remove-Item msys2.tar.xz; `
|
||||
Remove-Item msys2.tar; `
|
||||
Remove-Item 7z.exe; `
|
||||
Remove-Item -Recurse 7zip; `
|
||||
[Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine); `
|
||||
[Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine)
|
||||
# Add a fix for https://github.com/docker/for-win/issues/2920 as entry point to the container.
|
||||
SHELL ["cmd", "/c"]
|
||||
COPY "fix-msys64.cmd" "C:\\fix-msys64.cmd"
|
||||
ENTRYPOINT cmd /C C:\\fix-msys64.cmd && cmd /c
|
||||
|
||||
# Install MSYS2 packages
|
||||
RUN C:\msys64\usr\bin\bash.exe -l -c \"pacman --needed --noconfirm -S zip unzip patch diffutils git\"
|
||||
|
||||
# Install VS Build Tools (required to build C++ targets)
|
||||
RUN Invoke-WebRequest -UseBasicParsing https://download.visualstudio.microsoft.com/download/pr/df649173-11e9-4af2-8eb7-0eb02ba8958a/cadb5bdac41e55bb8f6a6b7c45273370/vs_buildtools.exe -OutFile vs_BuildTools.exe; `
|
||||
# Installer won't detect DOTNET_SKIP_FIRST_TIME_EXPERIENCE if ENV is used, must use setx /M
|
||||
setx /M DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1; `
|
||||
Start-Process vs_BuildTools.exe `
|
||||
-ArgumentList `
|
||||
'--add', 'Microsoft.VisualStudio.Workload.VCTools', `
|
||||
'--add', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', `
|
||||
'--add', 'Microsoft.Component.VC.Runtime.UCRTSDK', `
|
||||
'--add', 'Microsoft.VisualStudio.Component.Windows10SDK.17763', `
|
||||
'--quiet', '--norestart', '--nocache' `
|
||||
-NoNewWindow -Wait; `
|
||||
Remove-Item -Force vs_buildtools.exe; `
|
||||
Remove-Item -Force -Recurse \"${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\"; `
|
||||
Remove-Item -Force -Recurse ${Env:TEMP}\*; `
|
||||
Remove-Item -Force -Recurse \"${Env:ProgramData}\Package Cache\"; `
|
||||
[Environment]::SetEnvironmentVariable('BAZEL_VC', \"${Env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\BuildTools\VC\", [System.EnvironmentVariableTarget]::Machine)
|
||||
|
||||
# Install Python (required to build Python targets)
|
||||
RUN Invoke-WebRequest -UseBasicParsing https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile python-3.5.1.exe; `
|
||||
Start-Process python-3.5.1.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait; `
|
||||
Remove-Item -Force python-3.5.1.exe
|
||||
|
||||
CMD ["cmd.exe"]
|
||||
CMD ["cmd.exe"]
|
@ -2,17 +2,11 @@
|
||||
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
|
||||
# See documentation in /docs/BAZEL.md
|
||||
|
||||
# Save built files and downloaded repositories in a location that can be cached by CodeFresh and
|
||||
# shared between builds. This helps speed up the analysis time significantly with Bazel managed node
|
||||
# dependencies on the CI.
|
||||
# Save downloaded repositories in a location that can be cached by CodeFresh and shared between
|
||||
# builds. This helps speed up the analysis time significantly with Bazel managed node dependencies
|
||||
# on the CI.
|
||||
# https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines/#caching-the-artifacts-of-your-build-system
|
||||
build --repository_cache=C:/codefresh/volume/bazel_repository_cache
|
||||
# Setting the output_base to a Docker volume is currently broken because of a Docker bug on Windows:
|
||||
# https://github.com/moby/moby/issues/37024
|
||||
# This affects Bazel because bazel_output_base\external\bazel_tools is an absolute path junction.
|
||||
# When its fixed we can uncomment this line, and use a different output_base for Ivy tests (they
|
||||
# use a separate compiler and destructively replace the cache).
|
||||
# startup --output_base=C:/codefresh/volume/bazel_output_base
|
||||
|
||||
# Don't be spammy in the logs
|
||||
# TODO(gmagolan): Hide progress again once build performance improves
|
||||
@ -29,10 +23,20 @@ build --announce_rc
|
||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||
# Limit Bazel to consuming resources that fit in CodeFresh VMs
|
||||
# TODO(filipesilva): determine the correct memory limit
|
||||
build --local_resources=10240,8.0,1.0
|
||||
build --local_resources=8000,8.0,1.0
|
||||
|
||||
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
|
||||
test --flaky_test_attempts=2
|
||||
|
||||
# More details on failures
|
||||
build --verbose_failures=true
|
||||
|
||||
# Include PATH in Windows build/tests
|
||||
# https://github.com/bazelbuild/rules_typescript/pull/356
|
||||
build --action_env=PATH
|
||||
test --action_env=PATH --test_env=PATH
|
||||
|
||||
# Exclude tests known to not work on Windows.
|
||||
|
||||
# Chrome web tests are currently broken.
|
||||
test --test_tag_filters=-browser:chromium-local
|
||||
|
@ -2,7 +2,6 @@ version: '1.0'
|
||||
|
||||
steps:
|
||||
BuildImage:
|
||||
title: Build Docker image
|
||||
type: build
|
||||
image_name: node-bazel-windows
|
||||
working_directory: ./.codefresh
|
||||
@ -13,7 +12,7 @@ steps:
|
||||
dockerfile: ./Dockerfile.win-1809
|
||||
|
||||
RunTests:
|
||||
title: Run Bazel tests
|
||||
title: Run Example
|
||||
image: ${{BuildImage}}
|
||||
commands:
|
||||
# Install dependencies
|
||||
@ -21,8 +20,7 @@ steps:
|
||||
# Add Bazel CI config
|
||||
- copy .codefresh\bazel.rc %ProgramData%\bazel.bazelrc
|
||||
# Run tests
|
||||
# At the moment 'browser:chromium-local' are broken in CI while locally they work
|
||||
# VE
|
||||
- yarn bazel test --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only,-browser:chromium-local //...
|
||||
# Ivy
|
||||
- yarn bazel test --define=compile=aot --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot --test_tag_filters=-no-ivy-aot,-fixme-ivy-aot,-browser:chromium-local //...
|
||||
- yarn bazel test //tools/ts-api-guardian:all //packages/language-service/test //packages/compiler/test //packages/compiler-cli/test:ngc //packages/compiler-cli/test/ngtsc:ngtsc
|
||||
- yarn test-ivy-aot //packages/animations/test //packages/common/test //packages/forms/test //packages/http/test //packages/platform-browser/test //packages/platform-browser-dynamic/test //packages/router/test
|
||||
- yarn bazel test //tools/public_api_guard/...
|
||||
- yarn bazel test //packages/compiler-cli/integrationtest:integrationtest //packages/compiler-cli/test/compliance:compliance
|
||||
|
6
.codefresh/fix-msys64.cmd
Normal file
6
.codefresh/fix-msys64.cmd
Normal file
@ -0,0 +1,6 @@
|
||||
@echo off
|
||||
REM Fix for https://github.com/docker/for-win/issues/2920
|
||||
REM echo "Fixing msys64 folder..."
|
||||
REM Touch all .dll files inside C:\msys64\
|
||||
forfiles /p C:\msys64\ /s /m *.dll /c "cmd /c Copy /B @path+,, >NUL"
|
||||
REM echo "Fixed msys64 folder."
|
31
BUILD.bazel
31
BUILD.bazel
@ -18,15 +18,15 @@ filegroup(
|
||||
name = "web_test_bootstrap_scripts",
|
||||
# do not sort
|
||||
srcs = [
|
||||
"@npm//:node_modules/core-js/client/core.js",
|
||||
"@npm//:node_modules/zone.js/dist/zone.js",
|
||||
"@npm//:node_modules/zone.js/dist/zone-testing.js",
|
||||
"@npm//:node_modules/zone.js/dist/task-tracking.js",
|
||||
"@npm//node_modules/core-js:client/core.js",
|
||||
"@npm//node_modules/zone.js:dist/zone.js",
|
||||
"@npm//node_modules/zone.js:dist/zone-testing.js",
|
||||
"@npm//node_modules/zone.js:dist/task-tracking.js",
|
||||
"//:test-events.js",
|
||||
"//:shims_for_IE.js",
|
||||
# Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
"@npm//:node_modules/systemjs/dist/system.src.js",
|
||||
"@npm//:node_modules/reflect-metadata/Reflect.js",
|
||||
"@npm//node_modules/systemjs:dist/system.src.js",
|
||||
"@npm//node_modules/reflect-metadata:Reflect.js",
|
||||
],
|
||||
)
|
||||
|
||||
@ -35,15 +35,15 @@ filegroup(
|
||||
srcs = [
|
||||
# We also declare the unminfied 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-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: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-mocks:angular-mocks.js",
|
||||
"@npm//node_modules/angular-mocks-1.5:angular-mocks.js",
|
||||
"@npm//node_modules/angular-mocks-1.6:angular-mocks.js",
|
||||
],
|
||||
)
|
||||
|
||||
@ -74,7 +74,6 @@ karma_web_test(
|
||||
"//packages/core/test:test_lib",
|
||||
"//packages/forms/test:test_lib",
|
||||
"//packages/http/test:test_lib",
|
||||
"//packages/zone.js/test:karma_jasmine_test_ci",
|
||||
# "//packages/router/test:test_lib",
|
||||
# //packages/router/test:test_lib fails with:
|
||||
# IE 11.0.0 (Windows 8.1.0.0) bootstrap should restore the scrolling position FAILED
|
||||
|
381
CHANGELOG.md
381
CHANGELOG.md
@ -1,140 +1,3 @@
|
||||
<a name="8.2.0-rc.0"></a>
|
||||
# [8.2.0-rc.0](https://github.com/angular/angular/compare/8.2.0-next.2...8.2.0-rc.0) (2019-07-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** increase memory limit of ngc under bazel from 2 to 4 GB ([#31784](https://github.com/angular/angular/issues/31784)) ([5a8eb92](https://github.com/angular/angular/commit/5a8eb92))
|
||||
* **core:** allow Z variations of CSS transforms in sanitizer ([#29264](https://github.com/angular/angular/issues/29264)) ([78e7fdd](https://github.com/angular/angular/commit/78e7fdd))
|
||||
* **elements:** handle falsy initial value ([#31604](https://github.com/angular/angular/issues/31604)) ([7151eae](https://github.com/angular/angular/commit/7151eae)), closes [angular/angular#30834](https://github.com/angular/angular/issues/30834)
|
||||
* **platform-browser:** debug element query predicates not compatible with strictFunctionTypes ([#30993](https://github.com/angular/angular/issues/30993)) ([10a1e19](https://github.com/angular/angular/commit/10a1e19))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **bazel:** compile targets used for indexing by Kythe with Ivy ([#31786](https://github.com/angular/angular/issues/31786)) ([82055b2](https://github.com/angular/angular/commit/82055b2))
|
||||
* **upgrade:** support $element in upgraded component template/templateUrl functions ([#31637](https://github.com/angular/angular/issues/31637)) ([29e1c53](https://github.com/angular/angular/commit/29e1c53))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **compiler:** avoid copying from prototype while cloning an object ([#31638](https://github.com/angular/angular/issues/31638)) ([24ca582](https://github.com/angular/angular/commit/24ca582)), closes [#31627](https://github.com/angular/angular/issues/31627)
|
||||
|
||||
|
||||
<a name="8.1.3"></a>
|
||||
## [8.1.3](https://github.com/angular/angular/compare/8.1.2...8.1.3) (2019-07-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **elements:** handle falsy initial value ([#31604](https://github.com/angular/angular/issues/31604)) ([434b796](https://github.com/angular/angular/commit/434b796)), closes [angular/angular#30834](https://github.com/angular/angular/issues/30834)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **compiler:** avoid copying from prototype while cloning an object ([#31638](https://github.com/angular/angular/issues/31638)) ([1f3daa0](https://github.com/angular/angular/commit/1f3daa0)), closes [#31627](https://github.com/angular/angular/issues/31627)
|
||||
|
||||
|
||||
<a name="8.2.0-next.2"></a>
|
||||
# [8.2.0-next.2](https://github.com/angular/angular/compare/8.2.0-next.1...8.2.0-next.2) (2019-07-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use the correct WTF array to iterate over ([#31208](https://github.com/angular/angular/issues/31208)) ([9204de9](https://github.com/angular/angular/commit/9204de9))
|
||||
* **bazel:** pass custom bazel compiler host rather than rewriting one ([#31496](https://github.com/angular/angular/issues/31496)) ([0c61a35](https://github.com/angular/angular/commit/0c61a35))
|
||||
* **compiler-cli:** Return original sourceFile instead of redirected sourceFile from getSourceFile ([#26036](https://github.com/angular/angular/issues/26036)) ([3166cff](https://github.com/angular/angular/commit/3166cff)), closes [#22524](https://github.com/angular/angular/issues/22524)
|
||||
* **language-service:** Eagarly initialize data members ([#31577](https://github.com/angular/angular/issues/31577)) ([0110de2](https://github.com/angular/angular/commit/0110de2))
|
||||
|
||||
|
||||
|
||||
<a name="8.1.2"></a>
|
||||
## [8.1.2](https://github.com/angular/angular/compare/8.1.0...8.1.2) (2019-07-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use the correct WTF array to iterate over ([#31208](https://github.com/angular/angular/issues/31208)) ([4aed480](https://github.com/angular/angular/commit/4aed480))
|
||||
* **compiler-cli:** Return original sourceFile instead of redirected sourceFile from getSourceFile ([#26036](https://github.com/angular/angular/issues/26036)) ([13dbb98](https://github.com/angular/angular/commit/13dbb98)), closes [#22524](https://github.com/angular/angular/issues/22524)
|
||||
* **core:** export provider interfaces that are part of the public API types ([#31377](https://github.com/angular/angular/issues/31377)) ([bebf089](https://github.com/angular/angular/commit/bebf089)), closes [/github.com/angular/angular/pull/31377#discussion_r299254408](https://github.com//github.com/angular/angular/pull/31377/issues/discussion_r299254408) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L365-L366](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L365-L366) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L283-L284](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L283-L284) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts#L23](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts/issues/L23)
|
||||
|
||||
|
||||
|
||||
<a name="8.2.0-next.1"></a>
|
||||
# [8.2.0-next.1](https://github.com/angular/angular/compare/8.2.0-next.0...8.2.0-next.1) (2019-07-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** revert location of xi18n outputs to bazel-genfiles ([#31410](https://github.com/angular/angular/issues/31410)) ([1d3e227](https://github.com/angular/angular/commit/1d3e227))
|
||||
* **compiler:** give ASTWithSource its own visit method ([#31347](https://github.com/angular/angular/issues/31347)) ([6aaca21](https://github.com/angular/angular/commit/6aaca21))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** add automatic migration from Renderer to Renderer2 ([#30936](https://github.com/angular/angular/issues/30936)) ([c095597](https://github.com/angular/angular/commit/c095597))
|
||||
|
||||
|
||||
|
||||
<a name="8.1.1"></a>
|
||||
## [8.1.1](https://github.com/angular/angular/compare/8.1.0...8.1.1) (2019-07-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** export provider interfaces that are part of the public API types ([#31377](https://github.com/angular/angular/issues/31377)) ([bebf089](https://github.com/angular/angular/commit/bebf089)), closes [/github.com/angular/angular/pull/31377#discussion_r299254408](https://github.com//github.com/angular/angular/pull/31377/issues/discussion_r299254408) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L365-L366](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L365-L366) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts#L283-L284](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/interface/provider.ts/issues/L283-L284) [/github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts#L23](https://github.com//github.com/angular/angular/blob/9e34670b2/packages/core/src/di/index.ts/issues/L23)
|
||||
|
||||
|
||||
<a name="8.2.0-next.0"></a>
|
||||
# [8.2.0-next.0](https://github.com/angular/angular/compare/8.1.0-rc.0...8.2.0-next.0) (2019-07-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** handle `undefined` meta in `injectArgs` ([#31333](https://github.com/angular/angular/issues/31333)) ([80ccd6c](https://github.com/angular/angular/commit/80ccd6c)), closes [CLI #14888](https://github.com/angular/angular-cli/issues/14888)
|
||||
* **service-worker:** cache opaque responses in data groups with `freshness` strategy ([#30977](https://github.com/angular/angular/issues/30977)) ([d7be38f](https://github.com/angular/angular/commit/d7be38f)), closes [#30968](https://github.com/angular/angular/issues/30968)
|
||||
* **service-worker:** cache opaque responses when requests exceeds timeout threshold ([#30977](https://github.com/angular/angular/issues/30977)) ([93abc35](https://github.com/angular/angular/commit/93abc35))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **bazel:** allow passing a custom bazel compiler host to ngc compile ([#31341](https://github.com/angular/angular/issues/31341)) ([a29dc96](https://github.com/angular/angular/commit/a29dc96))
|
||||
* **bazel:** allow passing and rewriting an old bazel host ([#31381](https://github.com/angular/angular/issues/31381)) ([11a208f](https://github.com/angular/angular/commit/11a208f)), closes [#31341](https://github.com/angular/angular/issues/31341)
|
||||
|
||||
|
||||
|
||||
<a name="8.1.0"></a>
|
||||
# [8.1.0](https://github.com/angular/angular/compare/8.1.0-rc.0...8.1.0) (2019-07-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** handle `undefined` meta in `injectArgs` ([#31333](https://github.com/angular/angular/issues/31333)) ([80ccd6c](https://github.com/angular/angular/commit/80ccd6c)), closes [CLI #14888](https://github.com/angular/angular-cli/issues/14888)
|
||||
* **service-worker:** cache opaque responses in data groups with `freshness` strategy ([#30977](https://github.com/angular/angular/issues/30977)) ([b0c3453](https://github.com/angular/angular/commit/b0c3453)), closes [#30968](https://github.com/angular/angular/issues/30968)
|
||||
* **service-worker:** cache opaque responses when requests exceeds timeout threshold ([#30977](https://github.com/angular/angular/issues/30977)) ([a9038ef](https://github.com/angular/angular/commit/a9038ef))
|
||||
|
||||
|
||||
|
||||
<a name="8.1.0-rc.0"></a>
|
||||
# [8.1.0-rc.0](https://github.com/angular/angular/compare/8.1.0-next.3...8.1.0-rc.0) (2019-06-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** exclude all angular schematics folders from metadata build ([#31237](https://github.com/angular/angular/issues/31237)) ([16717fa](https://github.com/angular/angular/commit/16717fa)), closes [#31235](https://github.com/angular/angular/issues/31235)
|
||||
* **bazel:** remove unsupported Css pre-processors from ng new ([#31234](https://github.com/angular/angular/issues/31234)) ([e83667a](https://github.com/angular/angular/commit/e83667a)), closes [#31209](https://github.com/angular/angular/issues/31209)
|
||||
* **bazel:** update ng new schema to match the current ng new schema of [@schematics](https://github.com/schematics)/angular ([#31234](https://github.com/angular/angular/issues/31234)) ([805fc86](https://github.com/angular/angular/commit/805fc86)), closes [#31233](https://github.com/angular/angular/issues/31233)
|
||||
* **compiler:** fix Elements not making a new ParseSourceSpan ([#31190](https://github.com/angular/angular/issues/31190)) ([7035f22](https://github.com/angular/angular/commit/7035f22))
|
||||
* **compiler:** stringify `Object.create(null)` tokens ([#16848](https://github.com/angular/angular/issues/16848)) ([5e53956](https://github.com/angular/angular/commit/5e53956))
|
||||
* **service-worker:** registration failed on Safari ([#31140](https://github.com/angular/angular/issues/31140)) ([a5dd4ed](https://github.com/angular/angular/commit/a5dd4ed)), closes [#31061](https://github.com/angular/angular/issues/31061)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **upgrade:** provide unit test helpers for wiring up injectors ([#16848](https://github.com/angular/angular/issues/16848)) ([3fb78aa](https://github.com/angular/angular/commit/3fb78aa))
|
||||
|
||||
|
||||
|
||||
<a name="8.0.3"></a>
|
||||
## [8.0.3](https://github.com/angular/angular/compare/8.0.2...8.0.3) (2019-06-26)
|
||||
|
||||
@ -148,18 +11,6 @@
|
||||
|
||||
|
||||
|
||||
<a name="8.1.0-next.3"></a>
|
||||
# [8.1.0-next.3](https://github.com/angular/angular/compare/8.1.0-next.2...8.1.0-next.3) (2019-06-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** builder workspace should use nodejs v10.16.0 ([#31088](https://github.com/angular/angular/issues/31088)) ([a1fc4de](https://github.com/angular/angular/commit/a1fc4de))
|
||||
* **core:** temporarily remove [@deprecated](https://github.com/deprecated) jsdoc tag for a TextBedStatic.get overload ([#30714](https://github.com/angular/angular/issues/30714)) ([6bc9c78](https://github.com/angular/angular/commit/6bc9c78)), closes [#30514](https://github.com/angular/angular/issues/30514)
|
||||
* **language-service:** Remove 'any' in getQuickInfoAtPosition ([#31014](https://github.com/angular/angular/issues/31014)) ([a4601ec](https://github.com/angular/angular/commit/a4601ec))
|
||||
|
||||
|
||||
|
||||
<a name="8.0.2"></a>
|
||||
## [8.0.2](https://github.com/angular/angular/compare/8.0.1...8.0.2) (2019-06-19)
|
||||
|
||||
@ -172,20 +23,6 @@
|
||||
|
||||
|
||||
|
||||
<a name="8.1.0-next.2"></a>
|
||||
# [8.1.0-next.2](https://github.com/angular/angular/compare/8.1.0-next.1...8.1.0-next.2) (2019-06-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** do not modify tsconfig.json ([#30877](https://github.com/angular/angular/issues/30877)) ([b086676](https://github.com/angular/angular/commit/b086676))
|
||||
* **bazel:** exclude components schematics from build ([#30825](https://github.com/angular/angular/issues/30825)) ([05a43ca](https://github.com/angular/angular/commit/05a43ca))
|
||||
* **bazel:** Load global stylesheet in dev and prod ([#30879](https://github.com/angular/angular/issues/30879)) ([17bfedd](https://github.com/angular/angular/commit/17bfedd))
|
||||
* **common:** expose the `HttpUploadProgressEvent` interface as public API ([#30852](https://github.com/angular/angular/issues/30852)) ([5c18f23](https://github.com/angular/angular/commit/5c18f23)), closes [#30814](https://github.com/angular/angular/issues/30814)
|
||||
* **service-worker:** avoid uncaught rejection warning when registration fails ([#30876](https://github.com/angular/angular/issues/30876)) ([81c2a94](https://github.com/angular/angular/commit/81c2a94))
|
||||
|
||||
|
||||
|
||||
<a name="8.0.1"></a>
|
||||
## [8.0.1](https://github.com/angular/angular/compare/8.0.0...8.0.1) (2019-06-13)
|
||||
|
||||
@ -200,83 +37,24 @@
|
||||
|
||||
|
||||
|
||||
<a name="8.1.0-next.1"></a>
|
||||
# [8.1.0-next.1](https://github.com/angular/angular/compare/8.1.0-beta.0...8.1.0-next.1) (2019-06-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** TypeScript related migrations should cater for BOM ([#30719](https://github.com/angular/angular/issues/30719)) ([80394ce](https://github.com/angular/angular/commit/80394ce)), closes [/github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/src/tree/recorder.ts#L72](https://github.com//github.com/angular/angular-cli/blob/master/packages/angular_devkit/schematics/src/tree/recorder.ts/issues/L72) [#30713](https://github.com/angular/angular/issues/30713)
|
||||
|
||||
|
||||
|
||||
<a name="8.1.0-beta.0"></a>
|
||||
# [8.1.0-beta.0](https://github.com/angular/angular/compare/8.0.0...8.1.0-beta.0) (2019-05-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** allow ts_library interop with list-typed inputs ([#30600](https://github.com/angular/angular/issues/30600)) ([3125376](https://github.com/angular/angular/commit/3125376))
|
||||
* **bazel:** Bump ibazel to 0.10.1 for windows fixes ([#30196](https://github.com/angular/angular/issues/30196)) ([1353bf0](https://github.com/angular/angular/commit/1353bf0))
|
||||
* **bazel:** Directly spawn native Bazel binary ([#30306](https://github.com/angular/angular/issues/30306)) ([2a0f497](https://github.com/angular/angular/commit/2a0f497))
|
||||
* **bazel:** Disable sandbox on Mac OS ([#30460](https://github.com/angular/angular/issues/30460)) ([b6b1aec](https://github.com/angular/angular/commit/b6b1aec))
|
||||
* **bazel:** Exclude common/upgrade* in metadata.tsconfig.json ([#30133](https://github.com/angular/angular/issues/30133)) ([1f4c380](https://github.com/angular/angular/commit/1f4c380))
|
||||
* **bazel:** ng test should run specific ts_web_test_suite ([#30526](https://github.com/angular/angular/issues/30526)) ([e688e02](https://github.com/angular/angular/commit/e688e02))
|
||||
* **bazel:** pass correct arguments to http_server in Windows ([#30346](https://github.com/angular/angular/issues/30346)) ([3aff79c](https://github.com/angular/angular/commit/3aff79c)), closes [#29785](https://github.com/angular/angular/issues/29785)
|
||||
* **bazel:** update peerDep ranges ([#30155](https://github.com/angular/angular/issues/30155)) ([4ae0ee8](https://github.com/angular/angular/commit/4ae0ee8))
|
||||
* **bazel:** Use existing npm/yarn lock files ([#30438](https://github.com/angular/angular/issues/30438)) ([ff29ccc](https://github.com/angular/angular/commit/ff29ccc))
|
||||
* **compiler-cli:** log ngcc skipping messages as debug instead of info ([#30232](https://github.com/angular/angular/issues/30232)) ([60a8888](https://github.com/angular/angular/commit/60a8888))
|
||||
* **core:** consistently use ng:/// for sourcemap URLs ([#29826](https://github.com/angular/angular/issues/29826)) ([392473e](https://github.com/angular/angular/commit/392473e))
|
||||
* **core:** CSS sanitizer now allows parens in file names ([#30322](https://github.com/angular/angular/issues/30322)) ([728db88](https://github.com/angular/angular/commit/728db88))
|
||||
* **core:** fix interpolate identifier in AOT ([#30243](https://github.com/angular/angular/issues/30243)) ([30d1f29](https://github.com/angular/angular/commit/30d1f29))
|
||||
* **core:** migrations not always migrating all files ([#30269](https://github.com/angular/angular/issues/30269)) ([349935a](https://github.com/angular/angular/commit/349935a))
|
||||
* **core:** remove deprecated `TestBed.deprecatedOverrideProvider` API ([#30576](https://github.com/angular/angular/issues/30576)) ([a96976e](https://github.com/angular/angular/commit/a96976e))
|
||||
* **core:** require 'static' flag on queries in typings ([#30639](https://github.com/angular/angular/issues/30639)) ([84dd267](https://github.com/angular/angular/commit/84dd267))
|
||||
* **core:** static-query migration errors not printed properly ([#30458](https://github.com/angular/angular/issues/30458)) ([6ceb903](https://github.com/angular/angular/commit/6ceb903))
|
||||
* **core:** static-query migration fails with default parameter values ([#30269](https://github.com/angular/angular/issues/30269)) ([6357d4a](https://github.com/angular/angular/commit/6357d4a))
|
||||
* **core:** static-query migration should gracefully exit if AOT compiler throws ([#30269](https://github.com/angular/angular/issues/30269)) ([509352f](https://github.com/angular/angular/commit/509352f))
|
||||
* **core:** static-query migration should handle queries on accessors ([#30327](https://github.com/angular/angular/issues/30327)) ([0ffdb48](https://github.com/angular/angular/commit/0ffdb48))
|
||||
* **core:** static-query migration should not fallback to test strategy ([#30458](https://github.com/angular/angular/issues/30458)) ([0cdf598](https://github.com/angular/angular/commit/0cdf598))
|
||||
* **core:** static-query migration should not prompt if no queries are used ([#30254](https://github.com/angular/angular/issues/30254)) ([4c12d74](https://github.com/angular/angular/commit/4c12d74))
|
||||
* **core:** static-query usage migration strategy should detect ambiguous query usage ([#30215](https://github.com/angular/angular/issues/30215)) ([8d3365e](https://github.com/angular/angular/commit/8d3365e))
|
||||
* **core:** temporarily remove [@deprecated](https://github.com/deprecated) jsdoc tag for a TextBed.get overload ([#30514](https://github.com/angular/angular/issues/30514)) ([f6bf892](https://github.com/angular/angular/commit/f6bf892)), closes [#29290](https://github.com/angular/angular/issues/29290) [#29905](https://github.com/angular/angular/issues/29905)
|
||||
* **language-service:** Remove tsserverlibrary from rollup globals ([#30123](https://github.com/angular/angular/issues/30123)) ([124e497](https://github.com/angular/angular/commit/124e497))
|
||||
* **router:** ensure `history.state` is set in `eager` update mode ([#30154](https://github.com/angular/angular/issues/30154)) ([b40f6f3](https://github.com/angular/angular/commit/b40f6f3))
|
||||
* **router:** ensure navigations start with the current URL value incase redirect is skipped ([#30344](https://github.com/angular/angular/issues/30344)) ([0fd9d08](https://github.com/angular/angular/commit/0fd9d08)), closes [#30340](https://github.com/angular/angular/issues/30340) [#30160](https://github.com/angular/angular/issues/30160)
|
||||
* **router:** fix a problem with router not responding to back button ([#30160](https://github.com/angular/angular/issues/30160)) ([3327bd8](https://github.com/angular/angular/commit/3327bd8))
|
||||
* **router:** IE 11 bug can break URL unification when comparing objects ([#30393](https://github.com/angular/angular/issues/30393)) ([197584d](https://github.com/angular/angular/commit/197584d))
|
||||
* **router:** type cast correctly for IE 11 bug breaking URL Unification when comparing objects ([#30464](https://github.com/angular/angular/issues/30464)) ([53f3564](https://github.com/angular/angular/commit/53f3564))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **bazel:** use `rbe_autoconfig()` and new container. ([#29336](https://github.com/angular/angular/issues/29336)) ([9abf114](https://github.com/angular/angular/commit/9abf114))
|
||||
* **common:** add ability to watch for AngularJS URL updates through `onUrlChange` hook ([#30466](https://github.com/angular/angular/issues/30466)) ([1aff524](https://github.com/angular/angular/commit/1aff524))
|
||||
* **common:** stricter types for `SlicePipe` ([#30156](https://github.com/angular/angular/issues/30156)) ([95830ee](https://github.com/angular/angular/commit/95830ee))
|
||||
* **core:** deprecate integration with the Web Tracing Framework (WTF) ([#30642](https://github.com/angular/angular/issues/30642)) ([f310a59](https://github.com/angular/angular/commit/f310a59))
|
||||
* **language-service:** Implement `definitionAndBoundSpan` ([#30125](https://github.com/angular/angular/issues/30125)) ([f491673](https://github.com/angular/angular/commit/f491673))
|
||||
* **platform-webworker:** deprecate platform-webworker ([#30642](https://github.com/angular/angular/issues/30642)) ([ccc76f7](https://github.com/angular/angular/commit/ccc76f7))
|
||||
|
||||
|
||||
|
||||
<a name="8.0.0"></a>
|
||||
# [8.0.0](https://github.com/angular/angular/compare/8.0.0-rc.5...8.0.0) (2019-05-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add support for TypeScript 3.4 (and drop older versions) ([#29372](https://github.com/angular/angular/issues/29372)) ([ef85336](https://github.com/angular/angular/commit/ef85336))
|
||||
* add support for TypeScript 3.3 (and drop older versions) ([#29004](https://github.com/angular/angular/issues/29004)) ([75748d6](https://github.com/angular/angular/commit/75748d6))
|
||||
* **common:** add ability to watch for AngularJS URL updates through `onUrlChange` hook ([#30466](https://github.com/angular/angular/issues/30466)) ([8022d36](https://github.com/angular/angular/commit/8022d36))
|
||||
* **common:** stricter types for `SlicePipe` ([#30156](https://github.com/angular/angular/issues/30156)) ([722b2fa](https://github.com/angular/angular/commit/722b2fa))
|
||||
* **bazel:** use `rbe_autoconfig()` and new container ([#29336](https://github.com/angular/angular/issues/29336)) ([e562acc](https://github.com/angular/angular/commit/e562acc))
|
||||
* **common:** add @angular/common/upgrade package for `$location`-related APIs ([#30055](https://github.com/angular/angular/issues/30055)) ([152d99e](https://github.com/angular/angular/commit/152d99e))
|
||||
* **common:** add ability to retrieve the state from `Location` service ([#30055](https://github.com/angular/angular/issues/30055)) ([b44b143](https://github.com/angular/angular/commit/b44b143))
|
||||
* **common:** stricter types for SlicePipe ([#30156](https://github.com/angular/angular/issues/30156)) ([722b2fa](https://github.com/angular/angular/commit/722b2fa))
|
||||
* **bazel:** use rbe_autoconfig() and new container. ([#29336](https://github.com/angular/angular/issues/29336)) ([e562acc](https://github.com/angular/angular/commit/e562acc))
|
||||
* **common:** add [@angular](https://github.com/angular)/common/upgrade package for $location-related APIs ([#30055](https://github.com/angular/angular/issues/30055)) ([152d99e](https://github.com/angular/angular/commit/152d99e))
|
||||
* **common:** add ability to retrieve the state from Location service ([#30055](https://github.com/angular/angular/issues/30055)) ([b44b143](https://github.com/angular/angular/commit/b44b143))
|
||||
* **common:** add ability to track all location changes ([#30055](https://github.com/angular/angular/issues/30055)) ([3a9cf3f](https://github.com/angular/angular/commit/3a9cf3f))
|
||||
* **common:** add APIs to read component pieces of URL ([#30055](https://github.com/angular/angular/issues/30055)) ([b635fe8](https://github.com/angular/angular/commit/b635fe8))
|
||||
* **common:** add `MockPlatformLocation` to enable more robust testing of `Location` services ([#30055](https://github.com/angular/angular/issues/30055)) ([d0672c2](https://github.com/angular/angular/commit/d0672c2))
|
||||
* **common:** add `UrlCodec` type for use with upgrade applications ([#30055](https://github.com/angular/angular/issues/30055)) ([ec455e1](https://github.com/angular/angular/commit/ec455e1))
|
||||
* **common:** add MockPlatformLocation to enable more robust testing of Location services ([#30055](https://github.com/angular/angular/issues/30055)) ([d0672c2](https://github.com/angular/angular/commit/d0672c2))
|
||||
* **common:** add UrlCodec type for use with upgrade applications ([#30055](https://github.com/angular/angular/issues/30055)) ([ec455e1](https://github.com/angular/angular/commit/ec455e1))
|
||||
* **common:** provide replacement for AngularJS $location service ([#30055](https://github.com/angular/angular/issues/30055)) ([4277600](https://github.com/angular/angular/commit/4277600))
|
||||
* remove deprecated `DOCUMENT` token from platform-browser ([#28117](https://github.com/angular/angular/issues/28117)) ([3a9d247](https://github.com/angular/angular/commit/3a9d247))
|
||||
* remove deprecated DOCUMENT token from platform-browser ([#28117](https://github.com/angular/angular/issues/28117)) ([3a9d247](https://github.com/angular/angular/commit/3a9d247))
|
||||
* **compiler:** support skipping leading trivia in template source-maps ([#30095](https://github.com/angular/angular/issues/30095)) ([304a12f](https://github.com/angular/angular/commit/304a12f))
|
||||
* **core:** add missing ARIA attributes to html sanitizer ([#29685](https://github.com/angular/angular/issues/29685)) ([909557d](https://github.com/angular/angular/commit/909557d)), closes [#26815](https://github.com/angular/angular/issues/26815)
|
||||
* **router:** deprecate loadChildren:string ([#30073](https://github.com/angular/angular/issues/30073)) ([c61df39](https://github.com/angular/angular/commit/c61df39))
|
||||
@ -285,28 +63,28 @@
|
||||
* **service-worker:** support bypassing SW with specific header/query param ([#30010](https://github.com/angular/angular/issues/30010)) ([6200732](https://github.com/angular/angular/commit/6200732)), closes [#21191](https://github.com/angular/angular/issues/21191)
|
||||
* **compiler-cli:** export tooling definitions ([#29929](https://github.com/angular/angular/issues/29929)) ([e1f51ea](https://github.com/angular/angular/commit/e1f51ea))
|
||||
* **compiler-cli:** lower some exported expressions ([#30038](https://github.com/angular/angular/issues/30038)) ([8e73f9b](https://github.com/angular/angular/commit/8e73f9b))
|
||||
* **core:** add schematics to move deprecated `DOCUMENT` import ([#29950](https://github.com/angular/angular/issues/29950)) ([645e305](https://github.com/angular/angular/commit/645e305))
|
||||
* **core:** add schematics to move deprecated DOCUMENT import ([#29950](https://github.com/angular/angular/issues/29950)) ([645e305](https://github.com/angular/angular/commit/645e305))
|
||||
* **bazel:** update the build to use the new architect api ([#29720](https://github.com/angular/angular/issues/29720)) ([902a53a](https://github.com/angular/angular/commit/902a53a))
|
||||
* remove @angular/http dependency from @angular/platform-server ([#29408](https://github.com/angular/angular/issues/29408)) ([9745f55](https://github.com/angular/angular/commit/9745f55))
|
||||
* remove [@angular](https://github.com/angular)/http dependency from [@angular](https://github.com/angular)/platform-server ([#29408](https://github.com/angular/angular/issues/29408)) ([9745f55](https://github.com/angular/angular/commit/9745f55))
|
||||
* **compiler-cli:** ngcc - make logging more configurable ([#29591](https://github.com/angular/angular/issues/29591)) ([8d3d75e](https://github.com/angular/angular/commit/8d3d75e))
|
||||
* **core:** Add `AbstractType<T>` interface ([#29295](https://github.com/angular/angular/issues/29295)) ([afd4a4e](https://github.com/angular/angular/commit/afd4a4e)), closes [#26491](https://github.com/angular/angular/issues/26491)
|
||||
* **core:** Add "AbstractType<T>" interface ([#29295](https://github.com/angular/angular/issues/29295)) ([afd4a4e](https://github.com/angular/angular/commit/afd4a4e)), closes [#26491](https://github.com/angular/angular/issues/26491)
|
||||
* **core:** template-var-assignment update schematic ([#29608](https://github.com/angular/angular/issues/29608)) ([7c8f4e3](https://github.com/angular/angular/commit/7c8f4e3))
|
||||
* **bazel:** Upgrade rules_nodejs and rules_sass ([#29388](https://github.com/angular/angular/issues/29388)) ([d6d081e](https://github.com/angular/angular/commit/d6d081e))
|
||||
* **service-worker:** support multiple apps on different subpaths of a domain ([#27080](https://github.com/angular/angular/issues/27080)) ([e721c08](https://github.com/angular/angular/commit/e721c08)), closes [#21388](https://github.com/angular/angular/issues/21388)
|
||||
* **bazel:** Eject Bazel ([#29167](https://github.com/angular/angular/issues/29167)) ([36a1550](https://github.com/angular/angular/commit/36a1550))
|
||||
* **bazel:** Hide Bazel files in Bazel builder ([#29110](https://github.com/angular/angular/issues/29110)) ([7060d90](https://github.com/angular/angular/commit/7060d90))
|
||||
* **forms:** clear (remove all) components from a FormArray ([#28918](https://github.com/angular/angular/issues/28918)) ([a68b1a1](https://github.com/angular/angular/commit/a68b1a1)), closes [#18531](https://github.com/angular/angular/issues/18531)
|
||||
* **platform-server:** wait on returned `BEFORE_APP_SERIALIZED` promises ([#29120](https://github.com/angular/angular/issues/29120)) ([7102ea8](https://github.com/angular/angular/commit/7102ea8))
|
||||
* **platform-server:** wait on returned BEFORE_APP_SERIALIZED promises ([#29120](https://github.com/angular/angular/issues/29120)) ([7102ea8](https://github.com/angular/angular/commit/7102ea8))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** allow `ts_library` interop with list-typed inputs ([#30600](https://github.com/angular/angular/issues/30600)) ([bf38df4](https://github.com/angular/angular/commit/bf38df4))
|
||||
* **bazel:** allow ts_library interop with list-typed inputs ([#30600](https://github.com/angular/angular/issues/30600)) ([bf38df4](https://github.com/angular/angular/commit/bf38df4))
|
||||
* **bazel:** Disable sandbox on Mac OS ([#30460](https://github.com/angular/angular/issues/30460)) ([3de26a8](https://github.com/angular/angular/commit/3de26a8))
|
||||
* **bazel:** ng test should run specific ts_web_test_suite ([#30526](https://github.com/angular/angular/issues/30526)) ([8bc4da8](https://github.com/angular/angular/commit/8bc4da8))
|
||||
* **core:** remove deprecated `TestBed.deprecatedOverrideProvider` API ([#30576](https://github.com/angular/angular/issues/30576)) ([5a46f94](https://github.com/angular/angular/commit/5a46f94))
|
||||
* **core:** require 'static' flag on queries in typings ([#30641](https://github.com/angular/angular/issues/30641)) ([c8af830](https://github.com/angular/angular/commit/c8af830))
|
||||
* **core:** temporarily remove [@deprecated](https://github.com/deprecated) jsdoc tag for a `TextBed.get` overload ([#30514](https://github.com/angular/angular/issues/30514)) ([561e01d](https://github.com/angular/angular/commit/561e01d)), closes [#29290](https://github.com/angular/angular/issues/29290) [#29905](https://github.com/angular/angular/issues/29905)
|
||||
* **core:** temporarily remove [@deprecated](https://github.com/deprecated) jsdoc tag for a TextBed.get overload ([#30514](https://github.com/angular/angular/issues/30514)) ([561e01d](https://github.com/angular/angular/commit/561e01d)), closes [#29290](https://github.com/angular/angular/issues/29290) [#29905](https://github.com/angular/angular/issues/29905)
|
||||
* **router:** type cast correctly for IE 11 bug breaking URL Unification when comparing objects ([#30464](https://github.com/angular/angular/issues/30464)) ([32daa93](https://github.com/angular/angular/commit/32daa93))
|
||||
* **bazel:** Directly spawn native Bazel binary ([#30306](https://github.com/angular/angular/issues/30306)) ([d1fcc2b](https://github.com/angular/angular/commit/d1fcc2b))
|
||||
* **bazel:** pass correct arguments to http_server in Windows ([#30346](https://github.com/angular/angular/issues/30346)) ([71eba45](https://github.com/angular/angular/commit/71eba45)), closes [#29785](https://github.com/angular/angular/issues/29785)
|
||||
@ -322,7 +100,7 @@
|
||||
* **core:** static-query usage migration strategy should detect ambiguous query usage ([#30215](https://github.com/angular/angular/issues/30215)) ([e295c6a](https://github.com/angular/angular/commit/e295c6a))
|
||||
* **router:** ensure navigations start with the current URL value incase redirect is skipped ([#30344](https://github.com/angular/angular/issues/30344)) ([9b88920](https://github.com/angular/angular/commit/9b88920)), closes [#30340](https://github.com/angular/angular/issues/30340) [#30160](https://github.com/angular/angular/issues/30160)
|
||||
* **router:** IE 11 bug can break URL unification when comparing objects ([#30393](https://github.com/angular/angular/issues/30393)) ([c383491](https://github.com/angular/angular/commit/c383491))
|
||||
* **bazel:** Bump ibazel to 0.10.1 for Windows fixes ([#30196](https://github.com/angular/angular/issues/30196)) ([9f68c35](https://github.com/angular/angular/commit/9f68c35))
|
||||
* **bazel:** Bump ibazel to 0.10.1 for windows fixes ([#30196](https://github.com/angular/angular/issues/30196)) ([9f68c35](https://github.com/angular/angular/commit/9f68c35))
|
||||
* **compiler-cli:** log ngcc skipping messages as debug instead of info ([#30232](https://github.com/angular/angular/issues/30232)) ([548b003](https://github.com/angular/angular/commit/548b003))
|
||||
* **core:** fix interpolate identifier in AOT ([#30243](https://github.com/angular/angular/issues/30243)) ([3fe3a84](https://github.com/angular/angular/commit/3fe3a84))
|
||||
* **router:** ensure `history.state` is set in `eager` update mode ([#30154](https://github.com/angular/angular/issues/30154)) ([9720227](https://github.com/angular/angular/commit/9720227))
|
||||
@ -332,54 +110,57 @@
|
||||
* **bazel:** Exclude common/upgrade* in metadata.tsconfig.json ([#30133](https://github.com/angular/angular/issues/30133)) ([6711f22](https://github.com/angular/angular/commit/6711f22))
|
||||
* **bazel:** update peerDep ranges ([#30155](https://github.com/angular/angular/issues/30155)) ([6067583](https://github.com/angular/angular/commit/6067583))
|
||||
* **bazel:** make name param in ng add optional ([#30074](https://github.com/angular/angular/issues/30074)) ([0b5f480](https://github.com/angular/angular/commit/0b5f480))
|
||||
* **bazel:** Make sure only single copy of @angular/bazel is installed ([#30072](https://github.com/angular/angular/issues/30072)) ([2905bf5](https://github.com/angular/angular/commit/2905bf5))
|
||||
* **bazel:** Make sure only single copy of `[@angular](https://github.com/angular)/bazel` is installed ([#30072](https://github.com/angular/angular/issues/30072)) ([2905bf5](https://github.com/angular/angular/commit/2905bf5))
|
||||
* **bazel:** transitive npm deps in ng_module ([#30065](https://github.com/angular/angular/issues/30065)) ([61365a9](https://github.com/angular/angular/commit/61365a9))
|
||||
* **common:** add upgrade sub-package to `ng_package` rule for @angular/common ([#30117](https://github.com/angular/angular/issues/30117)) ([6de4cbd](https://github.com/angular/angular/commit/6de4cbd)), closes [#30055](https://github.com/angular/angular/issues/30055) [#30116](https://github.com/angular/angular/issues/30116)
|
||||
* **common:** adjust `MockPlatformLocation` to set state to new object ([#30055](https://github.com/angular/angular/issues/30055)) ([825efa8](https://github.com/angular/angular/commit/825efa8))
|
||||
* **common:** add upgrade sub-package to ng_package rule for [@angular](https://github.com/angular)/common ([#30117](https://github.com/angular/angular/issues/30117)) ([6de4cbd](https://github.com/angular/angular/commit/6de4cbd)), closes [#30055](https://github.com/angular/angular/issues/30055) [#30116](https://github.com/angular/angular/issues/30116)
|
||||
* **common:** adjust MockPlatformLocation to set state to new object ([#30055](https://github.com/angular/angular/issues/30055)) ([825efa8](https://github.com/angular/angular/commit/825efa8))
|
||||
* **compiler:** Fix compiler crash due to isSkipSelf of null ([#30075](https://github.com/angular/angular/issues/30075)) ([28fd5ab](https://github.com/angular/angular/commit/28fd5ab))
|
||||
* **upgrade:** do not break if `onMicrotaskEmpty` emits while a `$digest` is in progress ([#29794](https://github.com/angular/angular/issues/29794)) ([0ddf2e7](https://github.com/angular/angular/commit/0ddf2e7)), closes [#24680](https://github.com/angular/angular/issues/24680) [/github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts#L12](https://github.com//github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts/issues/L12) [#24680](https://github.com/angular/angular/issues/24680)
|
||||
* **bazel:** do not typecheck core schematic files ([#29876](https://github.com/angular/angular/issues/29876)) ([2ba799d](https://github.com/angular/angular/commit/2ba799d))
|
||||
* **bazel:** restore `ng build --prod` ([#30005](https://github.com/angular/angular/issues/30005)) ([96a8289](https://github.com/angular/angular/commit/96a8289))
|
||||
* **common:** prevent repeated application of `HttpParams` mutations ([#29045](https://github.com/angular/angular/issues/29045)) ([8e8e89a](https://github.com/angular/angular/commit/8e8e89a)), closes [#20430](https://github.com/angular/angular/issues/20430)
|
||||
* **common:** async pipe will properly check when it receives an NaN value from an observable ([#22305](https://github.com/angular/angular/issues/22305)) ([3f6bf6d](https://github.com/angular/angular/commit/3f6bf6d)), closes [#15721](https://github.com/angular/angular/issues/15721)
|
||||
* **core:** don't include a local `EventListener` in typings ([#29809](https://github.com/angular/angular/issues/29809)) ([4bde40f](https://github.com/angular/angular/commit/4bde40f)), closes [#29806](https://github.com/angular/angular/issues/29806)
|
||||
* **core:** use shakeable global definitions ([#29929](https://github.com/angular/angular/issues/29929)) ([e5905bb](https://github.com/angular/angular/commit/e5905bb))
|
||||
* **bazel:** restore ng build --prod ([#30005](https://github.com/angular/angular/issues/30005)) ([96a8289](https://github.com/angular/angular/commit/96a8289))
|
||||
* **common:** prevent repeated application of HttpParams mutations ([#29045](https://github.com/angular/angular/issues/29045)) ([8e8e89a](https://github.com/angular/angular/commit/8e8e89a)), closes [#20430](https://github.com/angular/angular/issues/20430)
|
||||
* **common:** async pipe will properly check when it recieves an NaN value from an observable ([#22305](https://github.com/angular/angular/issues/22305)) ([3f6bf6d](https://github.com/angular/angular/commit/3f6bf6d)), closes [#15721](https://github.com/angular/angular/issues/15721)
|
||||
* **core:** don't include a local `EventListener` in typings ([#29809](https://github.com/angular/angular/issues/29809)) ([4bde40f](https://github.com/angular/angular/commit/4bde40f)), closes [/github.com/angular/angular/blob/303eae918d997070a36b523ddc97e018f622c258/packages/core/src/debug/debug_node.ts#L32](https://github.com//github.com/angular/angular/blob/303eae918d997070a36b523ddc97e018f622c258/packages/core/src/debug/debug_node.ts/issues/L32) [#29806](https://github.com/angular/angular/issues/29806)
|
||||
* **core:** use shakeable global definitions ([#29929](https://github.com/angular/angular/issues/29929)) ([e5905bb](https://github.com/angular/angular/commit/e5905bb)), closes [/github.com/angular/angular-cli/blob/5fc1f2499cbe57f9a95e4b0dfced130eb3a8046d/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts#L279-L282](https://github.com//github.com/angular/angular-cli/blob/5fc1f2499cbe57f9a95e4b0dfced130eb3a8046d/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts/issues/L279-L282)
|
||||
* **language-service:** Use proper types instead of any ([#29942](https://github.com/angular/angular/issues/29942)) ([1a56cd5](https://github.com/angular/angular/commit/1a56cd5))
|
||||
* **bazel:** Install packages after `ng add` when invoked independently ([#29852](https://github.com/angular/angular/issues/29852)) ([bd2ce9c](https://github.com/angular/angular/commit/bd2ce9c))
|
||||
* **compiler-cli:** pass config path to `ts.parseJsonConfigFileContent` ([#29872](https://github.com/angular/angular/issues/29872)) ([86a3f90](https://github.com/angular/angular/commit/86a3f90))
|
||||
* **compiler-cli:** pass config path to ts.parseJsonConfigFileContent ([#29872](https://github.com/angular/angular/issues/29872)) ([86a3f90](https://github.com/angular/angular/commit/86a3f90)), closes [/github.com/Microsoft/TypeScript/blob/025d82633915b67003ea38ba40b9239a19721c13/src/compiler/emitter.ts#L56-L57](https://github.com//github.com/Microsoft/TypeScript/blob/025d82633915b67003ea38ba40b9239a19721c13/src/compiler/emitter.ts/issues/L56-L57)
|
||||
* **router:** support non-NgFactory promise in loadChildren typings ([#29832](https://github.com/angular/angular/issues/29832)) ([2bfb6a0](https://github.com/angular/angular/commit/2bfb6a0))
|
||||
* **bazel:** add `configuration_env_vars = ["compile"]` to generated `@npm//@angular/bazel/bin:ngc-wrapped` `nodejs_binary` ([#29694](https://github.com/angular/angular/issues/29694)) ([2e66ddf](https://github.com/angular/angular/commit/2e66ddf))
|
||||
* **bazel:** add configuration_env_vars = ["compile"] to generated [@npm](https://github.com/npm)//[@angular](https://github.com/angular)/bazel/bin:ngc-wrapped nodejs_binary ([#29694](https://github.com/angular/angular/issues/29694)) ([2e66ddf](https://github.com/angular/angular/commit/2e66ddf))
|
||||
* **bazel:** docs formatting ([#29817](https://github.com/angular/angular/issues/29817)) ([cc2e4b6](https://github.com/angular/angular/commit/cc2e4b6))
|
||||
* **bazel:** remove karma-jasmine from `ts_web_test_suite` ([#29695](https://github.com/angular/angular/issues/29695)) ([2bd9214](https://github.com/angular/angular/commit/2bd9214))
|
||||
* **bazel:** remove karma-jasmine from ts_web_test_suite ([#29695](https://github.com/angular/angular/issues/29695)) ([2bd9214](https://github.com/angular/angular/commit/2bd9214))
|
||||
* **bazel:** support running ng-add on minimal applications ([#29681](https://github.com/angular/angular/issues/29681)) ([9810c6c](https://github.com/angular/angular/commit/9810c6c)), closes [#29680](https://github.com/angular/angular/issues/29680)
|
||||
* **common:** add `@Injectable()` to common pipes ([#29834](https://github.com/angular/angular/issues/29834)) ([387fbb8](https://github.com/angular/angular/commit/387fbb8))
|
||||
* **compiler-cli:** ensure `LogicalProjectPaths` always start with a slash ([#29627](https://github.com/angular/angular/issues/29627)) ([e02684e](https://github.com/angular/angular/commit/e02684e))
|
||||
* **compiler-cli:** ensure LogicalProjectPaths always start with a slash ([#29627](https://github.com/angular/angular/issues/29627)) ([e02684e](https://github.com/angular/angular/commit/e02684e))
|
||||
* **core:** add missing migration to npm package ([#29705](https://github.com/angular/angular/issues/29705)) ([96b76dc](https://github.com/angular/angular/commit/96b76dc))
|
||||
* **core:** call `ngOnDestroy` for tree-shakeable providers ([#28943](https://github.com/angular/angular/issues/28943)) ([30b0442](https://github.com/angular/angular/commit/30b0442)), closes [#28927](https://github.com/angular/angular/issues/28927)
|
||||
* **core:** Deprecate `TestBed.get(...):any` ([#29290](https://github.com/angular/angular/issues/29290)) ([609024f](https://github.com/angular/angular/commit/609024f)), closes [#13785](https://github.com/angular/angular/issues/13785) [#26491](https://github.com/angular/angular/issues/26491)
|
||||
* **core:** call ngOnDestroy for tree-shakeable providers ([#28943](https://github.com/angular/angular/issues/28943)) ([30b0442](https://github.com/angular/angular/commit/30b0442)), closes [#28927](https://github.com/angular/angular/issues/28927)
|
||||
* **core:** Deprecate TestBed.get(...):any ([#29290](https://github.com/angular/angular/issues/29290)) ([609024f](https://github.com/angular/angular/commit/609024f)), closes [#13785](https://github.com/angular/angular/issues/13785) [#26491](https://github.com/angular/angular/issues/26491)
|
||||
* **core:** resolve ts compile issues due to lenient tsconfig ([#29843](https://github.com/angular/angular/issues/29843)) ([54058ba](https://github.com/angular/angular/commit/54058ba))
|
||||
* **platform-browser:** insert `APP_ID` in styles, contentAttr and hostAttr ([#17745](https://github.com/angular/angular/issues/17745)) ([712d60e](https://github.com/angular/angular/commit/712d60e))
|
||||
* **platform-browser:** insert APP_ID in styles, contentAttr and hostAttr ([#17745](https://github.com/angular/angular/issues/17745)) ([712d60e](https://github.com/angular/angular/commit/712d60e))
|
||||
* **bazel:** use //:tsconfig.json as the default for ng_module ([#29670](https://github.com/angular/angular/issues/29670)) ([#29711](https://github.com/angular/angular/issues/29711)) ([9e33dc3](https://github.com/angular/angular/commit/9e33dc3))
|
||||
* **platform-browser:** insert APP_ID in styles, contentAttr and hostAttr ([#17745](https://github.com/angular/angular/issues/17745)) ([ca14509](https://github.com/angular/angular/commit/ca14509))
|
||||
* **bazel:** Update schematics to support routing ([#29548](https://github.com/angular/angular/issues/29548)) ([401b8ee](https://github.com/angular/angular/commit/401b8ee))
|
||||
* **bazel:** use `//:tsconfig.json` as the default for `ng_module` ([#29670](https://github.com/angular/angular/issues/29670)) ([b14537a](https://github.com/angular/angular/commit/b14537a))
|
||||
* **bazel:** use //:tsconfig.json as the default for ng_module ([#29670](https://github.com/angular/angular/issues/29670)) ([b14537a](https://github.com/angular/angular/commit/b14537a))
|
||||
* **compiler-cli:** ngcc - cope with processing entry-points multiple times ([#29657](https://github.com/angular/angular/issues/29657)) ([6b39c9c](https://github.com/angular/angular/commit/6b39c9c))
|
||||
* **core:** static-query schematic should detect static queries in getters. ([#29609](https://github.com/angular/angular/issues/29609)) ([33016b8](https://github.com/angular/angular/commit/33016b8))
|
||||
* **common:** escape query selector used when anchor scrolling ([#29577](https://github.com/angular/angular/issues/29577)) ([7671c73](https://github.com/angular/angular/commit/7671c73)), closes [#28193](https://github.com/angular/angular/issues/28193)
|
||||
* **router:** adjust setting navigationTransition when a new navigation cancels an existing one ([#29636](https://github.com/angular/angular/issues/29636)) ([e884c0c](https://github.com/angular/angular/commit/e884c0c)), closes [#29389](https://github.com/angular/angular/issues/29389) [#29590](https://github.com/angular/angular/issues/29590)
|
||||
* **bazel:** allow `ng_module` users to set `createExternalSymbolFactoryReexports` ([#29459](https://github.com/angular/angular/issues/29459)) ([21be0fb](https://github.com/angular/angular/commit/21be0fb))
|
||||
* **bazel:** allow ng_module users to set createExternalSymbolFactoryReexports ([#29459](https://github.com/angular/angular/issues/29459)) ([21be0fb](https://github.com/angular/angular/commit/21be0fb))
|
||||
* **bazel:** workaround problem reading summary files from node_modules ([#29459](https://github.com/angular/angular/issues/29459)) ([769d960](https://github.com/angular/angular/commit/769d960))
|
||||
* **compiler:** inherit param types when class has a constructor which takes no declared parameters and delegates up ([#29232](https://github.com/angular/angular/issues/29232)) ([0007564](https://github.com/angular/angular/commit/0007564))
|
||||
* **core:** parse incorrect ML open tag as text ([#29328](https://github.com/angular/angular/issues/29328)) ([dafbbf8](https://github.com/angular/angular/commit/dafbbf8)), closes [#29231](https://github.com/angular/angular/issues/29231)
|
||||
* **core:** static-query schematic should detect queries in `ngDoCheck` and `ngOnChanges` ([#29492](https://github.com/angular/angular/issues/29492)) ([09fab58](https://github.com/angular/angular/commit/09fab58))
|
||||
* **router:** support `NgFactory` promise in loadChildren typings ([#29392](https://github.com/angular/angular/issues/29392)) ([26a8c59](https://github.com/angular/angular/commit/26a8c59))
|
||||
* **core:** static-query schematic should detect queries in "ngDoCheck" and "ngOnChanges" ([#29492](https://github.com/angular/angular/issues/29492)) ([09fab58](https://github.com/angular/angular/commit/09fab58))
|
||||
* **router:** support NgFactory promise in loadChildren typings ([#29392](https://github.com/angular/angular/issues/29392)) ([26a8c59](https://github.com/angular/angular/commit/26a8c59))
|
||||
* **bazel:** correct regexp test for self-references in metadata ([#29346](https://github.com/angular/angular/issues/29346)) ([9d090cb](https://github.com/angular/angular/commit/9d090cb))
|
||||
* **bazel:** don't produce self-references in metadata ([#29317](https://github.com/angular/angular/issues/29317)) ([3facdeb](https://github.com/angular/angular/commit/3facdeb)), closes [#29315](https://github.com/angular/angular/issues/29315)
|
||||
* **bazel:** fix strict null checks compile error in `packages/bazel/src/schematics/ng-add/index.ts` ([#29282](https://github.com/angular/angular/issues/29282)) ([9a7f560](https://github.com/angular/angular/commit/9a7f560))
|
||||
* **bazel:** Remove @angular/upgrade from dev dependencies ([#29319](https://github.com/angular/angular/issues/29319)) ([1db8bf3](https://github.com/angular/angular/commit/1db8bf3))
|
||||
* **bazel:** fix strict null checks compile error in packages/bazel/src/schematics/ng-add/index.ts ([#29282](https://github.com/angular/angular/issues/29282)) ([9a7f560](https://github.com/angular/angular/commit/9a7f560))
|
||||
* **bazel:** Remove [@angular](https://github.com/angular)/upgrade from dev dependencies ([#29319](https://github.com/angular/angular/issues/29319)) ([1db8bf3](https://github.com/angular/angular/commit/1db8bf3))
|
||||
* **bazel:** Support new e2e project layout ([#29318](https://github.com/angular/angular/issues/29318)) ([8ef690c](https://github.com/angular/angular/commit/8ef690c))
|
||||
* **bazel:** turn off pure call tree shaking for ng_package ([#29210](https://github.com/angular/angular/issues/29210)) ([4990b93](https://github.com/angular/angular/commit/4990b93))
|
||||
* **compiler-cli:** incorrect metadata bundle for multiple unnamed re-exports ([#29360](https://github.com/angular/angular/issues/29360)) ([105cfaf](https://github.com/angular/angular/commit/105cfaf))
|
||||
* **compiler-cli:** incorrect metadata bundle for multiple unnamed re-exports ([#29360](https://github.com/angular/angular/issues/29360)) ([105cfaf](https://github.com/angular/angular/commit/105cfaf)), closes [/github.com/angular/material2/blob/master/tools/package-tools/build-release.ts#L78-L85](https://github.com//github.com/angular/material2/blob/master/tools/package-tools/build-release.ts/issues/L78-L85)
|
||||
* **core:** don't wrap `<tr>` and `<col>` elements into a required parent ([#29219](https://github.com/angular/angular/issues/29219)) ([f2dc32e](https://github.com/angular/angular/commit/f2dc32e))
|
||||
* **core:** parse incorrect ML open tag as text ([#29328](https://github.com/angular/angular/issues/29328)) ([4605df8](https://github.com/angular/angular/commit/4605df8)), closes [#29231](https://github.com/angular/angular/issues/29231)
|
||||
* **compiler-cli:** incorrect metadata bundle for multiple unnamed re-exports ([#29360](https://github.com/angular/angular/issues/29360)) ([cf8d934](https://github.com/angular/angular/commit/cf8d934)), closes [github.com/angular/material2/blob/master/tools/package-tools/build-release.ts#L78-L85](https://github.com/angular/material2/blob/master/tools/package-tools/build-release.ts#L78-L85)
|
||||
* **bazel:** add missing binary path for api-extractor ([#29202](https://github.com/angular/angular/issues/29202)) ([df354d1](https://github.com/angular/angular/commit/df354d1))
|
||||
* **bazel:** ng build should produce prod bundle ([#29136](https://github.com/angular/angular/issues/29136)) ([14ce8a9](https://github.com/angular/angular/commit/14ce8a9))
|
||||
* **compiler:** ensure template is updated if an output is transformed ([#29041](https://github.com/angular/angular/issues/29041)) ([c7e4931](https://github.com/angular/angular/commit/c7e4931))
|
||||
@ -393,16 +174,16 @@
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* **bazel:** @bazel/typescript is now a peerDependency of @angular/bazel so users of @angular/bazel must add @bazel/typescript to their package.json
|
||||
* **bazel:** `ng_module` now depends on a minimum of build_bazel_rules_nodejs 0.27.12
|
||||
* **core:** In Angular version 8, it's required that all `@ViewChild` and `@ContentChild`
|
||||
queries have a `'static'` flag specifying whether the query is 'static' or
|
||||
* **bazel:** @bazel/typescript is now a peerDependency of @angular/bazel so user's of @angular/bazel must add @bazel/typescript to their package.json
|
||||
* **bazel:** ng_module now depends on a minimum of build_bazel_rules_nodejs 0.27.12
|
||||
* **core:** In Angular version 8, it's required that all @ViewChild and @ContentChild
|
||||
queries have a 'static' flag specifying whether the query is 'static' or
|
||||
'dynamic'. The compiler previously sorted queries automatically, but in
|
||||
8.0 developers are required to explicitly specify which behavior is wanted.
|
||||
This is a temporary requirement as part of a migration; see
|
||||
[static query migration guide](https://v8.angular.io/guide/static-query-migration) for more details.
|
||||
https://v8.angular.io/guide/static-query-migration for more details.
|
||||
|
||||
`@ViewChildren` and `@ContentChildren` queries are always dynamic, and so are
|
||||
@ViewChildren and @ContentChildren queries are always dynamic, and so are
|
||||
unaffected.
|
||||
|
||||
* `TestBed.get()` has two signatures, one which is typed and another which accepts and returns `any`. The signature for `any` is deprecated; all usage of `TestBed.get()` should go through the typed API. This mainly affects string tokens
|
||||
@ -436,8 +217,8 @@ This is a temporary requirement as part of a migration; see
|
||||
- `<tr>` would be wrapped in `<tbody>` if not inside `<tbody>`, `<tfoot>` or `<thead>`;
|
||||
- `<col>` would be wrapped in `<colgroup>` if not inside `<colgroup>`.
|
||||
|
||||
This mechanism of automatic wrapping / auto-correcting was problematic for several reasons:
|
||||
- it is non-obvious and arbitrary (ex. there are more HTML elements that have rules for parent type);
|
||||
This meachanism of automatic wrapping / auto-correcting was problematic for several reasons:
|
||||
- it is non-obvious and arbitrary (ex. there are more HTML elements that has rules for parent type);
|
||||
- it is incorrect for cases where `<tr>` / `<col>` are at the root of a component's content, ex.:
|
||||
|
||||
```html
|
||||
@ -449,13 +230,10 @@ This is a temporary requirement as part of a migration; see
|
||||
In the above example the `<projecting-tr-inside-tbody>` component could be "surprised" to see additional
|
||||
`<tbody>` elements inserted by Angular HTML parser.
|
||||
|
||||
* **http:** The deprecated @angular/http package has been removed, the @angular/common/http package should be used instead.
|
||||
For details on how to migrate, please refer to [the deprecations guide](https://angular.io/guide/deprecations#angularhttp).
|
||||
|
||||
|
||||
* TypeScript 3.1 and 3.2 are no longer supported.
|
||||
|
||||
Please update your TypeScript version to 3.4, as version 3.3 is also not supported.
|
||||
Please update your TypeScript version to 3.3
|
||||
|
||||
|
||||
<a name="8.0.0-rc.5"></a>
|
||||
@ -541,17 +319,6 @@ unaffected.
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="7.2.15"></a>
|
||||
## [7.2.15](https://github.com/angular/angular/compare/7.2.14...7.2.15) (2019-05-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **upgrade:** do not break if `onMicrotaskEmpty` emits while a `$digest` is in progress ([#29794](https://github.com/angular/angular/issues/29794)) ([#30107](https://github.com/angular/angular/issues/30107)) ([1084c19](https://github.com/angular/angular/commit/1084c19)), closes [#24680](https://github.com/angular/angular/issues/24680) [/github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts#L12](https://github.com//github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts/issues/L12) [#24680](https://github.com/angular/angular/issues/24680)
|
||||
|
||||
|
||||
|
||||
<a name="8.0.0-rc.2"></a>
|
||||
# [8.0.0-rc.2](https://github.com/angular/angular/compare/8.0.0-rc.1...8.0.0-rc.2) (2019-04-29)
|
||||
|
||||
@ -581,9 +348,9 @@ unaffected.
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** make name param in ng add optional ([#30074](https://github.com/angular/angular/issues/30074)) ([0b5f480](https://github.com/angular/angular/commit/0b5f480))
|
||||
* **bazel:** Make sure only single copy of `@angular/bazel` is installed ([#30072](https://github.com/angular/angular/issues/30072)) ([2905bf5](https://github.com/angular/angular/commit/2905bf5))
|
||||
* **bazel:** Make sure only single copy of `[@angular](https://github.com/angular)/bazel` is installed ([#30072](https://github.com/angular/angular/issues/30072)) ([2905bf5](https://github.com/angular/angular/commit/2905bf5))
|
||||
* **bazel:** transitive npm deps in ng_module ([#30065](https://github.com/angular/angular/issues/30065)) ([61365a9](https://github.com/angular/angular/commit/61365a9))
|
||||
* **common:** add upgrade sub-package to ng_package rule for @angular/common ([#30117](https://github.com/angular/angular/issues/30117)) ([6de4cbd](https://github.com/angular/angular/commit/6de4cbd)), closes [#30055](https://github.com/angular/angular/issues/30055) [#30116](https://github.com/angular/angular/issues/30116)
|
||||
* **common:** add upgrade sub-package to ng_package rule for [@angular](https://github.com/angular)/common ([#30117](https://github.com/angular/angular/issues/30117)) ([6de4cbd](https://github.com/angular/angular/commit/6de4cbd)), closes [#30055](https://github.com/angular/angular/issues/30055) [#30116](https://github.com/angular/angular/issues/30116)
|
||||
* **common:** adjust MockPlatformLocation to set state to new object ([#30055](https://github.com/angular/angular/issues/30055)) ([825efa8](https://github.com/angular/angular/commit/825efa8))
|
||||
* **compiler:** Fix compiler crash due to isSkipSelf of null ([#30075](https://github.com/angular/angular/issues/30075)) ([28fd5ab](https://github.com/angular/angular/commit/28fd5ab))
|
||||
* **upgrade:** do not break if `onMicrotaskEmpty` emits while a `$digest` is in progress ([#29794](https://github.com/angular/angular/issues/29794)) ([0ddf2e7](https://github.com/angular/angular/commit/0ddf2e7)), closes [#24680](https://github.com/angular/angular/issues/24680) [/github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts#L12](https://github.com//github.com/angular/angular/blob/78146c189/packages/core/src/util/ng_dev_mode.ts/issues/L12) [#24680](https://github.com/angular/angular/issues/24680)
|
||||
@ -591,7 +358,7 @@ unaffected.
|
||||
|
||||
### Features
|
||||
|
||||
* **common:** add @angular/common/upgrade package for $location-related APIs ([#30055](https://github.com/angular/angular/issues/30055)) ([152d99e](https://github.com/angular/angular/commit/152d99e))
|
||||
* **common:** add [@angular](https://github.com/angular)/common/upgrade package for $location-related APIs ([#30055](https://github.com/angular/angular/issues/30055)) ([152d99e](https://github.com/angular/angular/commit/152d99e))
|
||||
* **common:** add ability to retrieve the state from Location service ([#30055](https://github.com/angular/angular/issues/30055)) ([b44b143](https://github.com/angular/angular/commit/b44b143))
|
||||
* **common:** add ability to track all location changes ([#30055](https://github.com/angular/angular/issues/30055)) ([3a9cf3f](https://github.com/angular/angular/commit/3a9cf3f))
|
||||
* **common:** add APIs to read component pieces of URL ([#30055](https://github.com/angular/angular/issues/30055)) ([b635fe8](https://github.com/angular/angular/commit/b635fe8))
|
||||
@ -654,7 +421,7 @@ unaffected.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** add configuration_env_vars = ["compile"] to generated [@npm](https://github.com/npm)//@angular/bazel/bin:ngc-wrapped nodejs_binary ([#29694](https://github.com/angular/angular/issues/29694)) ([2e66ddf](https://github.com/angular/angular/commit/2e66ddf))
|
||||
* **bazel:** add configuration_env_vars = ["compile"] to generated [@npm](https://github.com/npm)//[@angular](https://github.com/angular)/bazel/bin:ngc-wrapped nodejs_binary ([#29694](https://github.com/angular/angular/issues/29694)) ([2e66ddf](https://github.com/angular/angular/commit/2e66ddf))
|
||||
* **bazel:** docs formatting ([#29817](https://github.com/angular/angular/issues/29817)) ([cc2e4b6](https://github.com/angular/angular/commit/cc2e4b6))
|
||||
* **bazel:** remove karma-jasmine from ts_web_test_suite ([#29695](https://github.com/angular/angular/issues/29695)) ([2bd9214](https://github.com/angular/angular/commit/2bd9214))
|
||||
* **bazel:** support running ng-add on minimal applications ([#29681](https://github.com/angular/angular/issues/29681)) ([9810c6c](https://github.com/angular/angular/commit/9810c6c)), closes [#29680](https://github.com/angular/angular/issues/29680)
|
||||
@ -729,7 +496,7 @@ let service = TestBed.get(SERVICE_TOKEN); // type Service
|
||||
|
||||
### Features
|
||||
|
||||
* remove @angular/http dependency from @angular/platform-server ([#29408](https://github.com/angular/angular/issues/29408)) ([9745f55](https://github.com/angular/angular/commit/9745f55))
|
||||
* remove [@angular](https://github.com/angular)/http dependency from [@angular](https://github.com/angular)/platform-server ([#29408](https://github.com/angular/angular/issues/29408)) ([9745f55](https://github.com/angular/angular/commit/9745f55))
|
||||
* **compiler-cli:** ngcc - make logging more configurable ([#29591](https://github.com/angular/angular/issues/29591)) ([8d3d75e](https://github.com/angular/angular/commit/8d3d75e))
|
||||
* **core:** Add "AbstractType<T>" interface ([#29295](https://github.com/angular/angular/issues/29295)) ([afd4a4e](https://github.com/angular/angular/commit/afd4a4e)), closes [#26491](https://github.com/angular/angular/issues/26491)
|
||||
* **core:** template-var-assignment update schematic ([#29608](https://github.com/angular/angular/issues/29608)) ([7c8f4e3](https://github.com/angular/angular/commit/7c8f4e3))
|
||||
@ -789,7 +556,7 @@ This release contains various API docs improvements.
|
||||
* **bazel:** correct regexp test for self-references in metadata ([#29346](https://github.com/angular/angular/issues/29346)) ([9d090cb](https://github.com/angular/angular/commit/9d090cb))
|
||||
* **bazel:** don't produce self-references in metadata ([#29317](https://github.com/angular/angular/issues/29317)) ([3facdeb](https://github.com/angular/angular/commit/3facdeb)), closes [#29315](https://github.com/angular/angular/issues/29315)
|
||||
* **bazel:** fix strict null checks compile error in packages/bazel/src/schematics/ng-add/index.ts ([#29282](https://github.com/angular/angular/issues/29282)) ([9a7f560](https://github.com/angular/angular/commit/9a7f560))
|
||||
* **bazel:** Remove @angular/upgrade from dev dependencies ([#29319](https://github.com/angular/angular/issues/29319)) ([1db8bf3](https://github.com/angular/angular/commit/1db8bf3))
|
||||
* **bazel:** Remove [@angular](https://github.com/angular)/upgrade from dev dependencies ([#29319](https://github.com/angular/angular/issues/29319)) ([1db8bf3](https://github.com/angular/angular/commit/1db8bf3))
|
||||
* **bazel:** Support new e2e project layout ([#29318](https://github.com/angular/angular/issues/29318)) ([8ef690c](https://github.com/angular/angular/commit/8ef690c))
|
||||
* **bazel:** turn off pure call tree shaking for ng_package ([#29210](https://github.com/angular/angular/issues/29210)) ([4990b93](https://github.com/angular/angular/commit/4990b93))
|
||||
* **compiler-cli:** incorrect metadata bundle for multiple unnamed re-exports ([#29360](https://github.com/angular/angular/issues/29360)) ([105cfaf](https://github.com/angular/angular/commit/105cfaf)), closes [/github.com/angular/material2/blob/master/tools/package-tools/build-release.ts#L78-L85](https://github.com//github.com/angular/material2/blob/master/tools/package-tools/build-release.ts/issues/L78-L85)
|
||||
@ -810,8 +577,8 @@ This release contains various API docs improvements.
|
||||
- `<tr>` would be wrapped in `<tbody>` if not inside `<tbody>`, `<tfoot>` or `<thead>`;
|
||||
- `<col>` would be wrapped in `<colgroup>` if not inside `<colgroup>`.
|
||||
|
||||
This mechanism of automatic wrapping / auto-correcting was problematic for several reasons:
|
||||
- it is non-obvious and arbitrary (ex. there are more HTML elements that have rules for parent type);
|
||||
This meachanism of automatic wrapping / auto-correcting was problematic for several reasons:
|
||||
- it is non-obvious and arbitrary (ex. there are more HTML elements that has rules for parent type);
|
||||
- it is incorrect for cases where `<tr>` / `<col>` are at the root of a component's content, ex.:
|
||||
|
||||
```html
|
||||
@ -1212,7 +979,7 @@ This release contains various API docs improvements.
|
||||
* **bazel:** unable to launch protractor test on windows ([#27850](https://github.com/angular/angular/issues/27850)) ([1e6c9be](https://github.com/angular/angular/commit/1e6c9be))
|
||||
* **bazel:** devserver entry_module should have underscore name ([#27719](https://github.com/angular/angular/issues/27719)) ([f57916c](https://github.com/angular/angular/commit/f57916c))
|
||||
* **bazel:** emit full node stack traces when Angular compilation crashes ([#27678](https://github.com/angular/angular/issues/27678)) ([522919a](https://github.com/angular/angular/commit/522919a))
|
||||
* **bazel:** fix major/minor semver check between @angular/bazel npm packager version and angular bazel repo version ([#27635](https://github.com/angular/angular/issues/27635)) ([1cc08b4](https://github.com/angular/angular/commit/1cc08b4))
|
||||
* **bazel:** fix major/minor semver check between [@angular](https://github.com/angular)/bazel npm packager version and angular bazel repo version ([#27635](https://github.com/angular/angular/issues/27635)) ([1cc08b4](https://github.com/angular/angular/commit/1cc08b4))
|
||||
* **bazel:** Load http_archive and rules_nodejs dependencies ([#27609](https://github.com/angular/angular/issues/27609)) ([8313ffc](https://github.com/angular/angular/commit/8313ffc))
|
||||
* **bazel:** ng_package writes unrelevant definitions to bazel out ([#27519](https://github.com/angular/angular/issues/27519)) ([44dfa60](https://github.com/angular/angular/commit/44dfa60)), closes [/github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts#L105-L124](https://github.com//github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts/issues/L105-L124)
|
||||
* **bazel:** Set module_name and enable ng test ([#27715](https://github.com/angular/angular/issues/27715)) ([85866de](https://github.com/angular/angular/commit/85866de))
|
||||
@ -1232,7 +999,7 @@ This release contains various API docs improvements.
|
||||
* **core:** export a value for InjectFlags ([#27279](https://github.com/angular/angular/issues/27279)) ([23b06af](https://github.com/angular/angular/commit/23b06af)), closes [#27251](https://github.com/angular/angular/issues/27251)
|
||||
* **core:** More precise return type for `InjectableDecorator` ([#27360](https://github.com/angular/angular/issues/27360)) ([4b9948c](https://github.com/angular/angular/commit/4b9948c)), closes [#26942](https://github.com/angular/angular/issues/26942)
|
||||
* **forms:** typed argument for FormBuilder group ([#26985](https://github.com/angular/angular/issues/26985)) ([b0c7561](https://github.com/angular/angular/commit/b0c7561))
|
||||
* **platform-server:** add @angular/http to the list of peerDependencies ([#27307](https://github.com/angular/angular/issues/27307)) ([32c5be9](https://github.com/angular/angular/commit/32c5be9)), closes [#26154](https://github.com/angular/angular/issues/26154)
|
||||
* **platform-server:** add [@angular](https://github.com/angular)/http to the list of peerDependencies ([#27307](https://github.com/angular/angular/issues/27307)) ([32c5be9](https://github.com/angular/angular/commit/32c5be9)), closes [#26154](https://github.com/angular/angular/issues/26154)
|
||||
* **router:** ensure URL is updated after second redirect with UrlUpdateStrategy="eager" ([#27523](https://github.com/angular/angular/issues/27523)) ([ad26cd6](https://github.com/angular/angular/commit/ad26cd6)), closes [#27116](https://github.com/angular/angular/issues/27116)
|
||||
* **router:** update URL after redirects when urlHandlingStrategy='eager' ([#27356](https://github.com/angular/angular/issues/27356)) ([11a8bd8](https://github.com/angular/angular/commit/11a8bd8)), closes [#27076](https://github.com/angular/angular/issues/27076)
|
||||
* **upgrade:** allow nesting components from different downgraded modules ([#27217](https://github.com/angular/angular/issues/27217)) ([bc0ee01](https://github.com/angular/angular/commit/bc0ee01))
|
||||
@ -1252,7 +1019,7 @@ This release contains various API docs improvements.
|
||||
* **animations:** mark actual descendant node as disabled ([#26180](https://github.com/angular/angular/issues/26180)) ([453589f](https://github.com/angular/angular/commit/453589f))
|
||||
* **bazel:** devserver entry_module should have underscore name ([#27719](https://github.com/angular/angular/issues/27719)) ([b108e9a](https://github.com/angular/angular/commit/b108e9a))
|
||||
* **bazel:** emit full node stack traces when Angular compilation crashes ([#27678](https://github.com/angular/angular/issues/27678)) ([0d8528b](https://github.com/angular/angular/commit/0d8528b))
|
||||
* **bazel:** fix major/minor semver check between @angular/bazel npm packager version and angular bazel repo version ([#27635](https://github.com/angular/angular/issues/27635)) ([3ed1e84](https://github.com/angular/angular/commit/3ed1e84))
|
||||
* **bazel:** fix major/minor semver check between [@angular](https://github.com/angular)/bazel npm packager version and angular bazel repo version ([#27635](https://github.com/angular/angular/issues/27635)) ([3ed1e84](https://github.com/angular/angular/commit/3ed1e84))
|
||||
* **bazel:** Load http_archive and rules_nodejs dependencies ([#27609](https://github.com/angular/angular/issues/27609)) ([89ace1a](https://github.com/angular/angular/commit/89ace1a))
|
||||
* **bazel:** ng_package writes unrelevant definitions to bazel out ([#27519](https://github.com/angular/angular/issues/27519)) ([ef056c5](https://github.com/angular/angular/commit/ef056c5)), closes [/github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts#L105-L124](https://github.com//github.com/angular/angular/blob/4f9374951d67c75f67a31c110bd61ab72563db7d/packages/bazel/src/ng_package/packager.ts/issues/L105-L124)
|
||||
* **bazel:** Read latest versions from latest-versions.ts & use semver check ([#27591](https://github.com/angular/angular/issues/27591)) ([93078e3](https://github.com/angular/angular/commit/93078e3))
|
||||
@ -1284,7 +1051,7 @@ This release contains various API docs improvements.
|
||||
* **bazel:** ng_package should correctly map to source maps in secondary entry-points ([#27313](https://github.com/angular/angular/issues/27313)) ([fc2c23e](https://github.com/angular/angular/commit/fc2c23e)), closes [#25510](https://github.com/angular/angular/issues/25510)
|
||||
* **compiler-cli:** flatModuleIndex files not generated on windows with multiple input files ([#27200](https://github.com/angular/angular/issues/27200)) ([8087b6b](https://github.com/angular/angular/commit/8087b6b))
|
||||
* **compiler-cli:** ngtsc shim files not being generated on case-insensitive platforms ([#27466](https://github.com/angular/angular/issues/27466)) ([84f2928](https://github.com/angular/angular/commit/84f2928)), closes [/github.com/Microsoft/TypeScript/blob/3e4c5c95abd515eb9713b881d27ab3a93cc00461/src/compiler/sys.ts#L681-L682](https://github.com//github.com/Microsoft/TypeScript/blob/3e4c5c95abd515eb9713b881d27ab3a93cc00461/src/compiler/sys.ts/issues/L681-L682)
|
||||
* **platform-server:** add @angular/http to the list of peerDependencies ([#27307](https://github.com/angular/angular/issues/27307)) ([236ac06](https://github.com/angular/angular/commit/236ac06)), closes [#26154](https://github.com/angular/angular/issues/26154)
|
||||
* **platform-server:** add [@angular](https://github.com/angular)/http to the list of peerDependencies ([#27307](https://github.com/angular/angular/issues/27307)) ([236ac06](https://github.com/angular/angular/commit/236ac06)), closes [#26154](https://github.com/angular/angular/issues/26154)
|
||||
|
||||
|
||||
|
||||
@ -1311,7 +1078,7 @@ This release contains various API docs improvements.
|
||||
* **compiler:** generate inputs with aliases properly ([#26774](https://github.com/angular/angular/issues/26774)) ([19fcfc3](https://github.com/angular/angular/commit/19fcfc3))
|
||||
* **compiler:** generate relative paths only in summary file errors ([#26759](https://github.com/angular/angular/issues/26759)) ([56f44be](https://github.com/angular/angular/commit/56f44be))
|
||||
* **core:** ignore comment nodes under unsafe elements ([#25879](https://github.com/angular/angular/issues/25879)) ([d5cbcef](https://github.com/angular/angular/commit/d5cbcef))
|
||||
* **core:** Remove static dependency from @angular/core to @angular/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([d042c4a](https://github.com/angular/angular/commit/d042c4a))
|
||||
* **core:** Remove static dependency from [@angular](https://github.com/angular)/core to [@angular](https://github.com/angular)/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([d042c4a](https://github.com/angular/angular/commit/d042c4a))
|
||||
* **core:** support computed base class in metadata inheritance ([#24014](https://github.com/angular/angular/issues/24014)) ([95743e3](https://github.com/angular/angular/commit/95743e3))
|
||||
* **bazel:** unknown replay compiler error in windows ([#26711](https://github.com/angular/angular/issues/26711)) ([aed95fd](https://github.com/angular/angular/commit/aed95fd))
|
||||
* **core:** ensure that `ɵdefineNgModule` is available in flat-file formats ([#26403](https://github.com/angular/angular/issues/26403)) ([a64859b](https://github.com/angular/angular/commit/a64859b))
|
||||
@ -1418,7 +1185,7 @@ This release contains various API docs improvements.
|
||||
* **compiler:** generate inputs with aliases properly ([#26774](https://github.com/angular/angular/issues/26774)) ([19fcfc3](https://github.com/angular/angular/commit/19fcfc3))
|
||||
* **compiler:** generate relative paths only in summary file errors ([#26759](https://github.com/angular/angular/issues/26759)) ([56f44be](https://github.com/angular/angular/commit/56f44be))
|
||||
* **core:** ignore comment nodes under unsafe elements ([#25879](https://github.com/angular/angular/issues/25879)) ([d5cbcef](https://github.com/angular/angular/commit/d5cbcef))
|
||||
* **core:** Remove static dependency from @angular/core to @angular/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([d042c4a](https://github.com/angular/angular/commit/d042c4a))
|
||||
* **core:** Remove static dependency from [@angular](https://github.com/angular)/core to [@angular](https://github.com/angular)/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([d042c4a](https://github.com/angular/angular/commit/d042c4a))
|
||||
* **core:** support computed base class in metadata inheritance ([#24014](https://github.com/angular/angular/issues/24014)) ([95743e3](https://github.com/angular/angular/commit/95743e3))
|
||||
|
||||
|
||||
@ -1430,7 +1197,7 @@ This release contains various API docs improvements.
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** generate relative paths only in summary file errors ([#26759](https://github.com/angular/angular/issues/26759)) ([c01f340](https://github.com/angular/angular/commit/c01f340))
|
||||
* **core:** Remove static dependency from @angular/core to @angular/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([#26879](https://github.com/angular/angular/issues/26879)) ([257ac83](https://github.com/angular/angular/commit/257ac83))
|
||||
* **core:** Remove static dependency from [@angular](https://github.com/angular)/core to [@angular](https://github.com/angular)/compiler ([#26734](https://github.com/angular/angular/issues/26734)) ([#26879](https://github.com/angular/angular/issues/26879)) ([257ac83](https://github.com/angular/angular/commit/257ac83))
|
||||
* **core:** support computed base class in metadata inheritance ([#24014](https://github.com/angular/angular/issues/24014)) ([b3c6409](https://github.com/angular/angular/commit/b3c6409))
|
||||
|
||||
|
||||
@ -1507,7 +1274,7 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
* **compiler:** update compiler to flatten nested template fns ([#24943](https://github.com/angular/angular/issues/24943)) ([fe14f18](https://github.com/angular/angular/commit/fe14f18))
|
||||
* **compiler:** update compiler to generate new slot allocations ([#25607](https://github.com/angular/angular/issues/25607)) ([27e2039](https://github.com/angular/angular/commit/27e2039))
|
||||
* **core:** In Testability.whenStable update callback, pass more complete ([#25010](https://github.com/angular/angular/issues/25010)) ([16c03c0](https://github.com/angular/angular/commit/16c03c0))
|
||||
* **core:** add missing `peerDependency ` to `@angular/compiler` ([#26033](https://github.com/angular/angular/issues/26033)) ([549de1e](https://github.com/angular/angular/commit/549de1e)), closes [/github.com/angular/angular/commit/919f42fea1df4b9e38b7d688aef5f2de668e9d3e#diff-58563046c4439699f2e6a89187099a54](https://github.com//github.com/angular/angular/commit/919f42fea1df4b9e38b7d688aef5f2de668e9d3e/issues/diff-58563046c4439699f2e6a89187099a54)
|
||||
* **core:** add missing `peerDependency ` to `[@angular](https://github.com/angular)/compiler` ([#26033](https://github.com/angular/angular/issues/26033)) ([549de1e](https://github.com/angular/angular/commit/549de1e)), closes [/github.com/angular/angular/commit/919f42fea1df4b9e38b7d688aef5f2de668e9d3e#diff-58563046c4439699f2e6a89187099a54](https://github.com//github.com/angular/angular/commit/919f42fea1df4b9e38b7d688aef5f2de668e9d3e/issues/diff-58563046c4439699f2e6a89187099a54)
|
||||
* **core:** allow null value for renderer setElement(…) ([#17065](https://github.com/angular/angular/issues/17065)) ([ff15043](https://github.com/angular/angular/commit/ff15043)), closes [#13686](https://github.com/angular/angular/issues/13686)
|
||||
* **core:** do not clear element content when using shadow dom ([#24861](https://github.com/angular/angular/issues/24861)) ([6e828bb](https://github.com/angular/angular/commit/6e828bb))
|
||||
* **core:** size regression with closure compiler ([#25531](https://github.com/angular/angular/issues/25531)) ([1f59f2f](https://github.com/angular/angular/commit/1f59f2f))
|
||||
@ -1953,7 +1720,7 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
* **upgrade:** propagate return value of resumeBootstrap ([#22754](https://github.com/angular/angular/issues/22754)) ([a2330ff](https://github.com/angular/angular/commit/a2330ff)), closes [#22723](https://github.com/angular/angular/issues/22723)
|
||||
* **upgrade:** two-way binding and listening for event ([#22772](https://github.com/angular/angular/issues/22772)) ([2b3de63](https://github.com/angular/angular/commit/2b3de63)), closes [#22734](https://github.com/angular/angular/issues/22734)
|
||||
* **upgrade:** correctly destroy nested downgraded component ([#22400](https://github.com/angular/angular/issues/22400)) ([8a85888](https://github.com/angular/angular/commit/8a85888)), closes [#22392](https://github.com/angular/angular/issues/22392)
|
||||
* **upgrade:** correctly handle `=` bindings in `@angular/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([f089bf5](https://github.com/angular/angular/commit/f089bf5))
|
||||
* **upgrade:** correctly handle `=` bindings in `[@angular](https://github.com/angular)/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([f089bf5](https://github.com/angular/angular/commit/f089bf5))
|
||||
* **upgrade:** fix empty transclusion content with AngularJS@>=1.5.8 ([#22167](https://github.com/angular/angular/issues/22167)) ([13ab91e](https://github.com/angular/angular/commit/13ab91e)), closes [#22175](https://github.com/angular/angular/issues/22175)
|
||||
|
||||
|
||||
@ -2088,7 +1855,7 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
* **router:** don't mutate route configs ([#22358](https://github.com/angular/angular/issues/22358)) ([8f0a064](https://github.com/angular/angular/commit/8f0a064)), closes [#22203](https://github.com/angular/angular/issues/22203)
|
||||
* **router:** fix URL serialization so special characters are only encoded where needed ([#22337](https://github.com/angular/angular/issues/22337)) ([789a47e](https://github.com/angular/angular/commit/789a47e)), closes [#10280](https://github.com/angular/angular/issues/10280)
|
||||
* **upgrade:** correctly destroy nested downgraded component ([#22400](https://github.com/angular/angular/issues/22400)) ([4aef9de](https://github.com/angular/angular/commit/4aef9de)), closes [#22392](https://github.com/angular/angular/issues/22392)
|
||||
* **upgrade:** correctly handle `=` bindings in `@angular/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([6638390](https://github.com/angular/angular/commit/6638390))
|
||||
* **upgrade:** correctly handle `=` bindings in `[@angular](https://github.com/angular)/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([6638390](https://github.com/angular/angular/commit/6638390))
|
||||
* **upgrade:** fix empty transclusion content with AngularJS@>=1.5.8 ([#22167](https://github.com/angular/angular/issues/22167)) ([a9a0e27](https://github.com/angular/angular/commit/a9a0e27)), closes [#22175](https://github.com/angular/angular/issues/22175)
|
||||
|
||||
|
||||
@ -2102,7 +1869,7 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
* **platform-server:** generate correct stylings for camel case names ([#22263](https://github.com/angular/angular/issues/22263)) ([de02a7a](https://github.com/angular/angular/commit/de02a7a)), closes [#19235](https://github.com/angular/angular/issues/19235)
|
||||
* **router:** don't mutate route configs ([#22358](https://github.com/angular/angular/issues/22358)) ([8f0a064](https://github.com/angular/angular/commit/8f0a064)), closes [#22203](https://github.com/angular/angular/issues/22203)
|
||||
* **upgrade:** correctly destroy nested downgraded component ([#22400](https://github.com/angular/angular/issues/22400)) ([4aef9de](https://github.com/angular/angular/commit/4aef9de)), closes [#22392](https://github.com/angular/angular/issues/22392)
|
||||
* **upgrade:** correctly handle `=` bindings in `@angular/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([6638390](https://github.com/angular/angular/commit/6638390))
|
||||
* **upgrade:** correctly handle `=` bindings in `[@angular](https://github.com/angular)/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([6638390](https://github.com/angular/angular/commit/6638390))
|
||||
* **upgrade:** fix empty transclusion content with AngularJS@>=1.5.8 ([#22167](https://github.com/angular/angular/issues/22167)) ([a9a0e27](https://github.com/angular/angular/commit/a9a0e27)), closes [#22175](https://github.com/angular/angular/issues/22175)
|
||||
|
||||
|
||||
@ -2549,7 +2316,7 @@ Note: Due to an animation fix back in 5.1.1 ([c2b3792](https://github.com/angula
|
||||
* **platform-server:** provide a way to hook into renderModule* ([#19023](https://github.com/angular/angular/issues/19023)) ([8dfc3c3](https://github.com/angular/angular/commit/8dfc3c3))
|
||||
* **router:** add ActivationStart/End events ([8f79150](https://github.com/angular/angular/commit/8f79150))
|
||||
* **router:** add events tracking activation of individual routes ([49cd851](https://github.com/angular/angular/commit/49cd851))
|
||||
* **service-worker:** introduce the @angular/service-worker package ([#19274](https://github.com/angular/angular/issues/19274)) ([d442b68](https://github.com/angular/angular/commit/d442b68))
|
||||
* **service-worker:** introduce the [@angular](https://github.com/angular)/service-worker package ([#19274](https://github.com/angular/angular/issues/19274)) ([d442b68](https://github.com/angular/angular/commit/d442b68))
|
||||
* **upgrade:** propagate touched state of NgModelController ([59c23c7](https://github.com/angular/angular/commit/59c23c7))
|
||||
* **upgrade:** support lazy-loading Angular module into AngularJS app ([30e76fc](https://github.com/angular/angular/commit/30e76fc))
|
||||
* update angular to support TypeScript 2.4 ([ca5aeba](https://github.com/angular/angular/commit/ca5aeba))
|
||||
@ -2773,7 +2540,7 @@ Because of multiple bugs and browser inconsistencies, we have dropped the intl a
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** do not leak DOM nodes/styling for host triggered animations ([#18853](https://github.com/angular/angular/issues/18853)) ([1cc3fe2](https://github.com/angular/angular/commit/1cc3fe2)), closes [#18606](https://github.com/angular/angular/issues/18606)
|
||||
* **common:** fix improper packaging for @angular/common/http ([#18613](https://github.com/angular/angular/issues/18613)) ([a203a95](https://github.com/angular/angular/commit/a203a95))
|
||||
* **common:** fix improper packaging for [@angular](https://github.com/angular)/common/http ([#18613](https://github.com/angular/angular/issues/18613)) ([a203a95](https://github.com/angular/angular/commit/a203a95))
|
||||
* **common:** fix XSSI prefix stripping by using JSON.parse always ([#18466](https://github.com/angular/angular/issues/18466)) ([8821723](https://github.com/angular/angular/commit/8821723)), closes [#18396](https://github.com/angular/angular/issues/18396) [#18453](https://github.com/angular/angular/issues/18453)
|
||||
* **compiler:** normalize the locale name ([#18963](https://github.com/angular/angular/issues/18963)) ([497e017](https://github.com/angular/angular/commit/497e017))
|
||||
* **core:** complete EventEmitter in QueryList on component destroy ([#18902](https://github.com/angular/angular/issues/18902)) ([7d137d7](https://github.com/angular/angular/commit/7d137d7)), closes [#18741](https://github.com/angular/angular/issues/18741)
|
||||
@ -3114,7 +2881,7 @@ Note: the 4.4.0 release on npm accidentally glitched-out midway, so we cut 4.4.1
|
||||
* **compiler-cli:** allow '==' to compare nullable types ([#16731](https://github.com/angular/angular/issues/16731)) ([d761059](https://github.com/angular/angular/commit/d761059))
|
||||
* **core:** detach projected views when a parent view is destroyed ([#16592](https://github.com/angular/angular/issues/16592)) ([f0f6544](https://github.com/angular/angular/commit/f0f6544)), closes [#15578](https://github.com/angular/angular/issues/15578)
|
||||
* **core:** projected views should be dirty checked when the declaring component is dirty checked. ([#16592](https://github.com/angular/angular/issues/16592)) ([fcc91d8](https://github.com/angular/angular/commit/fcc91d8)), closes [#14321](https://github.com/angular/angular/issues/14321)
|
||||
* **http:** flatten metadata for @angular/http/testing ([9da6340](https://github.com/angular/angular/commit/9da6340)), closes [#15521](https://github.com/angular/angular/issues/15521)
|
||||
* **http:** flatten metadata for [@angular](https://github.com/angular)/http/testing ([9da6340](https://github.com/angular/angular/commit/9da6340)), closes [#15521](https://github.com/angular/angular/issues/15521)
|
||||
* **http:** honor RequestArgs.search and RequestArgs.params map type ([aef5245](https://github.com/angular/angular/commit/aef5245)), closes [#15761](https://github.com/angular/angular/issues/15761) [#16392](https://github.com/angular/angular/issues/16392)
|
||||
* **http:** introduce encodingHint for text() for better ArrayBuffer support ([7ae7a84](https://github.com/angular/angular/commit/7ae7a84)), closes [#15932](https://github.com/angular/angular/issues/15932) [#16420](https://github.com/angular/angular/issues/16420)
|
||||
* **router:** fix redirect to a URL with a param having multiple values ([#16376](https://github.com/angular/angular/issues/16376)) ([5d4b36f](https://github.com/angular/angular/commit/5d4b36f)), closes [#16310](https://github.com/angular/angular/issues/16310)
|
||||
@ -3170,7 +2937,7 @@ Note: the 4.4.0 release on npm accidentally glitched-out midway, so we cut 4.4.1
|
||||
* **compiler:** avoid a `...null` spread in extraction ([#16547](https://github.com/angular/angular/issues/16547)) ([d0e1688](https://github.com/angular/angular/commit/d0e1688))
|
||||
* **core:** detach projected views when a parent view is destroyed ([#16592](https://github.com/angular/angular/issues/16592)) ([ee6705a](https://github.com/angular/angular/commit/ee6705a)), closes [#15578](https://github.com/angular/angular/issues/15578)
|
||||
* **core:** projected views should be dirty checked when the declaring component is dirty checked. ([#16592](https://github.com/angular/angular/issues/16592)) ([9218812](https://github.com/angular/angular/commit/9218812)), closes [#14321](https://github.com/angular/angular/issues/14321)
|
||||
* **http:** flatten metadata for @angular/http/testing ([9c70a3c](https://github.com/angular/angular/commit/9c70a3c)), closes [#15521](https://github.com/angular/angular/issues/15521)
|
||||
* **http:** flatten metadata for [@angular](https://github.com/angular)/http/testing ([9c70a3c](https://github.com/angular/angular/commit/9c70a3c)), closes [#15521](https://github.com/angular/angular/issues/15521)
|
||||
* **http:** honor RequestArgs.search and RequestArgs.params map type ([63066f7](https://github.com/angular/angular/commit/63066f7)), closes [#15761](https://github.com/angular/angular/issues/15761) [#16392](https://github.com/angular/angular/issues/16392)
|
||||
* **http:** introduce encodingHint for text() for better ArrayBuffer support ([ec3b6e9](https://github.com/angular/angular/commit/ec3b6e9)), closes [#15932](https://github.com/angular/angular/issues/15932) [#16420](https://github.com/angular/angular/issues/16420)
|
||||
* **router:** fix redirect to a URL with a param having multiple values ([#16376](https://github.com/angular/angular/issues/16376)) ([915eae5](https://github.com/angular/angular/commit/915eae5)), closes [#16310](https://github.com/angular/angular/issues/16310)
|
||||
@ -3800,7 +3567,7 @@ Note: the 4.0.0-rc.0 release on npm accidentally omitted one bug fix, so we cut
|
||||
* **core:** add isStable Observable property to ApplicationRef to indicate when it's stable and unstable ([#14337](https://github.com/angular/angular/issues/14337)) ([c481798](https://github.com/angular/angular/commit/c481798))
|
||||
* **platform-server:** add API to render Module and ModuleFactory to string ([#14381](https://github.com/angular/angular/issues/14381)) ([b4d444a](https://github.com/angular/angular/commit/b4d444a))
|
||||
* **platform-server:** Implement PlatformLocation for platformServer() ([#14405](https://github.com/angular/angular/issues/14405)) ([9e28568](https://github.com/angular/angular/commit/9e28568))
|
||||
* **platform-server:** support @angular/http from @angular/platform-server ([9559d3e](https://github.com/angular/angular/commit/9559d3e))
|
||||
* **platform-server:** support [@angular](https://github.com/angular)/http from [@angular](https://github.com/angular)/platform-server ([9559d3e](https://github.com/angular/angular/commit/9559d3e))
|
||||
* **tsc-wrapped:** add an option to `ngc` to bundle metadata ([#14509](https://github.com/angular/angular/issues/14509)) ([3b89670](https://github.com/angular/angular/commit/3b89670))
|
||||
|
||||
|
||||
@ -4761,7 +4528,7 @@ Note: The 2.2.0-beta.0 release also contains all the changes present in the 2.1.
|
||||
* **compiler:** properly shim `:host:before` and `:host(:before)` ([#12171](https://github.com/angular/angular/issues/12171)) ([aa92512](https://github.com/angular/angular/commit/aa92512)), closes [#12165](https://github.com/angular/angular/issues/12165)
|
||||
* **compiler:** validate `@HostBinding` name ([#12139](https://github.com/angular/angular/issues/12139)) ([13ecc14](https://github.com/angular/angular/commit/13ecc14))
|
||||
* **compiler-cli:** don't clone static symbols when simplifying annotation metadata ([#12158](https://github.com/angular/angular/issues/12158)) ([8c477b2](https://github.com/angular/angular/commit/8c477b2))
|
||||
* **compiler-cli:** remove peerDependency on @angular/platform-server ([#12122](https://github.com/angular/angular/issues/12122)) ([71b7654](https://github.com/angular/angular/commit/71b7654))
|
||||
* **compiler-cli:** remove peerDependency on [@angular](https://github.com/angular)/platform-server ([#12122](https://github.com/angular/angular/issues/12122)) ([71b7654](https://github.com/angular/angular/commit/71b7654))
|
||||
* **compiler-cli:** remove unused parse5 dependency from package.json ([eaaec69](https://github.com/angular/angular/commit/eaaec69))
|
||||
* **forms:** allow optional fields with pattern and minlength validators ([#12147](https://github.com/angular/angular/issues/12147)) ([d22eeb7](https://github.com/angular/angular/commit/d22eeb7))
|
||||
* **forms:** properly validate blank strings with minlength ([#12091](https://github.com/angular/angular/issues/12091)) ([f50c1da](https://github.com/angular/angular/commit/f50c1da))
|
||||
|
@ -222,7 +222,6 @@ The following is the list of supported scopes:
|
||||
* **router**
|
||||
* **service-worker**
|
||||
* **upgrade**
|
||||
* **zone.js**
|
||||
|
||||
There are currently a few exceptions to the "use package name" rule:
|
||||
|
||||
|
27
WORKSPACE
27
WORKSPACE
@ -18,11 +18,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",
|
||||
patch_args = ["-p1"],
|
||||
# Patch https://github.com/bazelbuild/rules_nodejs/pull/903
|
||||
patches = ["//tools:rollup_bundle_commonjs_ignoreGlobal.patch"],
|
||||
sha256 = "6d4edbf28ff6720aedf5f97f9b9a7679401bf7fca9d14a0fff80f644a99992b4",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.32.2/rules_nodejs-0.32.2.tar.gz"],
|
||||
sha256 = "e04a82a72146bfbca2d0575947daa60fda1878c8d3a3afe868a8ec39a6b968bb",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.31.1/rules_nodejs-0.31.1.tar.gz"],
|
||||
)
|
||||
|
||||
# Check the bazel version and download npm dependencies
|
||||
@ -30,7 +27,6 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "check_rules
|
||||
|
||||
# Bazel version must be at least the following version because:
|
||||
# - 0.26.0 managed_directories feature added which is required for nodejs rules 0.30.0
|
||||
# - 0.27.0 has a fix for managed_directories after `rm -rf node_modules`
|
||||
check_bazel_version(
|
||||
message = """
|
||||
You no longer need to install Bazel on your machine.
|
||||
@ -39,7 +35,7 @@ Try running `yarn bazel` instead.
|
||||
(If you did run that, check that you've got a fresh `yarn install`)
|
||||
|
||||
""",
|
||||
minimum_bazel_version = "0.27.0",
|
||||
minimum_bazel_version = "0.26.0",
|
||||
)
|
||||
|
||||
# The NodeJS rules version must be at least the following version because:
|
||||
@ -50,10 +46,7 @@ Try running `yarn bazel` instead.
|
||||
# - 0.27.12 Adds NodeModuleSources provider for transtive npm deps support
|
||||
# - 0.30.0 yarn_install now uses symlinked node_modules with new managed directories Bazel 0.26.0 feature
|
||||
# - 0.31.1 entry_point attribute of nodejs_binary & rollup_bundle is now a label
|
||||
# - 0.32.0 yarn_install and npm_install no longer puts build files under symlinked node_modules
|
||||
# - 0.32.1 remove override of @bazel/tsetse & exclude typescript lib declarations in node_module_library transitive_declarations
|
||||
# - 0.32.2 resolves bug in @bazel/hide-bazel-files postinstall step
|
||||
check_rules_nodejs_version(minimum_version_string = "0.32.2")
|
||||
check_rules_nodejs_version(minimum_version_string = "0.31.1")
|
||||
|
||||
# Setup the Node.js toolchain
|
||||
node_repositories(
|
||||
@ -77,7 +70,19 @@ node_repositories(
|
||||
|
||||
yarn_install(
|
||||
name = "npm",
|
||||
data = [
|
||||
"//:tools/npm/@angular_bazel/index.js",
|
||||
"//:tools/npm/@angular_bazel/package.json",
|
||||
"//:tools/postinstall-patches.js",
|
||||
"//:tools/yarn/check-yarn.js",
|
||||
],
|
||||
package_json = "//:package.json",
|
||||
# Don't install devDependencies, they are large and not used under Bazel
|
||||
prod_only = True,
|
||||
# Temporarily disable node_modules symlinking until the fix for
|
||||
# https://github.com/bazelbuild/bazel/issues/8487 makes it into a
|
||||
# future Bazel release
|
||||
symlink_node_modules = False,
|
||||
yarn_lock = "//:yarn.lock",
|
||||
)
|
||||
|
||||
|
@ -7,7 +7,7 @@ The Angular CLI is a command-line interface tool that you use to initialize, dev
|
||||
Major versions of Angular CLI follow the supported major version of Angular, but minor versions can be released separately.
|
||||
|
||||
Install the CLI using the `npm` package manager:
|
||||
<code-example language="bash">
|
||||
<code-example format="." language="bash">
|
||||
npm install -g @angular/cli
|
||||
</code-example>
|
||||
|
||||
@ -20,14 +20,14 @@ Invoke the tool on the command line through the `ng` executable.
|
||||
Online help is available on the command line.
|
||||
Enter the following to list commands or options for a given command (such as [generate](cli/generate)) with a short description.
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example format="." language="bash">
|
||||
ng help
|
||||
ng generate --help
|
||||
</code-example>
|
||||
|
||||
To create, build, and serve a new, basic Angular project on a development server, go to the parent directory of your new workspace use the following commands:
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example format="." language="bash">
|
||||
ng new my-first-project
|
||||
cd my-first-project
|
||||
ng serve
|
||||
@ -36,14 +36,6 @@ ng serve
|
||||
In your browser, open http://localhost:4200/ to see the new app run.
|
||||
When you use the [ng serve](cli/serve) command to build an app and serve it locally, the server automatically rebuilds the app and reloads the page when you change any of the source files.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
When you run `ng new my-first-project` a new folder, named `my-first-project`, will be created in the current working directory. Since you want to be able to create files inside that folder, make sure you have sufficient rights in the current working directory before running the command.
|
||||
|
||||
If the current working directory is not the right place for your project, you can change to a more appropriate directory by running `cd <path-to-other-directory>` first.
|
||||
|
||||
</div>
|
||||
|
||||
## Workspaces and project files
|
||||
|
||||
The [ng new](cli/new) command creates an *Angular workspace* folder and generates a new app skeleton.
|
||||
@ -82,8 +74,8 @@ Command syntax is shown as follows:
|
||||
* Option names are prefixed with a double dash (--).
|
||||
Option aliases are prefixed with a single dash (-).
|
||||
Arguments are not prefixed.
|
||||
For example:
|
||||
<code-example language="bash">
|
||||
For example:
|
||||
<code-example format="." language="bash">
|
||||
ng build my-app -c production
|
||||
</code-example>
|
||||
|
||||
@ -113,5 +105,5 @@ Schematic options are supplied to the command in the same format as immediate co
|
||||
|
||||
### Building with Bazel
|
||||
|
||||
Optionally, you can configure the Angular CLI to use [Bazel](https://docs.bazel.build) as the build tool. For more information, see [Building with Bazel](guide/bazel).
|
||||
Optionally, you can configure the Angular CLI to use [Bazel](https://docs.bazel.build) as the build tool. For more information, see [Building with Bazel](guide/bazel).
|
||||
|
||||
|
@ -25,8 +25,8 @@ describe('Attribute directives', () => {
|
||||
greenRb.click();
|
||||
browser.actions().mouseMove(highlightedEle).perform();
|
||||
|
||||
// Wait for up to 4s for the background color to be updated,
|
||||
// Wait for up to 2s for the background color to be updated,
|
||||
// to account for slow environments (e.g. CI).
|
||||
browser.wait(() => highlightedEle.getCssValue('background-color').then(c => c === lightGreen), 4000);
|
||||
browser.wait(() => highlightedEle.getCssValue('background-color').then(c => c === lightGreen), 2000);
|
||||
});
|
||||
});
|
||||
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 32 KiB |
@ -18,7 +18,7 @@ export class AppComponent {
|
||||
}
|
||||
|
||||
deleteItem(item: Item) {
|
||||
alert(`Delete the ${item.name}.`);
|
||||
alert(`Delete the ${item}.`);
|
||||
}
|
||||
|
||||
onClickMe(event?: KeyboardEvent) {
|
||||
|
@ -16,20 +16,16 @@
|
||||
<!-- #enddocregion checkout-form-1 -->
|
||||
|
||||
<div>
|
||||
<label for="name">
|
||||
Name
|
||||
</label>
|
||||
<input id="name" type="text" formControlName="name">
|
||||
<label>Name</label>
|
||||
<input type="text" formControlName="name">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="address">
|
||||
Address
|
||||
</label>
|
||||
<input id="address" type="text" formControlName="address">
|
||||
<label>Address</label>
|
||||
<input type="text" formControlName="address">
|
||||
</div>
|
||||
|
||||
<button class="button" type="submit">Purchase</button>
|
||||
|
||||
|
||||
<!-- #docregion checkout-form-1 -->
|
||||
</form>
|
||||
|
@ -10,6 +10,7 @@ export class AppComponent {
|
||||
gender = 'female';
|
||||
fly = true;
|
||||
logo = 'https://angular.io/assets/images/logos/angular/angular.png';
|
||||
heroes: string[] = ['Magneta', 'Celeritas', 'Dynama'];
|
||||
inc(i: number) {
|
||||
this.minutes = Math.min(5, Math.max(0, this.minutes + i));
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
// imports
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
// @NgModule decorator with its metadata
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
imports: [BrowserModule],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {}
|
@ -7,9 +7,9 @@ import { from } from 'rxjs';
|
||||
const data = from(fetch('/api/endpoint'));
|
||||
// Subscribe to begin listening for async result
|
||||
data.subscribe({
|
||||
next(response) { console.log(response); },
|
||||
error(err) { console.error('Error: ' + err); },
|
||||
complete() { console.log('Completed'); }
|
||||
next(response) { console.log(response); },
|
||||
error(err) { console.error('Error: ' + err); },
|
||||
complete() { console.log('Completed'); }
|
||||
});
|
||||
|
||||
// #enddocregion promise
|
||||
|
@ -2,49 +2,42 @@
|
||||
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
// TODO Not yet complete
|
||||
describe('Template Syntax', () => {
|
||||
// Not yet complete
|
||||
describe('Template Syntax', function () {
|
||||
|
||||
beforeAll(() => {
|
||||
beforeAll(function () {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
it('should be able to use interpolation with a hero', () => {
|
||||
const heroInterEle = element.all(by.css('h2+p')).get(0);
|
||||
it('should be able to use interpolation with a hero', function () {
|
||||
let heroInterEle = element.all(by.css('h2+p')).get(0);
|
||||
expect(heroInterEle.getText()).toEqual('My current hero is Hercules');
|
||||
});
|
||||
|
||||
it('should be able to use interpolation with a calculation', () => {
|
||||
const theSumEles = element.all(by.cssContainingText('h3~p', 'The sum of'));
|
||||
it('should be able to use interpolation with a calculation', function () {
|
||||
let theSumEles = element.all(by.cssContainingText('h3~p', 'The sum of'));
|
||||
expect(theSumEles.count()).toBe(2);
|
||||
expect(theSumEles.get(0).getText()).toEqual('The sum of 1 + 1 is 2');
|
||||
expect(theSumEles.get(1).getText()).toEqual('The sum of 1 + 1 is not 4');
|
||||
});
|
||||
|
||||
it('should be able to use class binding syntax', () => {
|
||||
const specialEle = element(by.cssContainingText('div', 'Special'));
|
||||
it('should be able to use class binding syntax', function () {
|
||||
let specialEle = element(by.cssContainingText('div', 'Special'));
|
||||
expect(specialEle.getAttribute('class')).toMatch('special');
|
||||
});
|
||||
|
||||
it('should be able to use style binding syntax', () => {
|
||||
const specialButtonEle = element(by.cssContainingText('div.special~button', 'button'));
|
||||
it('should be able to use style binding syntax', function () {
|
||||
let specialButtonEle = element(by.cssContainingText('div.special~button', 'button'));
|
||||
expect(specialButtonEle.getAttribute('style')).toMatch('color: red');
|
||||
});
|
||||
|
||||
it('should two-way bind to sizer', async () => {
|
||||
const div = element(by.css('div#two-way-1'));
|
||||
const incButton = div.element(by.buttonText('+'));
|
||||
const input = div.element(by.css('input'));
|
||||
const initSize = await input.getAttribute('value');
|
||||
let div = element(by.css('div#two-way-1'));
|
||||
let incButton = div.element(by.buttonText('+'));
|
||||
let input = div.element(by.css('input'));
|
||||
let initSize = await input.getAttribute('value');
|
||||
incButton.click();
|
||||
expect(input.getAttribute('value')).toEqual((+initSize + 1).toString());
|
||||
});
|
||||
|
||||
it('should change SVG rectangle\'s fill color on click', async () => {
|
||||
const div = element(by.css('app-svg'));
|
||||
const colorSquare = div.element(by.css('rect'));
|
||||
const initialColor = await colorSquare.getAttribute('fill');
|
||||
colorSquare.click();
|
||||
expect(colorSquare.getAttribute('fill')).not.toEqual(initialColor);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -38,7 +38,6 @@
|
||||
<a href="#safe-navigation-operator">Safe navigation operator <i>?.</i></a><br>
|
||||
<a href="#non-null-assertion-operator">Non-null assertion operator <i>!.</i></a><br>
|
||||
<a href="#enums">Enums</a><br>
|
||||
<a href="#svg-templates">SVG Templates</a><br>
|
||||
|
||||
<!-- Interpolation and expressions -->
|
||||
<hr><h2 id="interpolation">Interpolation</h2>
|
||||
@ -443,7 +442,7 @@ button</button>
|
||||
|
||||
<!-- #docregion without-NgModel -->
|
||||
<input [value]="currentHero.name"
|
||||
(input)="updateCurrentHeroName($event)">
|
||||
(input)="currentHero.name=$event.target.value" >
|
||||
<!-- #enddocregion without-NgModel -->
|
||||
without NgModel
|
||||
<br>
|
||||
@ -753,7 +752,7 @@ bindon-ngModel
|
||||
|
||||
<div>
|
||||
<!-- pipe price to USD and display the $ symbol -->
|
||||
<label>Price: </label>{{product.price | currency:'USD':'symbol'}}
|
||||
<label>Price: </label>{{product.price | currency:'USD':true}}
|
||||
</div>
|
||||
|
||||
<a class="to-toc" href="#toc">top</a>
|
||||
@ -858,9 +857,3 @@ The null hero's name is {{nullHero && nullHero.name}}
|
||||
</p>
|
||||
|
||||
<a class="to-toc" href="#toc">top</a>
|
||||
|
||||
<hr><h2 id="svg-templates">SVG Templates</h2>
|
||||
<!-- #docregion svg-templates -->
|
||||
<app-svg></app-svg>
|
||||
<!-- #enddocregion svg-templates -->
|
||||
<a class="to-toc" href="#toc">top</a>
|
||||
|
@ -5,7 +5,7 @@ import { AfterViewInit, Component, ElementRef, OnInit, QueryList, ViewChildren }
|
||||
|
||||
import { Hero } from './hero';
|
||||
|
||||
export enum Color {Red, Green, Blue}
|
||||
export enum Color {Red, Green, Blue};
|
||||
|
||||
/**
|
||||
* Giant grab bag of stuff to drive the chapter
|
||||
@ -29,7 +29,7 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
trackChanges(this.heroesWithTrackBy, () => this.heroesWithTrackByCount++);
|
||||
}
|
||||
|
||||
@ViewChildren('noTrackBy') heroesNoTrackBy: QueryList<ElementRef>;
|
||||
@ViewChildren('noTrackBy') heroesNoTrackBy: QueryList<ElementRef>;
|
||||
@ViewChildren('withTrackBy') heroesWithTrackBy: QueryList<ElementRef>;
|
||||
|
||||
actionName = 'Go for it';
|
||||
@ -66,10 +66,6 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
|
||||
currentHero: Hero;
|
||||
|
||||
updateCurrentHeroName(event: Event) {
|
||||
this.currentHero.name = (event.target as any).value;
|
||||
}
|
||||
|
||||
deleteHero(hero?: Hero) {
|
||||
this.alert(`Delete ${hero ? hero.name : 'the hero'}.`);
|
||||
}
|
||||
@ -109,13 +105,13 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
|
||||
get nullHero(): Hero { return null; }
|
||||
|
||||
onClickMe(event?: MouseEvent) {
|
||||
const evtMsg = event ? ' Event target class is ' + (event.target as HTMLElement).className : '';
|
||||
onClickMe(event?: KeyboardEvent) {
|
||||
let evtMsg = event ? ' Event target class is ' + (<HTMLElement>event.target).className : '';
|
||||
this.alert('Click me.' + evtMsg);
|
||||
}
|
||||
|
||||
onSave(event?: MouseEvent) {
|
||||
const evtMsg = event ? ' Event target is ' + (event.target as HTMLElement).textContent : '';
|
||||
onSave(event?: KeyboardEvent) {
|
||||
let evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).textContent : '';
|
||||
this.alert('Saved.' + evtMsg);
|
||||
if (event) { event.stopPropagation(); }
|
||||
}
|
||||
@ -144,9 +140,9 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
setCurrentClasses() {
|
||||
// CSS classes: added/removed per current state of component properties
|
||||
this.currentClasses = {
|
||||
saveable: this.canSave,
|
||||
modified: !this.isUnchanged,
|
||||
special: this.isSpecial
|
||||
'saveable': this.canSave,
|
||||
'modified': !this.isUnchanged,
|
||||
'special': this.isSpecial
|
||||
};
|
||||
}
|
||||
// #enddocregion setClasses
|
||||
@ -168,7 +164,7 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
// #enddocregion trackByHeroes
|
||||
|
||||
// #docregion trackById
|
||||
trackById(index: number, item: any): number { return item.id; }
|
||||
trackById(index: number, item: any): number { return item['id']; }
|
||||
// #enddocregion trackById
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,13 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { BigHeroDetailComponent, HeroDetailComponent } from './hero-detail.component';
|
||||
import { ClickDirective, ClickDirective2 } from './click.directive';
|
||||
import { HeroFormComponent } from './hero-form.component';
|
||||
import { heroSwitchComponents } from './hero-switch.components';
|
||||
import { SizerComponent } from './sizer.component';
|
||||
import { SvgComponent } from './svg.component';
|
||||
import { HeroFormComponent } from './hero-form.component';
|
||||
import { heroSwitchComponents } from './hero-switch.components';
|
||||
import { SizerComponent } from './sizer.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -23,8 +22,7 @@ import { SvgComponent } from './svg.component';
|
||||
heroSwitchComponents,
|
||||
ClickDirective,
|
||||
ClickDirective2,
|
||||
SizerComponent,
|
||||
SvgComponent
|
||||
SizerComponent
|
||||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* tslint:disable directive-selector directive-class-suffix */
|
||||
/* tslint:disable use-output-property-decorator directive-class-suffix */
|
||||
// #docplaster
|
||||
import { Directive, ElementRef, EventEmitter, Output } from '@angular/core';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Component, Input, ViewChild } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { NgForm } from '@angular/forms';
|
||||
|
||||
import { Hero } from './hero';
|
||||
|
||||
@ -15,11 +15,10 @@ export class HeroFormComponent {
|
||||
@Input() hero: Hero;
|
||||
@ViewChild('heroForm', {static: false}) form: NgForm;
|
||||
|
||||
// tslint:disable-next-line:variable-name
|
||||
private _submitMessage = '';
|
||||
|
||||
get submitMessage() {
|
||||
if (this.form && !this.form.valid) {
|
||||
if (!this.form.valid) {
|
||||
this._submitMessage = '';
|
||||
}
|
||||
return this._submitMessage;
|
||||
|
@ -1,4 +0,0 @@
|
||||
svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
<svg>
|
||||
<g>
|
||||
<rect x="0" y="0" width="100" height="100" [attr.fill]="fillColor" (click)="changeColor()" />
|
||||
<text x="120" y="50">click the rectangle to change the fill color</text>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 201 B |
@ -1,17 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-svg',
|
||||
templateUrl: './svg.component.svg',
|
||||
styleUrls: ['./svg.component.css']
|
||||
})
|
||||
export class SvgComponent {
|
||||
fillColor = 'rgb(255, 0, 0)';
|
||||
|
||||
changeColor() {
|
||||
const r = Math.floor(Math.random() * 256);
|
||||
const g = Math.floor(Math.random() * 256);
|
||||
const b = Math.floor(Math.random() * 256);
|
||||
this.fillColor = `rgb(${r}, ${g}, ${b})`;
|
||||
}
|
||||
}
|
@ -1,4 +1,8 @@
|
||||
/* HeroesComponent's private CSS styles */
|
||||
.selected {
|
||||
background-color: #CFD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
.heroes {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
@ -15,18 +19,18 @@
|
||||
height: 1.6em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.heroes li.selected:hover {
|
||||
background-color: #BBD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
.heroes li:hover {
|
||||
color: #607D8B;
|
||||
background-color: #DDD;
|
||||
left: .1em;
|
||||
}
|
||||
.heroes li.selected {
|
||||
background-color: #CFD8DC;
|
||||
color: white;
|
||||
}
|
||||
.heroes li.selected:hover {
|
||||
background-color: #BBD8DC;
|
||||
color: white;
|
||||
.heroes .text {
|
||||
position: relative;
|
||||
top: -3px;
|
||||
}
|
||||
.heroes .badge {
|
||||
display: inline-block;
|
||||
|
@ -34,7 +34,4 @@ export class HeroesComponent implements OnInit {
|
||||
this.selectedHero = hero;
|
||||
}
|
||||
// #enddocregion on-select
|
||||
// #docregion component
|
||||
}
|
||||
// #enddocregion component
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
/* HeroesComponent's private CSS styles */
|
||||
.selected {
|
||||
background-color: #CFD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
.heroes {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
@ -15,18 +19,18 @@
|
||||
height: 1.6em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.heroes li.selected:hover {
|
||||
background-color: #BBD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
.heroes li:hover {
|
||||
color: #607D8B;
|
||||
background-color: #DDD;
|
||||
left: .1em;
|
||||
}
|
||||
.heroes li.selected {
|
||||
background-color: #CFD8DC;
|
||||
color: white;
|
||||
}
|
||||
.heroes li.selected:hover {
|
||||
background-color: #BBD8DC;
|
||||
color: white;
|
||||
.heroes .text {
|
||||
position: relative;
|
||||
top: -3px;
|
||||
}
|
||||
.heroes .badge {
|
||||
display: inline-block;
|
||||
|
@ -1,4 +1,8 @@
|
||||
/* HeroesComponent's private CSS styles */
|
||||
.selected {
|
||||
background-color: #CFD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
.heroes {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
@ -15,18 +19,18 @@
|
||||
height: 1.6em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.heroes li.selected:hover {
|
||||
background-color: #BBD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
.heroes li:hover {
|
||||
color: #607D8B;
|
||||
background-color: #DDD;
|
||||
left: .1em;
|
||||
}
|
||||
.heroes li.selected {
|
||||
background-color: #CFD8DC;
|
||||
color: white;
|
||||
}
|
||||
.heroes li.selected:hover {
|
||||
background-color: #BBD8DC;
|
||||
color: white;
|
||||
.heroes .text {
|
||||
position: relative;
|
||||
top: -3px;
|
||||
}
|
||||
.heroes .badge {
|
||||
display: inline-block;
|
||||
@ -39,6 +43,8 @@
|
||||
left: -1px;
|
||||
top: -4px;
|
||||
height: 1.8em;
|
||||
min-width: 16px;
|
||||
text-align: right;
|
||||
margin-right: .8em;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { HeroesComponent } from './heroes/heroes.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'heroes', component: HeroesComponent }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
@ -7,7 +7,9 @@ import { RouterModule, Routes } from '@angular/router';
|
||||
// #docregion import-dashboard
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
// #enddocregion import-dashboard
|
||||
// #docregion heroes-route
|
||||
import { HeroesComponent } from './heroes/heroes.component';
|
||||
// #enddocregion heroes-route
|
||||
// #docregion import-herodetail
|
||||
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
|
||||
// #enddocregion import-herodetail
|
||||
@ -37,9 +39,7 @@ const routes: Routes = [
|
||||
imports: [ RouterModule.forRoot(routes) ],
|
||||
// #enddocregion ngmodule-imports
|
||||
// #docregion v1
|
||||
// #docregion export-routermodule
|
||||
exports: [ RouterModule ]
|
||||
// #enddocregion export-routermodule
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
// #enddocregion , v1
|
||||
|
@ -23,17 +23,13 @@ import { HeroSearchComponent } from './hero-search/hero-search.component';
|
||||
// #docregion v1
|
||||
import { MessagesComponent } from './messages/messages.component';
|
||||
|
||||
// #docregion import-httpclientmodule
|
||||
@NgModule({
|
||||
imports: [
|
||||
// #enddocregion import-httpclientmodule
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
AppRoutingModule,
|
||||
// #docregion in-mem-web-api-imports
|
||||
// #docregion import-httpclientmodule
|
||||
HttpClientModule,
|
||||
// #enddocregion import-httpclientmodule
|
||||
|
||||
// The HttpClientInMemoryWebApiModule module intercepts HTTP requests
|
||||
// and returns simulated server responses.
|
||||
@ -42,9 +38,7 @@ import { MessagesComponent } from './messages/messages.component';
|
||||
InMemoryDataService, { dataEncapsulation: false }
|
||||
)
|
||||
// #enddocregion in-mem-web-api-imports
|
||||
// #docregion import-httpclientmodule
|
||||
],
|
||||
// #enddocregion import-httpclientmodule
|
||||
declarations: [
|
||||
AppComponent,
|
||||
DashboardComponent,
|
||||
@ -56,9 +50,6 @@ import { MessagesComponent } from './messages/messages.component';
|
||||
// #docregion v1
|
||||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
// #docregion import-httpclientmodule
|
||||
})
|
||||
// #enddocregion import-httpclientmodule
|
||||
|
||||
export class AppModule { }
|
||||
// #enddocregion , v1
|
||||
|
@ -13,6 +13,11 @@ import { catchError, map, tap } from 'rxjs/operators';
|
||||
import { Hero } from './hero';
|
||||
import { MessageService } from './message.service';
|
||||
|
||||
// #docregion http-options
|
||||
const httpOptions = {
|
||||
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
|
||||
};
|
||||
// #enddocregion http-options
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class HeroService {
|
||||
@ -21,12 +26,6 @@ export class HeroService {
|
||||
private heroesUrl = 'api/heroes'; // URL to web api
|
||||
// #enddocregion heroesUrl
|
||||
|
||||
// #docregion http-options
|
||||
httpOptions = {
|
||||
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
|
||||
};
|
||||
// #enddocregion http-options
|
||||
|
||||
// #docregion ctor
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
@ -97,7 +96,7 @@ export class HeroService {
|
||||
// #docregion addHero
|
||||
/** POST: add a new hero to the server */
|
||||
addHero (hero: Hero): Observable<Hero> {
|
||||
return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe(
|
||||
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
|
||||
tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),
|
||||
catchError(this.handleError<Hero>('addHero'))
|
||||
);
|
||||
@ -110,7 +109,7 @@ export class HeroService {
|
||||
const id = typeof hero === 'number' ? hero : hero.id;
|
||||
const url = `${this.heroesUrl}/${id}`;
|
||||
|
||||
return this.http.delete<Hero>(url, this.httpOptions).pipe(
|
||||
return this.http.delete<Hero>(url, httpOptions).pipe(
|
||||
tap(_ => this.log(`deleted hero id=${id}`)),
|
||||
catchError(this.handleError<Hero>('deleteHero'))
|
||||
);
|
||||
@ -120,7 +119,7 @@ export class HeroService {
|
||||
// #docregion updateHero
|
||||
/** PUT: update the hero on the server */
|
||||
updateHero (hero: Hero): Observable<any> {
|
||||
return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(
|
||||
return this.http.put(this.heroesUrl, hero, httpOptions).pipe(
|
||||
tap(_ => this.log(`updated hero id=${hero.id}`)),
|
||||
catchError(this.handleError<any>('updateHero'))
|
||||
);
|
||||
|
@ -1,79 +0,0 @@
|
||||
import { browser, element, by, ExpectedConditions } from 'protractor';
|
||||
|
||||
describe('Lazy Loading AngularJS Tests', function () {
|
||||
const pageElements = {
|
||||
homePageHref: element(by.cssContainingText('app-root nav a', 'Home')),
|
||||
homePageParagraph: element(by.css('app-root app-home p')),
|
||||
ajsUsersPageHref: element(by.cssContainingText('app-root nav a', 'Users')),
|
||||
ajsUsersPageParagraph: element(by.css('app-root app-angular-js div p')),
|
||||
notFoundPageHref: element(by.cssContainingText('app-root nav a', '404 Page')),
|
||||
notFoundPageParagraph: element(by.css('app-root app-app404 p')),
|
||||
};
|
||||
|
||||
beforeAll(async() => {
|
||||
await browser.get('/');
|
||||
});
|
||||
|
||||
it('should display \'Angular Home\' when visiting the home page', async() => {
|
||||
await pageElements.homePageHref.click();
|
||||
|
||||
const paragraphText = await pageElements.homePageParagraph.getText();
|
||||
|
||||
expect(paragraphText).toEqual('Angular Home');
|
||||
});
|
||||
|
||||
it('should display \'Users Page\' page when visiting the AngularJS page at /users', async() => {
|
||||
await pageElements.ajsUsersPageHref.click();
|
||||
await loadAngularJS();
|
||||
|
||||
const paragraphText = await pageElements.ajsUsersPageParagraph.getText();
|
||||
|
||||
expect(paragraphText).toEqual('Users Page');
|
||||
});
|
||||
|
||||
it('should display \'Angular 404\' when visiting an invalid URL', async() => {
|
||||
await pageElements.notFoundPageHref.click();
|
||||
|
||||
const paragraphText = await pageElements.notFoundPageParagraph.getText();
|
||||
|
||||
expect(paragraphText).toEqual('Angular 404');
|
||||
});
|
||||
|
||||
// Workaround for https://github.com/angular/protractor/issues/4724
|
||||
async function loadAngularJS() {
|
||||
// Abort if `resumeBootstrap` has already occured
|
||||
if (await browser.executeScript(`return '__TESTABILITY__NG1_APP_ROOT_INJECTOR__' in window;`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Might have to re-insert the 'NG_DEFER_BOOTSTRAP!' if the name has been changed since protractor loaded the page
|
||||
if (!await browser.executeScript('window.name.includes(\'NG_DEFER_BOOTSTRAP!\')')) {
|
||||
await browser.executeScript('window.name = \'NG_DEFER_BOOTSTRAP!\' + name');
|
||||
}
|
||||
|
||||
// Wait for the AngularJS bundle to download and initialize
|
||||
await browser.wait(ExpectedConditions.presenceOf(element(by.css('app-root app-angular-js'))), 5000, 'AngularJS app');
|
||||
|
||||
// Run the protractor pre-bootstrap logic and resumeBootstrap
|
||||
// Based on https://github.com/angular/protractor/blob/5.3.0/lib/browser.ts#L950-L969
|
||||
{
|
||||
let moduleNames = [];
|
||||
for (const {name, script, args} of browser.mockModules_) {
|
||||
moduleNames.push(name);
|
||||
await browser.executeScriptWithDescription(script, 'add mock module ' + name, ...args);
|
||||
}
|
||||
|
||||
await browser.executeScriptWithDescription(
|
||||
// TODO: must manually assign __TESTABILITY__NG1_APP_ROOT_INJECTOR__ (https://github.com/angular/angular/issues/22723)
|
||||
`window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__ = angular.resumeBootstrap(arguments[0]) `
|
||||
+ `|| angular.element('app-angular-js').injector();`,
|
||||
'resume bootstrap',
|
||||
moduleNames
|
||||
);
|
||||
}
|
||||
|
||||
// Wait for the initial AngularJS page to finish loading
|
||||
await browser.waitForAngular();
|
||||
}
|
||||
});
|
||||
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"projectType": "cli-ajs"
|
||||
}
|
@ -1,22 +1,14 @@
|
||||
import { Component, OnInit, OnDestroy, ElementRef } from '@angular/core';
|
||||
import { Component, OnInit, ElementRef } from '@angular/core';
|
||||
import { LazyLoaderService } from '../lazy-loader.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-angular-js',
|
||||
template: '<div ng-view></div>'
|
||||
})
|
||||
export class AngularJSComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private lazyLoader: LazyLoaderService,
|
||||
private elRef: ElementRef
|
||||
) {}
|
||||
export class AngularJSComponent implements OnInit {
|
||||
constructor(private lazyLoader: LazyLoaderService, private elRef: ElementRef) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.lazyLoader.load(this.elRef.nativeElement);
|
||||
}
|
||||
|
||||
|
||||
ngOnDestroy() {
|
||||
this.lazyLoader.destroy();
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import { App404Component } from './app404/app404.component';
|
||||
BrowserModule,
|
||||
AppRoutingModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
@ -1,25 +1,23 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import * as angular from 'angular';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LazyLoaderService {
|
||||
private app: angular.auto.IInjectorService;
|
||||
bootstrapped = false;
|
||||
|
||||
load(el: HTMLElement): void {
|
||||
if (this.bootstrapped) {
|
||||
return;
|
||||
}
|
||||
|
||||
import('./angularjs-app').then(app => {
|
||||
try {
|
||||
this.app = app.bootstrap(el);
|
||||
app.bootstrap(el);
|
||||
this.bootstrapped = true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.app) {
|
||||
this.app.get('$rootScope').$destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,181 +0,0 @@
|
||||
# Accessibility in Angular
|
||||
|
||||
The web is used by a wide variety of people, including those who have visual or motor impairments.
|
||||
A variety of assistive technologies are available that make it much easier for these groups to
|
||||
interact with web-based software applications.
|
||||
In addition, designing an application to be more accessible generally improves the user experience for all users.
|
||||
|
||||
For an in-depth introduction to issues and techniques for designing accessible applications, see the [Accessibility](https://developers.google.com/web/fundamentals/accessibility/#what_is_accessibility) section of the Google's [Web Fundamentals](https://developers.google.com/web/fundamentals/).
|
||||
|
||||
This page discusses best practices for designing Angular applications that
|
||||
work well for all users, including those who rely on assistive technologies.
|
||||
|
||||
## Accessibility attributes
|
||||
|
||||
Building accessible web experience often involves setting [ARIA attributes](https://developers.google.com/web/fundamentals/accessibility/semantics-aria)
|
||||
to provide semantic meaning where it might otherwise be missing.
|
||||
Use [attribute binding](guide/template-syntax#attribute-binding) template syntax to control the values of accessibility-related attributes.
|
||||
|
||||
When binding to ARIA attributes in Angular, you must use the `attr.` prefix, as the ARIA
|
||||
specification depends specifically on HTML attributes rather than properties on DOM elements.
|
||||
|
||||
```html
|
||||
<!-- Use attr. when binding to an ARIA attribute -->
|
||||
<button [attr.aria-label]="myActionLabel">...</button>
|
||||
```
|
||||
|
||||
Note that this syntax is only necessary for attribute _bindings_.
|
||||
Static ARIA attributes require no extra syntax.
|
||||
|
||||
```html
|
||||
<!-- Static ARIA attributes require no extra syntax -->
|
||||
<button aria-label="Save document">...</button>
|
||||
```
|
||||
|
||||
NOTE:
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
By convention, HTML attributes use lowercase names (`tabindex`), while properties use camelCase names (`tabIndex`).
|
||||
|
||||
See the [Template Syntax](https://angular.io/guide/template-syntax#html-attribute-vs-dom-property) guide for more background on the difference between attributes and properties.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## Angular UI components
|
||||
|
||||
The [Angular Material](https://material.angular.io/) library, which is maintained by the Angular team, is a suite of reusable UI components that aims to be fully accessible.
|
||||
The [Component Development Kit (CDK)](https://material.angular.io/cdk/categories) includes the `a11y` package that provides tools to support various areas of accessibility.
|
||||
For example:
|
||||
|
||||
* `LiveAnnouncer` is used to announce messages for screen-reader users using an `aria-live` region. See the W3C documentation for more information on [aria-live regions](https://www.w3.org/WAI/PF/aria-1.1/states_and_properties#aria-live).
|
||||
|
||||
* The `cdkTrapFocus` directive traps Tab-key focus within an element. Use it to create accessible experience for components like modal dialogs, where focus must be constrained.
|
||||
|
||||
For full details of these and other tools, see the [Angular CDK accessibility overview](https://material.angular.io/cdk/a11y/overview).
|
||||
|
||||
|
||||
### Augmenting native elements
|
||||
|
||||
Native HTML elements capture a number of standard interaction patterns that are important to accessibility.
|
||||
When authoring Angular components, you should re-use these native elements directly when possible, rather than re-implementing well-supported behaviors.
|
||||
|
||||
For example, instead of creating a custom element for a new variety of button, you can create a component that uses an attribute selector with a native `<button>` element.
|
||||
This most commonly applies to `<button>` and `<a>`, but can be used with many other types of element.
|
||||
|
||||
You can see examples of this pattern in Angular Material: [`MatButton`](https://github.com/angular/components/blob/master/src/material/button/button.ts#L66-L68), [`MatTabNav`](https://github.com/angular/components/blob/master/src/material/tabs/tab-nav-bar/tab-nav-bar.ts#L67), [`MatTable`](https://github.com/angular/components/blob/master/src/material/table/table.ts#L17).
|
||||
|
||||
### Using containers for native elements
|
||||
|
||||
Sometimes using the appropriate native element requires a container element.
|
||||
For example, the native `<input>` element cannot have children, so any custom text entry components need
|
||||
to wrap an `<input>` with additional elements.
|
||||
While you might just include the `<input>` in your custom component's template,
|
||||
this makes it impossible for users of the component to set arbitrary properties and attributes to the input element.
|
||||
Instead, you can create a container component that uses content projection to include the native control in the
|
||||
component's API.
|
||||
|
||||
You can see [`MatFormField`](https://material.angular.io/components/form-field/overview) as an example of this pattern.
|
||||
|
||||
## Case study: Building a custom progress bar
|
||||
|
||||
The following example shows how to make a simple progress bar accessible by using host binding to control accessibility-related attributes.
|
||||
|
||||
* The component defines an accessibility-enabled element with both the standard HTML attribute `role`, and ARIA attributes. The ARIA attribute `aria-valuenow` is bound to the user's input.
|
||||
|
||||
```ts
|
||||
import { Component, Input } from '@angular/core';
|
||||
/**
|
||||
* Example progressbar component.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'example-progressbar',
|
||||
template: `<div class="bar" [style.width.%]="value"></div>`,
|
||||
styleUrls: ['./progress-bar.css'],
|
||||
host: {
|
||||
// Sets the role for this component to "progressbar"
|
||||
role: 'progressbar',
|
||||
|
||||
// Sets the minimum and maximum values for the progressbar role.
|
||||
'aria-valuemin': '0',
|
||||
'aria-valuemax': '0',
|
||||
|
||||
// Binding that updates the current value of the progressbar.
|
||||
'[attr.aria-valuenow]': 'value',
|
||||
}
|
||||
})
|
||||
export class ExampleProgressbar {
|
||||
/** Current value of the progressbar. */
|
||||
@Input() value: number = 0;
|
||||
}
|
||||
```
|
||||
|
||||
* In the template, the `aria-label` attribute ensures that the control is accessible to screen readers.
|
||||
|
||||
```html
|
||||
<label>
|
||||
Enter an example progress value
|
||||
<input type="number" min="0" max="100"
|
||||
[value]="progress" (input)="progress = $event.target.value">
|
||||
</label>
|
||||
|
||||
<!-- The user of the progressbar sets an aria-label to communicate what the progress means. -->
|
||||
<example-progressbar [value]="progress" aria-label="Example of a progress bar">
|
||||
</example-progressbar>
|
||||
```
|
||||
|
||||
[See the full example in StackBlitz](https://stackblitz.com/edit/angular-kn5jdi?file=src%2Fapp%2Fapp.component.html).
|
||||
|
||||
## Routing and focus management
|
||||
|
||||
Tracking and controlling [focus](https://developers.google.com/web/fundamentals/accessibility/focus/) in a UI is an important consideration in designing for accessibility.
|
||||
When using Angular routing, you should decide where page focus goes upon navigation.
|
||||
|
||||
To avoid relying solely on visual cues, you need to make sure your routing code updates focus after page navigation.
|
||||
Use the `NavigationEnd` event from the `Router` service to know when to update
|
||||
focus.
|
||||
|
||||
The following example shows how to find and focus the main content header in the DOM after navigation.
|
||||
|
||||
```ts
|
||||
|
||||
router.events.pipe(filter(e => e instanceof NavigationEnd)).subscribe(() => {
|
||||
const mainHeader = document.querySelector('#main-content-header')
|
||||
if (mainHeader) {
|
||||
mainHeader.focus();
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
In a real application, the element that receives focus will depend on your specific
|
||||
application structure and layout.
|
||||
The focused element should put users in a position to immediately move into the main content that has just been routed into view.
|
||||
You should avoid situations where focus returns to the `body` element after a route change.
|
||||
|
||||
|
||||
## Additional resources
|
||||
|
||||
* [Accessibility - Google Web Fundamentals](https://developers.google.com/web/fundamentals/accessibility)
|
||||
|
||||
* [ARIA specification and authoring practices](https://www.w3.org/TR/wai-aria/)
|
||||
|
||||
* [Material Design - Accessibility](https://material.io/design/usability/accessibility.html)
|
||||
|
||||
* [Smashing Magazine](https://www.smashingmagazine.com/search/?q=accessibility)
|
||||
|
||||
* [Inclusive Components](https://inclusive-components.design/)
|
||||
|
||||
* [Accessibility Resources and Code Examples](https://dequeuniversity.com/resources/)
|
||||
|
||||
* [W3C - Web Accessibility Initiative](https://www.w3.org/WAI/people-use-web/)
|
||||
|
||||
* [Rob Dodson A11ycasts](https://www.youtube.com/watch?v=HtTyRajRuyY)
|
||||
|
||||
* [Codelyzer](http://codelyzer.com/rules/) provides linting rules that can help you make sure your code meets accessibility standards.
|
||||
|
||||
Books
|
||||
|
||||
* "A Web for Everyone: Designing Accessible User Experiences", Sarah Horton and Whitney Quesenbery
|
||||
|
||||
* "Inclusive Design Patterns", Heydon Pickering
|
@ -66,7 +66,7 @@ The following table lists some of the key AngularJS template features with their
|
||||
|
||||
### Bindings/interpolation
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="interpolation"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="interpolation" linenums="false"></code-example>
|
||||
|
||||
|
||||
In Angular, a template expression in curly braces still denotes one-way binding.
|
||||
@ -102,7 +102,7 @@ The following table lists some of the key AngularJS template features with their
|
||||
|
||||
### Pipes
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="uppercase"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="uppercase" linenums="false"></code-example>
|
||||
|
||||
|
||||
In Angular you use similar syntax with the pipe (|) character to filter output, but now you call them **pipes**.
|
||||
@ -136,7 +136,7 @@ The following table lists some of the key AngularJS template features with their
|
||||
|
||||
### Input variables
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="local"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="local" linenums="false"></code-example>
|
||||
|
||||
|
||||
Angular has true template input variables that are explicitly defined using the `let` keyword.
|
||||
@ -202,10 +202,10 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### Bootstrapping
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/main.ts" header="main.ts"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/main.ts" header="main.ts" linenums="false"></code-example>
|
||||
<br>
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts" header="app.module.ts"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts" header="app.module.ts" linenums="false"></code-example>
|
||||
|
||||
|
||||
Angular doesn't have a bootstrap directive.
|
||||
@ -245,7 +245,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### ngClass
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngClass"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngClass" linenums="false"></code-example>
|
||||
|
||||
|
||||
In Angular, the `ngClass` directive works similarly.
|
||||
@ -291,7 +291,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### Bind to the `click` event
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="event-binding"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="event-binding" linenums="false"></code-example>
|
||||
|
||||
|
||||
AngularJS event-based directives do not exist in Angular.
|
||||
@ -338,7 +338,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### Component decorator
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false"></code-example>
|
||||
|
||||
|
||||
In Angular, the template no longer specifies its associated controller.
|
||||
@ -401,7 +401,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### Bind to the `href` property
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="href"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="href" linenums="false"></code-example>
|
||||
|
||||
|
||||
Angular uses property binding; there is no built-in *href* directive.
|
||||
@ -412,7 +412,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
In Angular, `href` is no longer used for routing. Routing uses `routerLink`, as shown in the following example.
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="router-link"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="router-link" linenums="false"></code-example>
|
||||
|
||||
|
||||
For more information on routing, see the [RouterLink binding](guide/router#router-link)
|
||||
@ -445,7 +445,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### *ngIf
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngIf"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngIf" linenums="false"></code-example>
|
||||
|
||||
|
||||
The `*ngIf` directive in Angular works the same as the `ng-if` directive in AngularJS. It removes
|
||||
@ -480,7 +480,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### ngModel
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngModel"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngModel" linenums="false"></code-example>
|
||||
|
||||
|
||||
In Angular, **two-way binding** is denoted by `[()]`, descriptively referred to as a "banana in a box". This syntax is a shortcut for defining both property binding (from the component to the view)
|
||||
@ -516,7 +516,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### *ngFor
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngFor"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngFor" linenums="false"></code-example>
|
||||
|
||||
|
||||
The `*ngFor` directive in Angular is similar to the `ng-repeat` directive in AngularJS. It repeats
|
||||
@ -559,7 +559,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### Bind to the `hidden` property
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="hidden"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="hidden" linenums="false"></code-example>
|
||||
|
||||
|
||||
Angular uses property binding; there is no built-in *show* directive.
|
||||
@ -598,7 +598,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### Bind to the `src` property
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="src"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="src" linenums="false"></code-example>
|
||||
|
||||
|
||||
Angular uses property binding; there is no built-in *src* directive.
|
||||
@ -635,7 +635,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### ngStyle
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngStyle"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="ngStyle" linenums="false"></code-example>
|
||||
|
||||
|
||||
In Angular, the `ngStyle` directive works similarly. It sets a CSS style on an HTML element based on an expression.
|
||||
@ -690,7 +690,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### ngSwitch
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngSwitch"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.html" region="ngSwitch" linenums="false"></code-example>
|
||||
|
||||
|
||||
In Angular, the `ngSwitch` directive works similarly.
|
||||
@ -765,7 +765,7 @@ For more information on pipes, see [Pipes](guide/pipes).
|
||||
|
||||
### currency
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="currency"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="currency" linenums="false"></code-example>
|
||||
|
||||
|
||||
The Angular `currency` pipe is similar although some of the parameters have changed.
|
||||
@ -793,7 +793,7 @@ For more information on pipes, see [Pipes](guide/pipes).
|
||||
|
||||
### date
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="date"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="date" linenums="false"></code-example>
|
||||
|
||||
|
||||
The Angular `date` pipe is similar.
|
||||
@ -847,7 +847,7 @@ For more information on pipes, see [Pipes](guide/pipes).
|
||||
|
||||
### json
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="json"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="json" linenums="false"></code-example>
|
||||
|
||||
|
||||
The Angular `json` pipe does the same thing.
|
||||
@ -876,7 +876,7 @@ For more information on pipes, see [Pipes](guide/pipes).
|
||||
|
||||
### slice
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="slice"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="slice" linenums="false"></code-example>
|
||||
|
||||
|
||||
The `SlicePipe` does the same thing but the *order of the parameters is reversed*, in keeping
|
||||
@ -907,7 +907,7 @@ For more information on pipes, see [Pipes](guide/pipes).
|
||||
|
||||
### lowercase
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="lowercase"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="lowercase" linenums="false"></code-example>
|
||||
|
||||
|
||||
The Angular `lowercase` pipe does the same thing.
|
||||
@ -935,7 +935,7 @@ For more information on pipes, see [Pipes](guide/pipes).
|
||||
|
||||
### number
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="number"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.component.html" region="number" linenums="false"></code-example>
|
||||
|
||||
|
||||
The Angular `number` pipe is similar.
|
||||
@ -1068,7 +1068,7 @@ The Angular code is shown using TypeScript.
|
||||
|
||||
### NgModules
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/app.module.1.ts" linenums="false"></code-example>
|
||||
|
||||
|
||||
NgModules, defined with the `NgModule` decorator, serve the same purpose:
|
||||
@ -1109,7 +1109,7 @@ The Angular code is shown using TypeScript.
|
||||
|
||||
### Component decorator
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="component" linenums="false"></code-example>
|
||||
|
||||
|
||||
Angular adds a decorator to the component class to provide any required metadata.
|
||||
@ -1145,7 +1145,7 @@ The Angular code is shown using TypeScript.
|
||||
|
||||
### Component class
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="class"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="class" linenums="false"></code-example>
|
||||
|
||||
|
||||
In Angular, you create a component class.
|
||||
@ -1184,7 +1184,7 @@ The Angular code is shown using TypeScript.
|
||||
|
||||
### Dependency injection
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="di"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="di" linenums="false"></code-example>
|
||||
|
||||
|
||||
In Angular, you pass in dependencies as arguments to the component class constructor.
|
||||
@ -1254,7 +1254,7 @@ also encapsulate a style sheet within a specific component.
|
||||
|
||||
|
||||
### Styles configuration
|
||||
<code-example hideCopy path="ajs-quick-reference/.angular-cli.1.json" region="styles"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/.angular-cli.1.json" region="styles" linenums="false"></code-example>
|
||||
|
||||
With the Angular CLI, you can configure your global styles in the `angular.json` file.
|
||||
You can rename the extension to `.scss` to use sass.
|
||||
@ -1263,7 +1263,7 @@ also encapsulate a style sheet within a specific component.
|
||||
In Angular, you can use the `styles` or `styleUrls` property of the `@Component` metadata to define
|
||||
a style sheet for a particular component.
|
||||
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="style-url"></code-example>
|
||||
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="style-url" linenums="false"></code-example>
|
||||
|
||||
|
||||
This allows you to set appropriate styles for individual components that won’t leak into
|
||||
|
@ -37,7 +37,8 @@ To get started with adding Angular animations to your project, import the animat
|
||||
|
||||
Import `BrowserAnimationsModule`, which introduces the animation capabilities into your Angular root application module.
|
||||
|
||||
<code-example path="animations/src/app/app.module.1.ts" header="src/app/app.module.ts" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/app.module.1.ts" header="src/app/app.module.ts" language="typescript" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -179,7 +180,9 @@ In this example, we'll name the trigger `openClose`, and attach it to the `butto
|
||||
|
||||
Animations are defined in the metadata of the component that controls the HTML element to be animated. Put the code that defines your animations under the `animations:` property within the `@Component()` decorator.
|
||||
|
||||
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" language="typescript" region="component"></code-example>
|
||||
<code-example path="animations/src/app/open-close.component.ts" header="src/app/open-close.component.ts" language="typescript"
|
||||
region="component" linenums="false">
|
||||
</code-example>
|
||||
|
||||
When you've defined an animation trigger for a component, you can attach it to an element in that component's template by wrapping the trigger name in brackets and preceding it with an `@` symbol. Then, you can bind the trigger to a template expression using standard Angular property binding syntax as shown below, where `triggerName` is the name of the trigger, and `expression` evaluates to a defined animation state.
|
||||
|
||||
|
@ -21,7 +21,7 @@ Angular offers two ways to compile your application:
|
||||
1. **_Just-in-Time_ (JIT)**, which compiles your app in the browser at runtime.
|
||||
1. **_Ahead-of-Time_ (AOT)**, which compiles your app at build time.
|
||||
|
||||
JIT compilation is the default when you run the [`ng build`](cli/build) (build only) or [`ng serve`](cli/serve) (build and serve locally) CLI commands:
|
||||
JIT compilation is the default when you run the [`ng build`](cli/build) (build only) or [`ng serve`](cli/serve) (build and serve locally) CLI commands:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
ng build
|
||||
@ -945,7 +945,7 @@ import { calculateValue } from './utilities';
|
||||
|
||||
To correct this error, export a function from the module and refer to the function in a `useFactory` provider instead.
|
||||
|
||||
<code-example>
|
||||
<code-example linenums="false">
|
||||
// CORRECTED
|
||||
import { calculateValue } from './utilities';
|
||||
|
||||
@ -978,7 +978,7 @@ The compiler does not support references to variables assigned by [destructuring
|
||||
|
||||
For example, you cannot write something like this:
|
||||
|
||||
<code-example>
|
||||
<code-example linenums="false">
|
||||
// ERROR
|
||||
import { configuration } from './configuration';
|
||||
|
||||
@ -994,7 +994,7 @@ const {foo, bar} = configuration;
|
||||
|
||||
To correct this error, refer to non-destructured values.
|
||||
|
||||
<code-example>
|
||||
<code-example linenums="false">
|
||||
// CORRECTED
|
||||
import { configuration } from './configuration';
|
||||
...
|
||||
@ -1041,7 +1041,7 @@ you can finesse the problem in four steps:
|
||||
|
||||
Here's an illustrative example.
|
||||
|
||||
<code-example>
|
||||
<code-example linenums="false">
|
||||
// CORRECTED
|
||||
import { Inject } from '@angular/core';
|
||||
|
||||
@ -1064,7 +1064,7 @@ uses the `@Inject(WINDOW)` to generate the injection code.
|
||||
|
||||
Angular does something similar with the `DOCUMENT` token so you can inject the browser's `document` object (or an abstraction of it, depending upon the platform in which the application runs).
|
||||
|
||||
<code-example>
|
||||
<code-example linenums="false">
|
||||
import { Inject } from '@angular/core';
|
||||
import { DOCUMENT } from '@angular/platform-browser';
|
||||
|
||||
@ -1101,7 +1101,7 @@ that you referenced in metadata.
|
||||
|
||||
The compiler can understand simple enum values but not complex values such as those derived from computed properties.
|
||||
|
||||
<code-example>
|
||||
<code-example linenums="false">
|
||||
// ERROR
|
||||
enum Colors {
|
||||
Red = 1,
|
||||
@ -1311,7 +1311,7 @@ Chuck: After reviewing your PR comment I'm still at a loss. See [comment there](
|
||||
{@a tsconfig-extends}
|
||||
## Configuration inheritance with extends
|
||||
Similar to TypeScript Compiler, Angular Compiler also supports `extends` in the `tsconfig.json` on `angularCompilerOptions`. A tsconfig file can inherit configurations from another file using the `extends` property.
|
||||
The `extends` is a top level property parallel to `compilerOptions` and `angularCompilerOptions`.
|
||||
The `extends` is a top level property parallel to `compilerOptions` and `angularCompilerOptions`.
|
||||
The configuration from the base file are loaded first, then overridden by those in the inheriting config file.
|
||||
Example:
|
||||
```json
|
||||
|
@ -10,7 +10,7 @@ Learn more in [The App Shell Model](https://developers.google.com/web/fundamenta
|
||||
## Step 1: Prepare the application
|
||||
|
||||
You can do this with the following CLI command:
|
||||
<code-example language="bash">
|
||||
<code-example format="." language="bash" linenums="false">
|
||||
ng new my-app --routing
|
||||
</code-example>
|
||||
|
||||
@ -20,7 +20,7 @@ For an existing application, you have to manually add the `RouterModule` and def
|
||||
|
||||
Use the CLI to automatically create the app shell.
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example format="." language="bash" linenums="false">
|
||||
ng generate app-shell --client-project my-app --universal-project server-app
|
||||
</code-example>
|
||||
|
||||
@ -29,7 +29,7 @@ ng generate app-shell --client-project my-app --universal-project server-app
|
||||
|
||||
After running this command you will notice that the `angular.json` configuration file has been updated to add two new targets, with a few other changes.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example format="." language="none" linenums="false">
|
||||
"server": {
|
||||
"builder": "@angular-devkit/build-angular:server",
|
||||
"options": {
|
||||
@ -52,7 +52,7 @@ After running this command you will notice that the `angular.json` configuration
|
||||
|
||||
Use the CLI to build the `app-shell` target.
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example format="." language="bash" linenums="false">
|
||||
ng run my-app:app-shell
|
||||
</code-example>
|
||||
|
||||
|
@ -10,12 +10,12 @@ For example, individual components define and control each of the following view
|
||||
You define a component's application logic—what it does to support the view—inside a class.
|
||||
The class interacts with the view through an API of properties and methods.
|
||||
|
||||
For example, `HeroListComponent` has a `heroes` property that holds an array of heroes.
|
||||
Its `selectHero()` method sets a `selectedHero` property when the user clicks to choose a hero from that list.
|
||||
The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor.
|
||||
For example, `HeroListComponent` has a `heroes` property that holds an array of heroes.
|
||||
Its `selectHero()` method sets a `selectedHero` property when the user clicks to choose a hero from that list.
|
||||
The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor.
|
||||
The service is provided to the component through the dependency injection system.
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (class)" region="class"></code-example>
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" header="src/app/hero-list.component.ts (class)" region="class"></code-example>
|
||||
|
||||
Angular creates, updates, and destroys components as the user moves through the application. Your app can take action at each moment in this lifecycle through optional [lifecycle hooks](guide/lifecycle-hooks), like `ngOnInit()`.
|
||||
|
||||
@ -31,7 +31,7 @@ In addition to containing or pointing to the template, the `@Component` metadata
|
||||
|
||||
Here's an example of basic metadata for `HeroListComponent`.
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (metadata)" region="metadata"></code-example>
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" header="src/app/hero-list.component.ts (metadata)" region="metadata"></code-example>
|
||||
|
||||
This example shows some of the most useful `@Component` configuration options:
|
||||
|
||||
@ -40,7 +40,7 @@ Angular inserts an instance of the `HeroListComponent` view between those tags.
|
||||
|
||||
* `templateUrl`: The module-relative address of this component's HTML template. Alternatively, you can provide the HTML template inline, as the value of the `template` property. This template defines the component's *host view*.
|
||||
|
||||
* `providers`: An array of [providers](guide/glossary#provider) for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
|
||||
* `providers`: An array of [providers](guide/glossary#provider) for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
|
||||
|
||||
|
||||
## Templates and views
|
||||
@ -69,7 +69,7 @@ This template uses typical HTML elements like `<h2>` and `<p>`, and also includ
|
||||
|
||||
* The `*ngFor` directive tells Angular to iterate over a list.
|
||||
* `{{hero.name}}`, `(click)`, and `[hero]` bind program data to and from the DOM, responding to user input. See more about [data binding](#data-binding) below.
|
||||
* The `<app-hero-detail>` tag in the example is an element that represents a new component, `HeroDetailComponent`.
|
||||
* The `<app-hero-detail>` tag in the example is an element that represents a new component, `HeroDetailComponent`.
|
||||
`HeroDetailComponent` (code not shown) defines the hero-detail child view of `HeroListComponent`.
|
||||
Notice how custom components like this mix seamlessly with native HTML in the same layouts.
|
||||
|
||||
@ -87,7 +87,7 @@ The following diagram shows the four forms of data binding markup. Each form has
|
||||
|
||||
This example from the `HeroListComponent` template uses three of these forms.
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.1.html" header="src/app/hero-list.component.html (binding)" region="binding"></code-example>
|
||||
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" header="src/app/hero-list.component.html (binding)" region="binding"></code-example>
|
||||
|
||||
* The `{{hero.name}}` [*interpolation*](guide/displaying-data#interpolation)
|
||||
displays the component's `hero.name` property value within the `<li>` element.
|
||||
@ -97,11 +97,11 @@ displays the component's `hero.name` property value within the `<li>` element.
|
||||
|
||||
* The `(click)` [*event binding*](guide/user-input#binding-to-user-input-events) calls the component's `selectHero` method when the user clicks a hero's name.
|
||||
|
||||
Two-way data binding (used mainly in [template-driven forms](guide/forms))
|
||||
combines property and event binding in a single notation.
|
||||
Two-way data binding (used mainly in [template-driven forms](guide/forms))
|
||||
combines property and event binding in a single notation.
|
||||
Here's an example from the `HeroDetailComponent` template that uses two-way data binding with the `ngModel` directive.
|
||||
|
||||
<code-example path="architecture/src/app/hero-detail.component.html" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
|
||||
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
|
||||
|
||||
In two-way binding, a data property value flows to the input box from the component as with property binding.
|
||||
The user's changes also flow back to the component, resetting the property to the latest value,
|
||||
@ -151,20 +151,20 @@ Angular templates are *dynamic*. When Angular renders them, it transforms the DO
|
||||
|
||||
A component is technically a directive.
|
||||
However, components are so distinctive and central to Angular applications that Angular
|
||||
defines the `@Component()` decorator, which extends the `@Directive()` decorator with
|
||||
defines the `@Component()` decorator, which extends the `@Directive()` decorator with
|
||||
template-oriented features.
|
||||
|
||||
In addition to components, there are two other kinds of directives: *structural* and *attribute*.
|
||||
In addition to components, there are two other kinds of directives: *structural* and *attribute*.
|
||||
Angular defines a number of directives of both kinds, and you can define your own using the `@Directive()` decorator.
|
||||
|
||||
Just as for components, the metadata for a directive associates the decorated class with a `selector` element that you use to insert it into HTML. In templates, directives typically appear within an element tag as attributes, either by name or as the target of an assignment or a binding.
|
||||
|
||||
#### Structural directives
|
||||
|
||||
*Structural directives* alter layout by adding, removing, and replacing elements in the DOM.
|
||||
*Structural directives* alter layout by adding, removing, and replacing elements in the DOM.
|
||||
The example template uses two built-in structural directives to add application logic to how the view is rendered.
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.1.html" header="src/app/hero-list.component.html (structural)" region="structural"></code-example>
|
||||
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" header="src/app/hero-list.component.html (structural)" region="structural"></code-example>
|
||||
|
||||
* [`*ngFor`](guide/displaying-data#ngFor) is an iterative; it tells Angular to stamp out one `<li>` per hero in the `heroes` list.
|
||||
* [`*ngIf`](guide/displaying-data#ngIf) is a conditional; it includes the `HeroDetail` component only if a selected hero exists.
|
||||
@ -176,7 +176,7 @@ In templates they look like regular HTML attributes, hence the name.
|
||||
|
||||
The `ngModel` directive, which implements two-way data binding, is an example of an attribute directive. `ngModel` modifies the behavior of an existing element (typically `<input>`) by setting its display value property and responding to change events.
|
||||
|
||||
<code-example path="architecture/src/app/hero-detail.component.html" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
|
||||
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" header="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
|
||||
|
||||
Angular has more pre-defined directives that either alter the layout structure
|
||||
(for example, [ngSwitch](guide/template-syntax#ngSwitch))
|
||||
|
@ -23,7 +23,7 @@ An NgModule is defined by a class decorated with `@NgModule()`. The `@NgModule()
|
||||
|
||||
Here's a simple root NgModule definition.
|
||||
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="module" header="src/app/app.module.ts"></code-example>
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="module" header="src/app/app.module.ts" linenums="false"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -69,9 +69,9 @@ In JavaScript each *file* is a module and all objects defined in the file belong
|
||||
The module declares some objects to be public by marking them with the `export` key word.
|
||||
Other JavaScript modules use *import statements* to access public objects from other modules.
|
||||
|
||||
<code-example path="architecture/src/app/app.module.ts" region="imports"></code-example>
|
||||
<code-example path="architecture/src/app/app.module.ts" region="imports" linenums="false"></code-example>
|
||||
|
||||
<code-example path="architecture/src/app/app.module.ts" region="export"></code-example>
|
||||
<code-example path="architecture/src/app/app.module.ts" region="export" linenums="false"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<a href="http://exploringjs.com/es6/ch_modules.html">Learn more about the JavaScript module system on the web.</a>
|
||||
@ -87,17 +87,17 @@ Angular loads as a collection of JavaScript modules. You can think of them as li
|
||||
|
||||
For example, import Angular's `Component` decorator from the `@angular/core` library like this.
|
||||
|
||||
<code-example path="architecture/src/app/app.component.ts" region="import"></code-example>
|
||||
<code-example path="architecture/src/app/app.component.ts" region="import" linenums="false"></code-example>
|
||||
|
||||
You also import NgModules from Angular *libraries* using JavaScript import statements.
|
||||
For example, the following code imports the `BrowserModule` NgModule from the `platform-browser` library.
|
||||
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module"></code-example>
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false"></code-example>
|
||||
|
||||
In the example of the simple root module above, the application module needs material from within
|
||||
`BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this.
|
||||
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports"></code-example>
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports" linenums="false"></code-example>
|
||||
|
||||
In this way you're using the Angular and JavaScript module systems *together*. Although it's easy to confuse the two systems, which share the common vocabulary of "imports" and "exports", you will become familiar with the different contexts in which they are used.
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Introduction to services and dependency injection
|
||||
|
||||
*Service* is a broad category encompassing any value, function, or feature that an app needs.
|
||||
A service is typically a class with a narrow, well-defined purpose.
|
||||
A service is typically a class with a narrow, well-defined purpose.
|
||||
It should do something specific and do it well.
|
||||
|
||||
Angular distinguishes components from services to increase modularity and reusability.
|
||||
@ -14,9 +14,9 @@ in order to mediate between the view (rendered by the template)
|
||||
and the application logic (which often includes some notion of a *model*).
|
||||
|
||||
A component can delegate certain tasks to services, such as fetching data from the server,
|
||||
validating user input, or logging directly to the console.
|
||||
validating user input, or logging directly to the console.
|
||||
By defining such processing tasks in an *injectable service class*, you make those tasks
|
||||
available to any component.
|
||||
available to any component.
|
||||
You can also make your app more adaptable by injecting different providers of the same kind of service,
|
||||
as appropriate in different circumstances.
|
||||
|
||||
@ -28,21 +28,21 @@ available to components through *dependency injection*.
|
||||
|
||||
Here's an example of a service class that logs to the browser console.
|
||||
|
||||
<code-example path="architecture/src/app/logger.service.ts" header="src/app/logger.service.ts (class)" region="class"></code-example>
|
||||
<code-example path="architecture/src/app/logger.service.ts" linenums="false" header="src/app/logger.service.ts (class)" region="class"></code-example>
|
||||
|
||||
Services can depend on other services. For example, here's a `HeroService` that depends on the `Logger` service, and also uses `BackendService` to get heroes. That service in turn might depend on the `HttpClient` service to fetch heroes asynchronously from a server.
|
||||
|
||||
<code-example path="architecture/src/app/hero.service.ts" header="src/app/hero.service.ts (class)" region="class"></code-example>
|
||||
<code-example path="architecture/src/app/hero.service.ts" linenums="false" header="src/app/hero.service.ts (class)" region="class"></code-example>
|
||||
|
||||
## Dependency injection (DI)
|
||||
|
||||
<img src="generated/images/guide/architecture/dependency-injection.png" alt="Service" class="left">
|
||||
|
||||
DI is wired into the Angular framework and used everywhere to provide new components with the services or other things they need.
|
||||
Components consume services; that is, you can *inject* a service into a component, giving the component access to that service class.
|
||||
Components consume services; that is, you can *inject* a service into a component, giving the component access to that service class.
|
||||
|
||||
To define a class as a service in Angular, use the `@Injectable()` decorator to provide the metadata that allows Angular to inject it into a component as a *dependency*.
|
||||
Similarly, use the `@Injectable()` decorator to indicate that a component or other class (such as another service, a pipe, or an NgModule) *has* a dependency.
|
||||
To define a class as a service in Angular, use the `@Injectable()` decorator to provide the metadata that allows Angular to inject it into a component as a *dependency*.
|
||||
Similarly, use the `@Injectable()` decorator to indicate that a component or other class (such as another service, a pipe, or an NgModule) *has* a dependency.
|
||||
|
||||
* The *injector* is the main mechanism. Angular creates an application-wide injector for you during the bootstrap process, and additional injectors as needed. You don't have to create injectors.
|
||||
|
||||
@ -50,19 +50,19 @@ Similarly, use the `@Injectable()` decorator to indicate that a component or oth
|
||||
|
||||
* A *provider* is an object that tells an injector how to obtain or create a dependency.
|
||||
|
||||
For any dependency that you need in your app, you must register a provider with the app's injector,
|
||||
so that the injector can use the provider to create new instances.
|
||||
For any dependency that you need in your app, you must register a provider with the app's injector,
|
||||
so that the injector can use the provider to create new instances.
|
||||
For a service, the provider is typically the service class itself.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
A dependency doesn't have to be a service—it could be a function, for example, or a value.
|
||||
A dependency doesn't have to be a service—it could be a function, for example, or a value.
|
||||
|
||||
</div>
|
||||
|
||||
When Angular creates a new instance of a component class, it determines which services or other dependencies that component needs by looking at the constructor parameter types. For example, the constructor of `HeroListComponent` needs `HeroService`.
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (constructor)" region="ctor"></code-example>
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" header="src/app/hero-list.component.ts (constructor)" region="ctor"></code-example>
|
||||
|
||||
When Angular discovers that a component depends on a service, it first checks if the injector has any existing instances of that service. If a requested service instance doesn't yet exist, the injector makes one using the registered provider, and adds it to the injector before returning the service to Angular.
|
||||
|
||||
@ -78,26 +78,26 @@ The process of `HeroService` injection looks something like this.
|
||||
|
||||
You must register at least one *provider* of any service you are going to use.
|
||||
The provider can be part of the service's own metadata, making that service available everywhere,
|
||||
or you can register providers with specific modules or components.
|
||||
or you can register providers with specific modules or components.
|
||||
You register providers in the metadata of the service (in the `@Injectable()` decorator),
|
||||
or in the `@NgModule()` or `@Component()` metadata
|
||||
or in the `@NgModule()` or `@Component()` metadata
|
||||
|
||||
* By default, the Angular CLI command [`ng generate service`](cli/generate) registers a provider with the root injector for your service by including provider metadata in the `@Injectable()` decorator. The tutorial uses this method to register the provider of HeroService class definition.
|
||||
|
||||
```
|
||||
```
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
```
|
||||
```
|
||||
|
||||
When you provide the service at the root level, Angular creates a single, shared instance of `HeroService`
|
||||
and injects it into any class that asks for it.
|
||||
and injects it into any class that asks for it.
|
||||
Registering the provider in the `@Injectable()` metadata also allows Angular to optimize an app
|
||||
by removing the service from the compiled app if it isn't used.
|
||||
by removing the service from the compiled app if it isn't used.
|
||||
|
||||
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator,
|
||||
|
||||
```
|
||||
```
|
||||
@NgModule({
|
||||
providers: [
|
||||
BackendService,
|
||||
@ -105,12 +105,12 @@ or in the `@NgModule()` or `@Component()` metadata
|
||||
],
|
||||
...
|
||||
})
|
||||
```
|
||||
```
|
||||
|
||||
* When you register a provider at the component level, you get a new instance of the
|
||||
service with each new instance of that component.
|
||||
service with each new instance of that component.
|
||||
At the component level, register a service provider in the `providers` property of the `@Component()` metadata.
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts (component providers)" region="providers"></code-example>
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" header="src/app/hero-list.component.ts (component providers)" region="providers"></code-example>
|
||||
|
||||
For more detailed information, see the [Dependency Injection](guide/dependency-injection) section.
|
||||
|
@ -37,13 +37,13 @@ This page demonstrates building a simple _appHighlight_ attribute
|
||||
directive to set an element's background color
|
||||
when the user hovers over that element. You can apply it like this:
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html (applied)" region="applied"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html (applied)" region="applied"></code-example>
|
||||
|
||||
{@a write-directive}
|
||||
|
||||
Please note that directives _do not_ support namespaces.
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.avoid.html" header="src/app/app.component.avoid.html (unsupported)" region="unsupported"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.avoid.html" linenums="false" header="src/app/app.component.avoid.html (unsupported)" region="unsupported"></code-example>
|
||||
|
||||
### Write the directive code
|
||||
|
||||
@ -101,7 +101,7 @@ Now edit the generated `src/app/highlight.directive.ts` to look as follows:
|
||||
The `import` statement specifies an additional `ElementRef` symbol from the Angular `core` library:
|
||||
|
||||
You use the `ElementRef` in the directive's constructor
|
||||
to [inject](guide/dependency-injection) a reference to the host DOM element,
|
||||
to [inject](guide/dependency-injection) a reference to the host DOM element,
|
||||
the element to which you applied `appHighlight`.
|
||||
|
||||
`ElementRef` grants direct access to the host DOM element
|
||||
@ -140,12 +140,12 @@ and respond by setting or clearing the highlight color.
|
||||
|
||||
Begin by adding `HostListener` to the list of imported symbols.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
|
||||
Then add two eventhandlers that respond when the mouse enters or leaves,
|
||||
each adorned by the `HostListener` decorator.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"></code-example>
|
||||
|
||||
The `@HostListener` decorator lets you subscribe to events of the DOM
|
||||
element that hosts an attribute directive, the `<p>` in this case.
|
||||
@ -166,7 +166,7 @@ The handlers delegate to a helper method that sets the color on the host DOM ele
|
||||
The helper method, `highlight`, was extracted from the constructor.
|
||||
The revised constructor simply declares the injected `el: ElementRef`.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (constructor)" region="ctor"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (constructor)" region="ctor"></code-example>
|
||||
|
||||
Here's the updated directive in full:
|
||||
|
||||
@ -187,11 +187,11 @@ Currently the highlight color is hard-coded _within_ the directive. That's infle
|
||||
In this section, you give the developer the power to set the highlight color while applying the directive.
|
||||
|
||||
Begin by adding `Input` to the list of symbols imported from `@angular/core`.
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
|
||||
Add a `highlightColor` property to the directive class like this:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (highlightColor)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (highlightColor)" region="color"></code-example>
|
||||
|
||||
{@a input}
|
||||
|
||||
@ -204,19 +204,19 @@ Without that input metadata, Angular rejects the binding; see [below](guide/attr
|
||||
|
||||
Try it by adding the following directive binding variations to the `AppComponent` template:
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html (excerpt)" region="color-1"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html (excerpt)" region="color-1"></code-example>
|
||||
|
||||
Add a `color` property to the `AppComponent`.
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.1.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.1.ts" linenums="false" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
|
||||
Let it control the highlight color with a property binding.
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html (excerpt)" region="color-2"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html (excerpt)" region="color-2"></code-example>
|
||||
|
||||
That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this.
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
|
||||
The `[appHighlight]` attribute binding both applies the highlighting directive to the `<p>` element
|
||||
and sets the directive's highlight color with a property binding.
|
||||
@ -225,7 +225,7 @@ That's a crisp, compact syntax.
|
||||
|
||||
You'll have to rename the directive's `highlightColor` property to `appHighlight` because that's now the color property binding name.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2"></code-example>
|
||||
|
||||
This is disagreeable. The word, `appHighlight`, is a terrible property name and it doesn't convey the property's intent.
|
||||
|
||||
@ -237,23 +237,23 @@ Fortunately you can name the directive property whatever you want _and_ **_alias
|
||||
|
||||
Restore the original property name and specify the selector as the alias in the argument to `@Input`.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (color property with alias)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" header="src/app/highlight.directive.ts (color property with alias)" region="color"></code-example>
|
||||
|
||||
_Inside_ the directive the property is known as `highlightColor`.
|
||||
_Outside_ the directive, where you bind to it, it's known as `appHighlight`.
|
||||
|
||||
You get the best of both worlds: the property name you want and the binding syntax you want:
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
|
||||
Now that you're binding via the alias to the `highlightColor`, modify the `onMouseEnter()` method to use that property.
|
||||
If someone neglects to bind to `appHighlightColor`, highlight the host element in red:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" header="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"></code-example>
|
||||
|
||||
Here's the latest version of the directive class.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (excerpt)"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" header="src/app/highlight.directive.ts (excerpt)"></code-example>
|
||||
|
||||
## Write a harness to try it
|
||||
|
||||
@ -263,11 +263,11 @@ lets you pick the highlight color with a radio button and bind your color choice
|
||||
|
||||
Update <code>app.component.html</code> as follows:
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (v2)" region="v2"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (v2)" region="v2"></code-example>
|
||||
|
||||
Revise the `AppComponent.color` so that it has no initial value.
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.ts" linenums="false" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
|
||||
Here are the harness and directive in action.
|
||||
|
||||
@ -287,12 +287,12 @@ Let the template developer set the default color.
|
||||
|
||||
Add a second **input** property to `HighlightDirective` called `defaultColor`:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (defaultColor)" region="defaultColor"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" header="src/app/highlight.directive.ts (defaultColor)" region="defaultColor"></code-example>
|
||||
|
||||
Revise the directive's `onMouseEnter` so that it first tries to highlight with the `highlightColor`,
|
||||
then with the `defaultColor`, and falls back to "red" if both properties are undefined.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" header="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter"></code-example>
|
||||
|
||||
How do you bind to a second property when you're already binding to the `appHighlight` attribute name?
|
||||
|
||||
@ -300,7 +300,7 @@ As with components, you can add as many directive property bindings as you need
|
||||
The developer should be able to write the following template HTML to both bind to the `AppComponent.color`
|
||||
and fall back to "violet" as the default color.
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (defaultColor)" region="defaultColor"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (defaultColor)" region="defaultColor"></code-example>
|
||||
|
||||
Angular knows that the `defaultColor` binding belongs to the `HighlightDirective`
|
||||
because you made it _public_ with the `@Input` decorator.
|
||||
@ -342,11 +342,11 @@ You can also experience and download the <live-example title="Attribute Directiv
|
||||
In this demo, the `highlightColor` property is an ***input*** property of
|
||||
the `HighlightDirective`. You've seen it applied without an alias:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
|
||||
|
||||
You've seen it with an alias:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
|
||||
|
||||
Either way, the `@Input` decorator tells Angular that this property is
|
||||
_public_ and available for binding by a parent component.
|
||||
@ -378,7 +378,7 @@ You can tell if `@Input` is needed by the position of the property name in a bin
|
||||
|
||||
Now apply that reasoning to the following example:
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
|
||||
* The `color` property in the expression on the right belongs to the template's component.
|
||||
The template and its component trust each other.
|
||||
|
@ -106,16 +106,19 @@ To use a directive, component, or pipe in a module, you must do a few things:
|
||||
Those three steps look like the following. In the file where you create your directive, export it.
|
||||
The following example, named `ItemDirective` is the default directive structure that the CLI generates in its own file, `item.directive.ts`:
|
||||
|
||||
<code-example path="bootstrapping/src/app/item.directive.ts" region="directive" header="src/app/item.directive.ts"></code-example>
|
||||
<code-example path="bootstrapping/src/app/item.directive.ts" region="directive" header="src/app/item.directive.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The key point here is that you have to export it so you can import it elsewhere. Next, import it
|
||||
into the NgModule, in this example `app.module.ts`, with a JavaScript import statement:
|
||||
|
||||
<code-example path="bootstrapping/src/app/app.module.ts" region="directive-import" header="src/app/app.module.ts"></code-example>
|
||||
<code-example path="bootstrapping/src/app/app.module.ts" region="directive-import" header="src/app/app.module.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
And in the same file, add it to the `@NgModule` `declarations` array:
|
||||
|
||||
<code-example path="bootstrapping/src/app/app.module.ts" region="declarations" header="src/app/app.module.ts"></code-example>
|
||||
<code-example path="bootstrapping/src/app/app.module.ts" region="declarations" header="src/app/app.module.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
|
||||
Now you could use your `ItemDirective` in a component. This example uses `AppModule`, but you'd do it the same way for a feature module. For more about directives, see [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives). You'd also use the same technique for [pipes](guide/pipes) and components.
|
||||
|
@ -53,7 +53,7 @@ Angular supports most recent browsers. This includes the following specific vers
|
||||
IE
|
||||
</td>
|
||||
<td>
|
||||
11, 10, 9
|
||||
11<br>10<br>9
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -89,7 +89,7 @@ Angular supports most recent browsers. This includes the following specific vers
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Nougat (7.0), Marshmallow (6.0), Lollipop (5.0, 5.1), KitKat (4.4)
|
||||
Nougat (7.0)<br>Marshmallow (6.0)<br>Lollipop (5.0, 5.1)<br>KitKat (4.4)
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -127,27 +127,25 @@ In Angular CLI version 8 and higher, applications are built using *differential
|
||||
This strategy allows you to continue to build your web application to support multiple browsers, but only load the necessary code that the browser needs.
|
||||
For more information about how this works, see [Differential Loading](guide/deployment#differential-loading) in the [Deployment guide](guide/deployment).
|
||||
|
||||
## Enabling polyfills with CLI projects
|
||||
## Enabling polyfills
|
||||
|
||||
The [Angular CLI](cli) provides support for polyfills.
|
||||
If you are not using the CLI to create your projects, see [Polyfill instructions for non-CLI users](#non-cli).
|
||||
[Angular CLI](cli) users enable polyfills through the `src/polyfills.ts` file that
|
||||
the CLI created with your project.
|
||||
|
||||
When you create a project with the `ng new` command, a `src/polyfills.ts` configuration file is created as part of your project folder.
|
||||
This file incorporates the mandatory and many of the optional polyfills as JavaScript `import` statements.
|
||||
|
||||
* The npm packages for the [_mandatory_ polyfills](#polyfill-libs) (such as `zone.js`) are installed automatically for you when you create your project with `ng new`, and their corresponding `import` statements are already enabled in the `src/polyfills.ts` configuration file.
|
||||
The npm packages for the _mandatory_ polyfills (such as `zone.js`) were installed automatically for you when you created your project and their corresponding `import` statements are ready to go. You probably won't touch these.
|
||||
|
||||
* If you need an _optional_ polyfill, you must install its npm package, then uncomment or create the corresponding import statement in the `src/polyfills.ts` configuration file.
|
||||
|
||||
For example, if you need the optional [web animations polyfill](http://caniuse.com/#feat=web-animation), you could install it with `npm`, using the following command (or the `yarn` equivalent):
|
||||
But if you need an optional polyfill, you'll have to install its npm package.
|
||||
For example, [if you need the web animations polyfill](http://caniuse.com/#feat=web-animation), you could install it with `npm`, using the following command (or the `yarn` equivalent):
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
# install the optional web animations polyfill
|
||||
# note that the web-animations-js polyfill is only here as an example
|
||||
# it isn't a strict requirement of Angular anymore (more below)
|
||||
npm install --save web-animations-js
|
||||
</code-example>
|
||||
|
||||
You can then add the import statement in the `src/polyfills.ts` file.
|
||||
For many polyfills, you can simply un-comment the corresponding `import` statement in the file, as in the following example.
|
||||
Then open the `polyfills.ts` file and un-comment the corresponding `import` statement as in the following example:
|
||||
|
||||
<code-example header="src/polyfills.ts">
|
||||
/**
|
||||
@ -157,14 +155,23 @@ For many polyfills, you can simply un-comment the corresponding `import` stateme
|
||||
import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
</code-example>
|
||||
|
||||
If the polyfill you want is not already in `polyfills.ts` file, add the `import` statement by hand.
|
||||
If you can't find the polyfill you want in `polyfills.ts`,
|
||||
add it yourself, following the same pattern:
|
||||
|
||||
1. install the npm package
|
||||
1. `import` the file in `polyfills.ts`
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Non-CLI users should follow the instructions [below](#non-cli).
|
||||
</div>
|
||||
|
||||
{@a polyfill-libs}
|
||||
|
||||
### Mandatory polyfills
|
||||
These are the polyfills required to run an Angular application on each supported browser:
|
||||
|
||||
|
||||
<table>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
@ -182,13 +189,26 @@ These are the polyfills required to run an Angular application on each supported
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
<td>
|
||||
Chrome, Firefox, Edge, <br>
|
||||
Safari, Android, IE10+
|
||||
Chrome, Firefox, Edge, Safari 9+
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
[ES2015](guide/browser-support#core-es6)
|
||||
[ES7/reflect](guide/browser-support#core-es7-reflect) (JIT only)
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
<td>
|
||||
Safari 7 & 8, IE10 & 11, Android 4.1+
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
[ES6](guide/browser-support#core-es6)
|
||||
|
||||
</td>
|
||||
|
||||
@ -202,7 +222,7 @@ These are the polyfills required to run an Angular application on each supported
|
||||
|
||||
<td>
|
||||
|
||||
ES2015<br>[classList](guide/browser-support#classlist)
|
||||
[ES6<br>classList](guide/browser-support#classlist)
|
||||
|
||||
</td>
|
||||
|
||||
@ -215,6 +235,12 @@ These are the polyfills required to run an Angular application on each supported
|
||||
|
||||
Some features of Angular may require additional polyfills.
|
||||
|
||||
For example, the animations library relies on the standard web animation API, which is only available in Chrome and Firefox today.
|
||||
(note that the dependency of web-animations-js in Angular is only necessary if `AnimationBuilder` is used.)
|
||||
|
||||
Here are the features which may require additional polyfills:
|
||||
|
||||
|
||||
<table>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
@ -237,8 +263,31 @@ Some features of Angular may require additional polyfills.
|
||||
|
||||
<td>
|
||||
|
||||
[AnimationBuilder](api/animations/AnimationBuilder).
|
||||
(Standard animation support does not require polyfills.)
|
||||
[JIT compilation](guide/aot-compiler).
|
||||
|
||||
Required to reflect for metadata.
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
[ES7/reflect](guide/browser-support#core-es7-reflect)
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
All current browsers. Enabled by default.
|
||||
Can remove if you always use AOT and only use Angular decorators.
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr style="vertical-align: top">
|
||||
|
||||
<td>
|
||||
|
||||
[Animations](guide/animations)
|
||||
<br>Only if `Animation Builder` is used within the application--standard
|
||||
animation support in Angular doesn't require any polyfills (as of NG6).
|
||||
|
||||
</td>
|
||||
|
||||
@ -249,9 +298,8 @@ Some features of Angular may require additional polyfills.
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<p>If AnimationBuilder is used, enables scrubbing
|
||||
support for IE/Edge and Safari.
|
||||
(Chrome and Firefox support this natively).</p>
|
||||
<p>If AnimationBuilder is used then the polyfill will enable scrubbing
|
||||
support for IE/Edge and Safari (Chrome and Firefox support this natively).</p>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@ -260,10 +308,15 @@ Some features of Angular may require additional polyfills.
|
||||
|
||||
<td>
|
||||
|
||||
If you use the following deprecated i18n pipes:
|
||||
If you use the following deprecated i18n pipes:
|
||||
|
||||
|
||||
[date](api/common/DeprecatedDatePipe),
|
||||
|
||||
[currency](api/common/DeprecatedCurrencyPipe),
|
||||
|
||||
[decimal](api/common/DeprecatedDecimalPipe),
|
||||
|
||||
[percent](api/common/DeprecatedPercentPipe)
|
||||
|
||||
</td>
|
||||
@ -284,7 +337,9 @@ Some features of Angular may require additional polyfills.
|
||||
|
||||
<td>
|
||||
|
||||
[NgClass](api/common/NgClass) on SVG elements
|
||||
[NgClass](api/common/NgClass)
|
||||
|
||||
on SVG elements
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -303,7 +358,9 @@ Some features of Angular may require additional polyfills.
|
||||
|
||||
<td>
|
||||
|
||||
[Http](guide/http) when sending and receiving binary data
|
||||
[Http](guide/http)
|
||||
|
||||
when sending and receiving binary data
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -326,8 +383,9 @@ Some features of Angular may require additional polyfills.
|
||||
|
||||
<td>
|
||||
|
||||
[Router](guide/router) when using
|
||||
[hash-based routing](guide/router#appendix-locationstrategy-and-browser-url-styles)
|
||||
[Router](guide/router)
|
||||
|
||||
when using [hash-based routing](guide/router#appendix-locationstrategy-and-browser-url-styles)
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -346,9 +404,8 @@ Some features of Angular may require additional polyfills.
|
||||
|
||||
|
||||
|
||||
### Suggested polyfills
|
||||
|
||||
The following polyfills are used to test the framework itself. They are a good starting point for an application.
|
||||
### Suggested polyfills ##
|
||||
Below are the polyfills which are used to test the framework itself. They are a good starting point for an application.
|
||||
|
||||
|
||||
<table>
|
||||
@ -369,6 +426,24 @@ The following polyfills are used to test the framework itself. They are a good s
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
<a id='core-es7-reflect' href="https://github.com/zloirock/core-js/tree/v2/fn/reflect">ES7/reflect</a>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
MIT
|
||||
</td>
|
||||
|
||||
<td>
|
||||
0.5KB
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
@ -391,7 +466,7 @@ The following polyfills are used to test the framework itself. They are a good s
|
||||
|
||||
<td>
|
||||
|
||||
<a id='core-es6' href="https://github.com/zloirock/core-js">ES2015</a>
|
||||
<a id='core-es6' href="https://github.com/zloirock/core-js">ES6</a>
|
||||
|
||||
</td>
|
||||
|
||||
@ -520,14 +595,11 @@ The following polyfills are used to test the framework itself. They are a good s
|
||||
computed with the <a href="http://closure-compiler.appspot.com/home">closure compiler</a>.
|
||||
|
||||
{@a non-cli}
|
||||
|
||||
## Polyfills for non-CLI users
|
||||
|
||||
If you are not using the CLI, add your polyfill scripts directly to the host web page (`index.html`).
|
||||
If you are not using the CLI, you should add your polyfill scripts directly to the host web page (`index.html`), perhaps like this.
|
||||
|
||||
For example:
|
||||
|
||||
<code-example header="src/index.html" language="html">
|
||||
<code-example header="src/index.html">
|
||||
<!-- pre-zone polyfills -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
<script src="node_modules/web-animations-js/web-animations.min.js"></script>
|
||||
|
@ -185,7 +185,8 @@ is available to <code>declarations</code> of this module.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@Injectable()</b><br>class MyService() {}</code></td>
|
||||
<td><p>Declares that a class can be provided and injected by other classes. Without this decorator, the compiler won't generate enough metadata to allow the class to be created properly when it's injected somewhere.</p>
|
||||
<td><p>Declares that a class has dependencies that should be injected into the constructor when the dependency injector is creating an instance of this class.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
@ -191,7 +191,7 @@ For our example builder, we expect the `options` value to be a `JsonObject` with
|
||||
|
||||
We can provide the following schema for type validation of these values.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example format="." language="json" linenums="false">
|
||||
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
@ -222,7 +222,7 @@ To link our builder implementation with its schema and name, we need to create a
|
||||
|
||||
Create a file named `builders.json` file that looks like this.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example format="." language="json" linenums="false">
|
||||
|
||||
{
|
||||
"builders": {
|
||||
@ -238,7 +238,7 @@ Create a file named `builders.json` file that looks like this.
|
||||
|
||||
In the `package.json` file, add a `builders` key that tells the Architect tool where to find our builder definition file.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example format="." language="json" linenums="false">
|
||||
|
||||
{
|
||||
"name": "@example/command-runner",
|
||||
@ -257,7 +257,7 @@ The first part of this is the package name (resolved using node resolution), an
|
||||
|
||||
Using one of our `options` is very straightforward, we did this in the previous section when we accessed `options.command`.
|
||||
|
||||
<code-example language="typescript">
|
||||
<code-example format="." language="typescript" linenums="false">
|
||||
context.reportStatus(`Executing "${options.command}"...`);
|
||||
const child = childProcess.spawn(options.command, options.args, { stdio: 'pipe' });
|
||||
|
||||
@ -274,7 +274,7 @@ The Architect tool uses the target definition to resolve input options for a giv
|
||||
The `angular.json` file has a section for each project, and the "architect" section of each project configures targets for builders used by CLI commands such as 'build', 'test', and 'lint'.
|
||||
By default, for example, the `build` command runs the builder `@angular-devkit/build-angular:browser` to perform the build task, and passes in default option values as specified for the `build` target in `angular.json`.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example format="." language="json" linenums="false">
|
||||
{
|
||||
"myApp": {
|
||||
...
|
||||
@ -361,7 +361,7 @@ npm install @example/command-runner
|
||||
|
||||
If we create a new project with `ng new builder-test`, the generated `angular.json` file looks something like this, with only default builder configurations.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example format="." language="json" linenums="false">
|
||||
|
||||
{
|
||||
// ...
|
||||
@ -413,7 +413,7 @@ We need to update the `angular.json` file to add a target for this builder to th
|
||||
|
||||
* The configurations key is optional, we'll leave it out for now.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example format="." language="json" linenums="false">
|
||||
|
||||
{
|
||||
"projects": {
|
||||
@ -495,7 +495,7 @@ Use integration testing for your builder, so that you can use the Architect sche
|
||||
Here’s an example of a test that runs the command builder.
|
||||
The test uses the builder to run the `ls` command, then validates that it ran successfully and listed the proper files.
|
||||
|
||||
<code-example language="typescript">
|
||||
<code-example format="." language="typescript" linenums="false">
|
||||
|
||||
import { Architect, ArchitectHost } from '@angular-devkit/architect';
|
||||
import { TestingArchitectHost } from '@angular-devkit/architect/testing';
|
||||
@ -592,4 +592,4 @@ The CLI Builder API provides a new way of changing the behavior of the Angular C
|
||||
|
||||
* We recommend that you use integration tests to test Architect builders. You can use unit tests to validate the logic that the builder executes.
|
||||
|
||||
* If your builder returns an Observable, it should clean up in the teardown logic of that Observable.
|
||||
* If your builder returns an Observable, it should clean up in the teardown logic of that Observable.
|
@ -38,7 +38,7 @@ The following example demonstrates how to use `query()` and `stagger()` function
|
||||
|
||||
* Animate each element on screen for 0.5 seconds using a custom-defined easing curve, simultaneously fading it in and un-transforming it.
|
||||
|
||||
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="page-animations" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="page-animations" language="typescript" linenums="false"></code-example>
|
||||
|
||||
## Parallel animation using group() function
|
||||
|
||||
@ -51,7 +51,7 @@ You've seen how to add a delay between each successive animation. But you may al
|
||||
|
||||
In the following example, using groups on both `:enter` and `:leave` allow for two different timing configurations. They're applied to the same element in parallel, but run independently.
|
||||
|
||||
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" header="src/app/hero-list-groups.component.ts (excerpt)" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" header="src/app/hero-list-groups.component.ts (excerpt)" language="typescript" linenums="false"></code-example>
|
||||
|
||||
## Sequential vs. parallel animations
|
||||
|
||||
@ -74,7 +74,7 @@ The HTML template contains a trigger called `filterAnimation`.
|
||||
|
||||
The component file contains three transitions.
|
||||
|
||||
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="filter-animations" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/hero-list-page.component.ts" header="src/app/hero-list-page.component.ts" region="filter-animations" language="typescript" linenums="false"></code-example>
|
||||
|
||||
The animation does the following:
|
||||
|
||||
@ -101,4 +101,4 @@ You may also be interested in the following:
|
||||
* [Introduction to Angular animations](guide/animations)
|
||||
* [Transition and triggers](guide/transition-and-triggers)
|
||||
* [Reusable animations](guide/reusable-animations)
|
||||
* [Route transition animations](guide/route-animations)
|
||||
* [Route transition animations](guide/route-animations)
|
@ -21,7 +21,8 @@ One way to do this is to set the `styles` property in the component metadata.
|
||||
The `styles` property takes an array of strings that contain CSS code.
|
||||
Usually you give it one string, as in the following example:
|
||||
|
||||
<code-example path="component-styles/src/app/hero-app.component.ts" header="src/app/hero-app.component.ts"></code-example>
|
||||
<code-example path="component-styles/src/app/hero-app.component.ts" header="src/app/hero-app.component.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
## Style scope
|
||||
|
||||
@ -70,7 +71,8 @@ Use the `:host` pseudo-class selector to target styles in the element that *host
|
||||
targeting elements *inside* the component's template).
|
||||
|
||||
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="host" header="src/app/hero-details.component.css"></code-example>
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="host" header="src/app/hero-details.component.css" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The `:host` selector is the only way to target the host element. You can't reach
|
||||
the host element from inside the component with other selectors because it's not part of the
|
||||
@ -81,7 +83,8 @@ including another selector inside parentheses after `:host`.
|
||||
|
||||
The next example targets the host element again, but only when it also has the `active` CSS class.
|
||||
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" header="src/app/hero-details.component.css"></code-example>
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" header="src/app/hero-details.component.css" linenums="false">
|
||||
</code-example>
|
||||
|
||||
### :host-context
|
||||
|
||||
@ -96,7 +99,8 @@ up to the document root. The `:host-context()` selector is useful when combined
|
||||
The following example applies a `background-color` style to all `<h2>` elements *inside* the component, only
|
||||
if some ancestor element has the CSS class `theme-light`.
|
||||
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" header="src/app/hero-details.component.css"></code-example>
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" header="src/app/hero-details.component.css" linenums="false">
|
||||
</code-example>
|
||||
|
||||
### (deprecated) `/deep/`, `>>>`, and `::ng-deep`
|
||||
|
||||
@ -111,7 +115,9 @@ can bleed into other components.
|
||||
The following example targets all `<h3>` elements, from the host element down
|
||||
through this component to all of its child elements in the DOM.
|
||||
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" header="src/app/hero-details.component.css"></code-example>
|
||||
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" header="src/app/hero-details.component.css" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
The `/deep/` combinator also has the aliases `>>>`, and `::ng-deep`.
|
||||
|
||||
@ -298,7 +304,8 @@ Choose from the following modes:
|
||||
|
||||
To set the components encapsulation mode, use the `encapsulation` property in the component metadata:
|
||||
|
||||
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" header="src/app/quest-summary.component.ts"></code-example>
|
||||
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" header="src/app/quest-summary.component.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
`ShadowDom` view encapsulation only works on browsers that have native support
|
||||
for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the
|
||||
|
@ -12,7 +12,7 @@ A simple example might be a button that sends users to your company website, tha
|
||||
|
||||
Use the Angular CLI to generate a new library skeleton with the following command:
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example format="." language="bash">
|
||||
ng generate library my-lib
|
||||
</code-example>
|
||||
|
||||
@ -35,7 +35,7 @@ The workspace configuration file, `angular.json`, is updated with a project of t
|
||||
|
||||
You can build, test, and lint the project with CLI commands:
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example format="." language="bash">
|
||||
ng build my-lib
|
||||
ng test my-lib
|
||||
ng lint my-lib
|
||||
@ -106,7 +106,7 @@ To learn more, see [Schematics Overview](guide/schematics) and [Schematics for
|
||||
Use the Angular CLI and the npm package manager to build and publish your library as an npm package.
|
||||
Libraries are built in [AoT mode](guide/aot-compiler) by default, so you do not need to specify the `-prod` flag when building for publication.
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example format="." language="bash">
|
||||
ng build my-lib
|
||||
cd dist/my-lib
|
||||
npm publish
|
||||
@ -158,7 +158,7 @@ You don't have to publish your library to the npm package manager in order to us
|
||||
To use your own library in an app:
|
||||
|
||||
* Build the library. You cannot use a library before it is built.
|
||||
<code-example language="bash">
|
||||
<code-example format="." language="bash">
|
||||
ng build my-lib
|
||||
</code-example>
|
||||
|
||||
@ -190,6 +190,6 @@ Every time a file is changed a partial build is performed that emits the amended
|
||||
|
||||
Incremental builds can be run as a backround process in your dev environment. To take advantage of this feature add the `--watch` flag to the build command:
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example format="." language="bash">
|
||||
ng build my-lib --watch
|
||||
</code-example>
|
||||
|
@ -21,14 +21,18 @@ constructor, and lets the framework provide them.
|
||||
|
||||
The following example shows that `AppComponent` declares its dependence on `LoggerService` and `UserContext`.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/app.component.ts" region="ctor" header="src/app/app.component.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/app.component.ts" region="ctor" header="src/app/app.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
`UserContext` in turn depends on both `LoggerService` and
|
||||
`UserService`, another service that gathers information about a particular user.
|
||||
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/user-context.service.ts" region="injectables" header="user-context.service.ts (injection)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/user-context.service.ts" region="injectables" header="user-context.service.ts (injection)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
When Angular creates `AppComponent`, the DI framework creates an instance of `LoggerService` and starts to create `UserContextService`.
|
||||
@ -181,13 +185,17 @@ This `HeroBiosAndContactsComponent` is a revision of `HeroBiosComponent` which y
|
||||
|
||||
Focus on the template:
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="template" header="dependency-injection-in-action/src/app/hero-bios.component.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="template" header="dependency-injection-in-action/src/app/hero-bios.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
Now there's a new `<hero-contact>` element between the `<hero-bio>` tags.
|
||||
Angular *projects*, or *transcludes*, the corresponding `HeroContactComponent` into the `HeroBioComponent` view,
|
||||
placing it in the `<ng-content>` slot of the `HeroBioComponent` template.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-bio.component.ts" region="template" header="src/app/hero-bio.component.ts (template)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-bio.component.ts" region="template" header="src/app/hero-bio.component.ts (template)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
The result is shown below, with the hero's telephone number from `HeroContactComponent` projected above the hero description.
|
||||
|
||||
@ -204,7 +212,9 @@ Here's `HeroContactComponent`, which demonstrates the qualifying decorators.
|
||||
|
||||
Focus on the constructor parameters.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-contact.component.ts" region="ctor-params" header="src/app/hero-contact.component.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-contact.component.ts" region="ctor-params" header="src/app/hero-contact.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
The `@Host()` function decorating the `heroCache` constructor property ensures that
|
||||
you get a reference to the cache service from the parent `HeroBioComponent`.
|
||||
@ -289,7 +299,9 @@ whose `nativeElement` property exposes the DOM element for the directive to mani
|
||||
The sample code applies the directive's `myHighlight` attribute to two `<div>` tags,
|
||||
first without a value (yielding the default color) and then with an assigned color value.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/app.component.html" region="highlight" header="src/app/app.component.html (highlight)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/app.component.html" region="highlight" header="src/app/app.component.html (highlight)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
|
||||
@ -313,7 +325,9 @@ Angular passes this token to the injector and assigns the result to the paramete
|
||||
The following is a typical example.
|
||||
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="ctor" header="src/app/hero-bios.component.ts (component constructor injection)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-bios.component.ts" region="ctor" header="src/app/hero-bios.component.ts (component constructor injection)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
Angular asks the injector for the service associated with `LoggerService`
|
||||
@ -372,7 +386,9 @@ You can also use a value provider in a unit test to provide mock data in place o
|
||||
|
||||
The `HeroOfTheMonthComponent` example has two value providers.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-value" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-value" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
* The first provides an existing instance of the `Hero` class to use for the `Hero` token, rather than
|
||||
requiring the injector to create a new instance with `new` or use its own cached instance.
|
||||
@ -411,7 +427,9 @@ extend the default class, or emulate the behavior of the real class in a test ca
|
||||
|
||||
The following code shows two examples in `HeroOfTheMonthComponent`.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-class" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="use-class" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
The first provider is the *de-sugared*, expanded form of the most typical case in which the
|
||||
class to be created (`HeroService`) is also the provider's dependency injection token.
|
||||
@ -430,7 +448,9 @@ Components outside the tree continue to receive the original `LoggerService` ins
|
||||
|
||||
`DateLoggerService` inherits from `LoggerService`; it appends the current date/time to each message:
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/date-logger.service.ts" region="date-logger-service" header="src/app/date-logger.service.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/date-logger.service.ts" region="date-logger-service" header="src/app/date-logger.service.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
{@a useexisting}
|
||||
|
||||
@ -452,11 +472,15 @@ You might want to shrink that API surface to just the members you actually need.
|
||||
In this example, the `MinimalLogger` [class-interface](#class-interface) reduces the API to two members:
|
||||
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="src/app/minimal-logger.service.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="src/app/minimal-logger.service.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
The following example puts `MinimalLogger` to use in a simplified version of `HeroOfTheMonthComponent`.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.1.ts" header="src/app/hero-of-the-month.component.ts (minimal version)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.1.ts" header="src/app/hero-of-the-month.component.ts (minimal version)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
The `HeroOfTheMonthComponent` constructor's `logger` parameter is typed as `MinimalLogger`, so only the `logs` and `logInfo` members are visible in a TypeScript-aware editor.
|
||||
|
||||
@ -508,7 +532,9 @@ The `runnersUpFactory()` returns the *provider factory function*, which can use
|
||||
the passed-in state value and the injected services `Hero` and `HeroService`.
|
||||
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/runners-up.ts" region="factory-synopsis" header="runners-up.ts (excerpt)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/runners-up.ts" region="factory-synopsis" header="runners-up.ts (excerpt)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
The provider factory function (returned by `runnersUpFactory()`) returns the actual dependency object,
|
||||
the string of names.
|
||||
@ -552,7 +578,9 @@ as the token for a provider of `LoggerService`.
|
||||
|
||||
`MinimalLogger` is an abstract class.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="dependency-injection-in-action/src/app/minimal-logger.service.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" header="dependency-injection-in-action/src/app/minimal-logger.service.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
An abstract class is usually a base class that you can extend.
|
||||
In this app, however there is no class that inherits from `MinimalLogger`.
|
||||
@ -578,7 +606,9 @@ Using a class as an interface gives you the characteristics of an interface in a
|
||||
To minimize memory cost, however, the class should have *no implementation*.
|
||||
The `MinimalLogger` transpiles to this unoptimized, pre-minified JavaScript for a constructor function.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" region="minimal-logger-transpiled" header="dependency-injection-in-action/src/app/minimal-logger.service.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/minimal-logger.service.ts" region="minimal-logger-transpiled" header="dependency-injection-in-action/src/app/minimal-logger.service.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
Notice that it doesn't have any members. It never grows no matter how many members you add to the class,
|
||||
as long as those members are typed but not implemented.
|
||||
@ -605,11 +635,15 @@ another token that happens to have the same name.
|
||||
You encountered them twice in the *Hero of the Month* example,
|
||||
in the *title* value provider and in the *runnersUp* factory provider.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="provide-injection-token" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="provide-injection-token" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
You created the `TITLE` token like this:
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="injection-token" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" region="injection-token" header="dependency-injection-in-action/src/app/hero-of-the-month.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
The type parameter, while optional, conveys the dependency's type to developers and tooling.
|
||||
The token description is another developer aid.
|
||||
@ -699,7 +733,9 @@ appear *above* the class definition.
|
||||
|
||||
Break the circularity with `forwardRef`.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<!--- Waiting for good examples
|
||||
|
@ -2,16 +2,16 @@
|
||||
|
||||
Application components often need to share information.
|
||||
You can often use loosely coupled techniques for sharing information,
|
||||
such as data binding and service sharing,
|
||||
but sometimes it makes sense for one component to have a direct reference to another component.
|
||||
such as data binding and service sharing,
|
||||
but sometimes it makes sense for one component to have a direct reference to another component.
|
||||
You need a direct reference, for instance, to access values or call methods on that component.
|
||||
|
||||
Obtaining a component reference is a bit tricky in Angular.
|
||||
Angular components themselves do not have a tree that you can
|
||||
Angular components themselves do not have a tree that you can
|
||||
inspect or navigate programmatically. The parent-child relationship is indirect,
|
||||
established through the components' [view objects](guide/glossary#view).
|
||||
|
||||
Each component has a *host view*, and can have additional *embedded views*.
|
||||
Each component has a *host view*, and can have additional *embedded views*.
|
||||
An embedded view in component A is the
|
||||
host view of component B, which can in turn have embedded view.
|
||||
This means that there is a [view hierarchy](guide/glossary#view-hierarchy) for each component,
|
||||
@ -40,14 +40,18 @@ In the following example, the parent `AlexComponent` has several children includ
|
||||
{@a alex}
|
||||
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-1" header="parent-finder.component.ts (AlexComponent v.1)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-1" header="parent-finder.component.ts (AlexComponent v.1)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
*Cathy* reports whether or not she has access to *Alex*
|
||||
after injecting an `AlexComponent` into her constructor:
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="cathy" header="parent-finder.component.ts (CathyComponent)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="cathy" header="parent-finder.component.ts (CathyComponent)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -94,13 +98,17 @@ inject its parent via the parent's base class*.
|
||||
The sample's `CraigComponent` explores this question. [Looking back](#alex),
|
||||
you see that the `Alex` component *extends* (*inherits*) from a class named `Base`.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" header="parent-finder.component.ts (Alex class signature)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" header="parent-finder.component.ts (Alex class signature)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="craig" header="parent-finder.component.ts (CraigComponent)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="craig" header="parent-finder.component.ts (CraigComponent)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -130,7 +138,9 @@ and add that provider to the `providers` array of the `@Component()` metadata fo
|
||||
{@a alex-providers}
|
||||
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="parent-finder.component.ts (AlexComponent providers)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
[Parent](#parent-token) is the provider's class interface token.
|
||||
@ -139,7 +149,9 @@ The [*forwardRef*](guide/dependency-injection-in-action#forwardref) breaks the c
|
||||
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter,
|
||||
the same way you've done it before.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="carol-class" header="parent-finder.component.ts (CarolComponent class)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="carol-class" header="parent-finder.component.ts (CarolComponent class)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -165,7 +177,9 @@ That means he must both *inject* the `Parent` class interface to get *Alice* and
|
||||
|
||||
Here's *Barry*.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="barry" header="parent-finder.component.ts (BarryComponent)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="barry" header="parent-finder.component.ts (BarryComponent)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -215,7 +229,9 @@ You [learned earlier](guide/dependency-injection-in-action#class-interface) that
|
||||
|
||||
The example defines a `Parent` class interface.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="parent" header="parent-finder.component.ts (Parent class-interface)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="parent" header="parent-finder.component.ts (Parent class-interface)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -225,7 +241,9 @@ Such a narrow interface helps decouple the child component class from its parent
|
||||
|
||||
A component that could serve as a parent *should* implement the class interface as the `AliceComponent` does.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-class-signature" header="parent-finder.component.ts (AliceComponent class signature)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-class-signature" header="parent-finder.component.ts (AliceComponent class signature)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -233,7 +251,9 @@ Doing so adds clarity to the code. But it's not technically necessary.
|
||||
Although `AlexComponent` has a `name` property, as required by its `Base` class,
|
||||
its class signature doesn't mention `Parent`.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" header="parent-finder.component.ts (AlexComponent class signature)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" header="parent-finder.component.ts (AlexComponent class signature)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -257,15 +277,21 @@ It doesn't in this example *only* to demonstrate that the code will compile and
|
||||
Writing variations of the same parent *alias provider* gets old quickly,
|
||||
especially this awful mouthful with a [*forwardRef*](guide/dependency-injection-in-action#forwardref).
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
You can extract that logic into a helper function like the following.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-the-parent" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-the-parent" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
Now you can add a simpler, more meaningful parent provider to your components.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
You can do better. The current version of the helper function can only alias the `Parent` class interface.
|
||||
@ -273,10 +299,14 @@ The application might have a variety of parent types, each with its own class in
|
||||
|
||||
Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent class interface.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-parent" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-parent" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
And here's how you could use it with a different parent type.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="beth-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="beth-providers" header="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
@ -68,11 +68,13 @@ using the `Logger` token.
|
||||
Another class, `EvenBetterLogger`, might display the user name in the log message.
|
||||
This logger gets the user from an injected `UserService` instance.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger"></code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The injector needs providers for both this new logging service and its dependent `UserService`. Configure this alternative logger with the `useClass` provider-definition key, like `BetterLogger`. The following array specifies both providers in the `providers` metadata option of the parent module or component.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5"></code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5" linenums="false">
|
||||
</code-example>
|
||||
|
||||
{@a aliased-class-providers}
|
||||
|
||||
@ -90,11 +92,13 @@ when a component asks for either the new or the old logger.
|
||||
|
||||
If you try to alias `OldLogger` to `NewLogger` with `useClass`, you end up with two different `NewLogger` instances in your app.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a"></code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false">
|
||||
</code-example>
|
||||
|
||||
To make sure there is only one instance of `NewLogger`, alias `OldLogger` with the `useExisting` option.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b"></code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b" linenums="false">
|
||||
</code-example>
|
||||
|
||||
{@a value-provider}
|
||||
|
||||
@ -106,11 +110,13 @@ configure the injector with the `useValue` option
|
||||
|
||||
The following code defines a variable that creates such an object to play the logger role.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger"></code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The following provider object uses the `useValue` key to associate the variable with the `Logger` token.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7"></code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7" linenums="false">
|
||||
</code-example>
|
||||
|
||||
{@a non-class-dependencies}
|
||||
|
||||
@ -124,7 +130,8 @@ like the title of the application or the address of a web API endpoint.
|
||||
These configuration objects aren't always instances of a class.
|
||||
They can be object literals, as shown in the following example.
|
||||
|
||||
<code-example path="dependency-injection/src/app/app.config.ts" region="config" header="src/app/app.config.ts (excerpt)"></code-example>
|
||||
<code-example path="dependency-injection/src/app/app.config.ts" region="config" header="src/app/app.config.ts (excerpt)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
{@a interface-not-valid-token}
|
||||
|
||||
@ -134,9 +141,11 @@ The `HERO_DI_CONFIG` constant conforms to the `AppConfig` interface.
|
||||
Unfortunately, you cannot use a TypeScript interface as a token.
|
||||
In TypeScript, an interface is a design-time artifact, and doesn't have a runtime representation (token) that the DI framework can use.
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface"></code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface"></code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -154,19 +163,22 @@ Another solution to choosing a provider token for non-class dependencies is
|
||||
to define and use an `InjectionToken` object.
|
||||
The following example shows how to define such a token.
|
||||
|
||||
<code-example path="dependency-injection/src/app/app.config.ts" region="token" header="src/app/app.config.ts"></code-example>
|
||||
<code-example path="dependency-injection/src/app/app.config.ts" region="token" header="src/app/app.config.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The type parameter, while optional, conveys the dependency's type to developers and tooling.
|
||||
The token description is another developer aid.
|
||||
|
||||
Register the dependency provider using the `InjectionToken` object:
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9"></code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Now you can inject the configuration object into any constructor that needs it, with
|
||||
the help of an `@Inject()` parameter decorator.
|
||||
|
||||
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" header="src/app/app.component.ts"></code-example>
|
||||
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" header="src/app/app.component.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -203,19 +215,22 @@ who is authorized and who isn't.
|
||||
|
||||
To resolve this, we give the `HeroService` constructor a boolean flag to control display of secret heroes.
|
||||
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" header="src/app/heroes/hero.service.ts (excerpt)"></code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" header="src/app/heroes/hero.service.ts (excerpt)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
You can inject `Logger`, but you can't inject the `isAuthorized` flag. Instead, you can use a factory provider to create a new logger instance for `HeroService`.
|
||||
|
||||
A factory provider needs a factory function.
|
||||
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" header="src/app/heroes/hero.service.provider.ts (excerpt)"></code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" header="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Although `HeroService` has no access to `UserService`, the factory function does.
|
||||
You inject both `Logger` and `UserService` into the factory provider
|
||||
and let the injector pass them along to the factory function.
|
||||
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" header="src/app/heroes/hero.service.provider.ts (excerpt)"></code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" header="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
* The `useFactory` field tells Angular that the provider is a factory function whose implementation is `heroServiceFactory`.
|
||||
|
||||
@ -307,13 +322,13 @@ Thus, services in the NgModule `providers` array or at component level are not t
|
||||
|
||||
The following example of non-tree-shakable providers in Angular configures a service provider for the injector of an NgModule.
|
||||
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/service-and-module.ts" header="src/app/tree-shaking/service-and-modules.ts"></code-example>
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/service-and-module.ts" header="src/app/tree-shaking/service-and-modules.ts" linenums="false"> </code-example>
|
||||
|
||||
You can then import this module into your application module
|
||||
to make the service available for injection in your app,
|
||||
as in the following example.
|
||||
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/app.module.ts" header="src/app/tree-shaking/app.modules.ts"></code-example>
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/app.module.ts" header="src/app/tree-shaking/app.modules.ts" linenums="false"> </code-example>
|
||||
|
||||
When `ngc` runs, it compiles `AppModule` into a module factory, which contains definitions for all the providers declared in all the modules it includes. At runtime, this factory becomes an injector that instantiates these services.
|
||||
|
||||
@ -325,11 +340,11 @@ You can make a provider tree-shakable by specifying it in the `@Injectable()` de
|
||||
|
||||
The following example shows the tree-shakable equivalent to the `ServiceModule` example above.
|
||||
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" header="src/app/tree-shaking/service.ts"></code-example>
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" header="src/app/tree-shaking/service.ts" linenums="false"> </code-example>
|
||||
|
||||
The service can be instantiated by configuring a factory function, as in the following example.
|
||||
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/service.0.ts" header="src/app/tree-shaking/service.0.ts"></code-example>
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/service.0.ts" header="src/app/tree-shaking/service.0.ts" linenums="false"> </code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
@ -196,7 +196,8 @@ Listing dependencies as constructor parameters may be all you need to test appli
|
||||
For example, you can create a new `HeroListComponent` with a mock service that you can manipulate
|
||||
under test.
|
||||
|
||||
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" header="src/app/test.component.ts"></code-example>
|
||||
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" header="src/app/test.component.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -258,7 +259,8 @@ In simple examples, the dependency value is an *instance*, and
|
||||
the class *type* serves as its own lookup key.
|
||||
Here you get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
|
||||
|
||||
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" header="src/app/injector.component.ts"></code-example>
|
||||
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" header="src/app/injector.component.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The behavior is similar when you write a constructor that requires an injected class-based dependency.
|
||||
When you define a constructor parameter with the `HeroService` class type,
|
||||
@ -285,7 +287,8 @@ constructor parameter with `@Optional()`.
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="import-optional">
|
||||
</code-example>
|
||||
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor"></code-example>
|
||||
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor" linenums="false">
|
||||
</code-example>
|
||||
|
||||
When using `@Optional()`, your code must be prepared for a null value. If you
|
||||
don't register a logger provider anywhere, the injector sets the
|
||||
|
@ -152,14 +152,17 @@ The list is by no means exhaustive, but should provide you with a good starting
|
||||
[rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html) to the `.htaccess` file as shown
|
||||
(https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):
|
||||
|
||||
<code-example>
|
||||
<code-example format=".">
|
||||
|
||||
RewriteEngine On
|
||||
# If an existing asset or directory is requested go to it as it is
|
||||
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
|
||||
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
|
||||
RewriteRule ^ - [L]<br>
|
||||
RewriteRule ^ - [L]
|
||||
|
||||
# If the requested resource doesn't exist, use index.html
|
||||
RewriteRule ^ /index.html
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
@ -167,15 +170,18 @@ The list is by no means exhaustive, but should provide you with a good starting
|
||||
[Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps),
|
||||
modified to serve `index.html`:
|
||||
|
||||
```
|
||||
try_files $uri $uri/ /index.html;
|
||||
```
|
||||
<code-example format=".">
|
||||
|
||||
try_files $uri $uri/ /index.html;
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
* [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown
|
||||
[here](http://stackoverflow.com/a/26152011/2116927):
|
||||
|
||||
<code-example format='.' language="xml">
|
||||
<code-example format='.' linenums="false">
|
||||
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
@ -190,6 +196,7 @@ modified to serve `index.html`:
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
@ -207,11 +214,13 @@ and to
|
||||
* [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
|
||||
[rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites).
|
||||
|
||||
<code-example language="json">
|
||||
<code-example format=".">
|
||||
|
||||
"rewrites": [ {
|
||||
"source": "**",
|
||||
"destination": "/index.html"
|
||||
} ]
|
||||
|
||||
</code-example>
|
||||
|
||||
{@a cors}
|
||||
@ -403,7 +412,7 @@ Differential loading, which is supported by default in Angular CLI version 8 and
|
||||
|
||||
Differential loading is a strategy where the CLI builds two separate bundles as part of your deployed application.
|
||||
|
||||
* The first bundle contains modern ES2015 syntax, takes advantage of built-in support in modern browsers, ships less polyfills, and results in a smaller bundle size.
|
||||
* The first bundle contains modern ES1015 syntax, takes advantage of built-in support in modern browsers, ships less polyfills, and results in a smaller bundle size.
|
||||
|
||||
* The second bundle contains code in the old ES5 syntax, along with all necessary polyfills. This results in a larger bundle size, but supports older browsers.
|
||||
|
||||
@ -436,20 +445,24 @@ When you create a production build using [`ng build --prod`](cli/build), the CLI
|
||||
|
||||
The `index.html` file is also modified during the build process to include script tags that enable differential loading. See the sample output below from the `index.html` file produced during a build using `ng build`.
|
||||
|
||||
<code-example language="html">
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
<script src="runtime-es2015.js" type="module"></script>
|
||||
<script src="runtime-es5.js" nomodule></script>
|
||||
<script src="polyfills-es2015.js" type="module"></script>
|
||||
<script src="polyfills-es5.js" nomodule></script>
|
||||
<script src="styles-es2015.js" type="module"></script>
|
||||
<script src="styles-es5.js" nomodule></script>
|
||||
<script src="vendor-es2015.js" type="module"></script>
|
||||
<script src="vendor-es5.js" nomodule></script>
|
||||
<script src="main-es2015.js" type="module"></script>
|
||||
<script src="main-es5.js" nomodule></script>
|
||||
</body>
|
||||
<code-example language="html" format="." linenums="false">
|
||||
|
||||
<!-- ... -->
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
<script src="runtime-es2015.js" type="module"></script>
|
||||
<script src="runtime-es5.js" nomodule></script>
|
||||
<script src="polyfills-es2015.js" type="module"></script>
|
||||
<script src="polyfills-es5.js" nomodule></script>
|
||||
<script src="styles-es2015.js" type="module"></script>
|
||||
<script src="styles-es5.js" nomodule></script>
|
||||
<script src="vendor-es2015.js" type="module"></script>
|
||||
<script src="vendor-es5.js" nomodule></script>
|
||||
<script src="main-es2015.js" type="module"></script>
|
||||
<script src="main-es5.js" nomodule></script>
|
||||
</body>
|
||||
<!-- ... -->
|
||||
|
||||
</code-example>
|
||||
|
||||
Each script tag has a `type="module"` or `nomodule` attribute. Browsers with native support for ES modules only load the scripts with the `module` type attribute and ignore scripts with the `nomodule` attribute. Legacy browsers only load the scripts with the `nomodule` attribute, and ignore the script tags with the `module` type that load ES modules.
|
||||
@ -479,7 +492,7 @@ not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||
|
||||
The `tsconfig.json` looks like this:
|
||||
|
||||
<code-example language="json">
|
||||
<code-example language="json" format="." linenums="false">
|
||||
|
||||
{
|
||||
"compileOnSave": false,
|
||||
@ -510,7 +523,7 @@ By default, legacy browsers such as IE 9-11 are ignored, and the compilation tar
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
To see which browsers are supported with the above configuration, see which settings meet to your browser support requirements, see the [Browserslist compatibility page](https://browserl.ist/?q=%3E+0.5%25%2C+last+2+versions%2C+Firefox+ESR%2C+not+dead%2C+not+IE+9-11).
|
||||
To see which browsers are supported with the above configuration, see which settings meet to your browser support requirements, see the [Browserslist compatibility page](https://browserl.ist/?q=%3E+0.5%25%2C+last+2+versions%2C+Firefox+ESR%2C+Chrome+41%2C+not+dead%2C+not+IE+9-11).
|
||||
|
||||
</div>
|
||||
|
||||
@ -550,7 +563,7 @@ To maintain the benefits of differential loading, however, a better option is to
|
||||
|
||||
To do this for `ng serve`, create a new file, `tsconfig-es5.app.json` next to `tsconfig.app.json` with the following content.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example language="json" format="." linenums="false">
|
||||
|
||||
{
|
||||
"extends": "./tsconfig.app.json",
|
||||
@ -563,7 +576,7 @@ To do this for `ng serve`, create a new file, `tsconfig-es5.app.json` next to `t
|
||||
|
||||
In `angular.json` add two new configuration sections under the `build` and `serve` targets to point to the new TypeScript configuration.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example language="json" format="." linenums="false">
|
||||
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
@ -610,7 +623,7 @@ ng serve --configuration es5
|
||||
|
||||
Create a new file, `tsconfig-es5.spec.json` next to `tsconfig.spec.json` with the following content.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example language="json" format="." linenums="false">
|
||||
|
||||
{
|
||||
"extends": "./tsconfig.spec.json",
|
||||
@ -621,7 +634,7 @@ Create a new file, `tsconfig-es5.spec.json` next to `tsconfig.spec.json` with th
|
||||
|
||||
</code-example>
|
||||
|
||||
<code-example language="json">
|
||||
<code-example language="json" format="." linenums="false">
|
||||
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
@ -647,9 +660,9 @@ ng test --configuration es5
|
||||
|
||||
### Configuring the e2e command
|
||||
|
||||
Create an [ES5 serve configuration](guide/deployment#configuring-serve-for-es5) as explained above, and configuration an ES5 configuration for the E2E target.
|
||||
Create an ES5 serve configuration as explained above (link to the above serve section), and configuration an ES5 configuration for the E2E target.
|
||||
|
||||
<code-example language="json">
|
||||
<code-example language="json" format="." linenums="false">
|
||||
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
|
@ -368,7 +368,7 @@ These two properties have subtle differences, so switching to `textContent` unde
|
||||
All of the `wtf*` APIs are deprecated and will be removed in a future version.
|
||||
|
||||
{@a webworker-apps}
|
||||
### Running Angular applications in platform-webworker
|
||||
### Running Angular applications in platform-webworker
|
||||
|
||||
The `@angular/platform-*` packages enable Angular to be run in different contexts. For examples,
|
||||
`@angular/platform-server` enables Angular to be run on the server, and `@angular/platform-browser`
|
||||
@ -382,7 +382,7 @@ worker is not the best strategy for most applications.
|
||||
|
||||
Going forward, we will focus our efforts related to web workers around their primary use case of
|
||||
offloading CPU-intensive, non-critical work needed for initial rendering (such as in-memory search
|
||||
and image processing). Learn more in the
|
||||
and image processing). Learn more in the
|
||||
[guide to Using Web Workers with the Angular CLI](guide/web-worker).
|
||||
|
||||
As of Angular version 8, all `platform-webworker` APIs are deprecated.
|
||||
@ -465,99 +465,3 @@ For more information about using `@angular/common/http`, see the [HttpClient gui
|
||||
| `MockBackend` | [`HttpTestingController`](/api/common/http/testing/HttpTestingController) |
|
||||
| `MockConnection` | [`HttpTestingController`](/api/common/http/testing/HttpTestingController) |
|
||||
|
||||
## Renderer to Renderer2 migration
|
||||
|
||||
### Migration Overview
|
||||
|
||||
The `Renderer` class has been marked as deprecated since Angular version 4. This section provides guidance on migrating from this deprecated API to the newer `Renderer2` API and what it means for your app.
|
||||
|
||||
### Why should I migrate to Renderer2?
|
||||
|
||||
The deprecated `Renderer` class has been removed in version 9 of Angular, so it's necessary to migrate to a supported API. Using `Renderer2` is the recommended strategy because it supports a similar set of functionality to `Renderer`. The API surface is quite large (with 19 methods), but the schematic should simplify this process for your applications.
|
||||
|
||||
### Is there action required on my end?
|
||||
|
||||
No. The schematic should handle most cases with the exception of `Renderer.animate()` and `Renderer.setDebugInfo()`, which already aren’t supported.
|
||||
|
||||
### What are the `__ngRendererX` methods? Why are they necessary?
|
||||
|
||||
Some methods either don't have exact equivalents in `Renderer2`, or they correspond to more than one expression. For example, both renderers have a `createElement()` method, but they're not equal because a call such as `renderer.createElement(parentNode, namespaceAndName)` in the `Renderer` corresponds to the following block of code in `Renderer2`:
|
||||
|
||||
```ts
|
||||
const [namespace, name] = splitNamespace(namespaceAndName);
|
||||
const el = renderer.createElement(name, namespace);
|
||||
if (parentNode) {
|
||||
renderer.appendChild(parentNode, el);
|
||||
}
|
||||
return el;
|
||||
```
|
||||
|
||||
Migration has to guarantee that the return values of functions and types of variables stay the same. To handle the majority of cases safely, the schematic declares helper functions at the bottom of the user's file. These helpers encapsulate your own logic and keep the replacements inside your code down to a single function call. Here's an example of how the `createElement()` migration looks:
|
||||
|
||||
|
||||
**Before:**
|
||||
|
||||
```ts
|
||||
public createAndAppendElement() {
|
||||
const el = this.renderer.createElement('span');
|
||||
el.textContent = 'hello world';
|
||||
return el;
|
||||
}
|
||||
```
|
||||
|
||||
**After:**
|
||||
|
||||
<code-example>
|
||||
|
||||
public createAndAppendElement() {
|
||||
const el = __ngRendererCreateElement(this.renderer, this.element, 'span');
|
||||
el.textContent = 'hello world';
|
||||
return el;
|
||||
}
|
||||
// Generated code at the bottom of the file
|
||||
__ngRendererCreateElement(renderer: any, parentNode: any, nameAndNamespace: any) {
|
||||
const [namespace, name] = __ngRendererSplitNamespace(namespaceAndName);
|
||||
const el = renderer.createElement(name, namespace);
|
||||
if (parentNode) {
|
||||
renderer.appendChild(parentNode, el);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
__ngRendererSplitNamespace(nameAndNamespace: any) {
|
||||
// returns the split name and namespace
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
When implementing these helper functions, the schematic ensures that they're only declared once per file and that their names are unique enough that there's a small chance of colliding with pre-existing functions in your code. The schematic also keeps their parameter types as `any` so that it doesn't have to insert extra logic that ensures that their values have the correct type.
|
||||
|
||||
### I’m a library author. Should I run this migration?
|
||||
|
||||
**Library authors should definitely use this migration to move away from the `Renderer`. Otherwise, the libraries won't work with applications built with version 9.**
|
||||
|
||||
|
||||
### Full list of method migrations
|
||||
|
||||
The following table shows all methods that the migration maps from `Renderer` to `Renderer2`.
|
||||
|
||||
|Renderer|Renderer2|
|
||||
|---|---|
|
||||
|`listen(renderElement, name, callback)`|`listen(renderElement, name, callback)`|
|
||||
|`setElementProperty(renderElement, propertyName, propertyValue)`|`setProperty(renderElement, propertyName, propertyValue)`|
|
||||
|`setText(renderNode, text)`|`setValue(renderNode, text)`|
|
||||
|`listenGlobal(target, name, callback)`|`listen(target, name, callback)`|
|
||||
|`selectRootElement(selectorOrNode, debugInfo?)`|`selectRootElement(selectorOrNode)`|
|
||||
|`createElement(parentElement, name, debugInfo?)`|`appendChild(parentElement, createElement(name))`|
|
||||
|`setElementStyle(el, style, value?)`|`value == null ? removeStyle(el, style) : setStyle(el, style, value)`
|
||||
|`setElementAttribute(el, name, value?)`|`attributeValue == null ? removeAttribute(el, name) : setAttribute(el, name, value)`
|
||||
|`createText(parentElement, value, debugInfo?)`|`appendChild(parentElement, createText(value))`|
|
||||
|`createTemplateAnchor(parentElement)`|`appendChild(parentElement, createComment(''))`|
|
||||
|`setElementClass(renderElement, className, isAdd)`|`isAdd ? addClass(renderElement, className) : removeClass(renderElement, className)`|
|
||||
|`projectNodes(parentElement, nodes)`|`for (let i = 0; i < nodes.length; i<ins></ins>) { appendChild(parentElement, nodes<i>); }`|
|
||||
|`attachViewAfter(node, viewRootNodes)`|`const parentElement = parentNode(node); const nextSibling = nextSibling(node); for (let i = 0; i < viewRootNodes.length; i<ins></ins>) { insertBefore(parentElement, viewRootNodes<i>, nextSibling);}`|
|
||||
|`detachView(viewRootNodes)`|`for (let i = 0; i < viewRootNodes.length; i<ins></ins>) {const node = viewRootNodes<i>; const parentElement = parentNode(node); removeChild(parentElement, node);}`|
|
||||
|`destroyView(hostElement, viewAllNodes)`|`for (let i = 0; i < viewAllNodes.length; i<ins></ins>) { destroyNode(viewAllNodes<i>); }`|
|
||||
|`setBindingDebugInfo()`|This function is a noop in `Renderer2`.|
|
||||
|`createViewRoot(hostElement)`|Should be replaced with a reference to `hostElement`|
|
||||
|`invokeElementMethod(renderElement, methodName, args?)`|`(renderElement as any)<methodName>.apply(renderElement, args);`|
|
||||
|`animate(element, startingStyles, keyframes, duration, delay, easing, previousPlayers?)`|Throws an error (same behavior as `Renderer.animate()`)|
|
||||
|
@ -31,7 +31,7 @@ The easiest way to display a component property
|
||||
is to bind the property name through interpolation.
|
||||
With interpolation, you put the property name in the view template, enclosed in double curly braces: `{{myHero}}`.
|
||||
|
||||
Use the CLI command [`ng new displaying-data`](cli/new) to create a workspace and app named `displaying-data`.
|
||||
Use the CLI command [`ng new displaying-data`](cli/new) to create a workspace and app named `displaying-data`.
|
||||
|
||||
Delete the <code>app.component.html</code> file. It is not needed for this example.
|
||||
|
||||
@ -41,7 +41,9 @@ changing the template and the body of the component.
|
||||
When you're done, it should look like this:
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts"></code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -51,7 +53,9 @@ The template displays the two component properties using double curly brace
|
||||
interpolation:
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.1.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -93,7 +97,9 @@ The CSS `selector` in the `@Component` decorator specifies an element named `<ap
|
||||
That element is a placeholder in the body of your `index.html` file:
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/index.html" header="src/index.html (body)" region="body"></code-example>
|
||||
<code-example path="displaying-data/src/index.html" linenums="false" header="src/index.html (body)" region="body">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -127,7 +133,7 @@ is simpler without the additional HTML file.
|
||||
In either style, the template data bindings have the same access to the component's properties.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
||||
By default, the Angular CLI command [`ng generate component`](cli/generate) generates components with a template file. You can override that with:
|
||||
|
||||
<code-example hideCopy language="sh" class="code-shell">
|
||||
@ -142,7 +148,9 @@ In either style, the template data bindings have the same access to the componen
|
||||
Although this example uses variable assignment to initialize the components, you could instead declare and initialize the properties using a constructor:
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app-ctor.component.1.ts" region="class"></code-example>
|
||||
<code-example path="displaying-data/src/app/app-ctor.component.1.ts" linenums="false" region="class">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -155,7 +163,9 @@ This app uses more terse "variable assignment" style simply for brevity.
|
||||
To display a list of heroes, begin by adding an array of hero names to the component and redefine `myHero` to be the first name in the array.
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (class)" region="class">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -163,7 +173,9 @@ Now use the Angular `ngFor` directive in the template to display
|
||||
each item in the `heroes` list.
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -172,7 +184,9 @@ in the `<li>` element is the Angular "repeater" directive.
|
||||
It marks that `<li>` element (and its children) as the "repeater template":
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (li)" region="li"></code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (li)" region="li">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -237,7 +251,9 @@ of hero names into an array of `Hero` objects. For that you'll need a `Hero` cla
|
||||
With the following code:
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/hero.ts" header="src/app/hero.ts"></code-example>
|
||||
<code-example path="displaying-data/src/app/hero.ts" linenums="false" header="src/app/hero.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -249,7 +265,9 @@ The declaration of the constructor parameters takes advantage of a TypeScript sh
|
||||
Consider the first parameter:
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/hero.ts" header="src/app/hero.ts (id)" region="id"></code-example>
|
||||
<code-example path="displaying-data/src/app/hero.ts" linenums="false" header="src/app/hero.ts (id)" region="id">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -267,7 +285,9 @@ After importing the `Hero` class, the `AppComponent.heroes` property can return
|
||||
of `Hero` objects:
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.3.ts" header="src/app/app.component.ts (heroes)" region="heroes"></code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (heroes)" region="heroes">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -276,7 +296,9 @@ At the moment it displays the hero's `id` and `name`.
|
||||
Fix that to display only the hero's `name` property.
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.3.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -294,7 +316,9 @@ The Angular `ngIf` directive inserts or removes an element based on a _truthy/fa
|
||||
To see it in action, add the following paragraph at the bottom of the template:
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.ts" header="src/app/app.component.ts (message)" region="message"></code-example>
|
||||
<code-example path="displaying-data/src/app/app.component.ts" linenums="false" header="src/app/app.component.ts (message)" region="message">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
|
@ -428,7 +428,7 @@ You control the _code-example_ output by setting one or more of its attributes:
|
||||
|
||||
* `region`- displays the source file fragment with that region name; regions are identified by _docregion_ markup in the source file, as explained [below](#region "Displaying a code fragment").
|
||||
|
||||
* `linenums`- value may be `true`, `false`, or a `number`. When not specified, line numbers default to `false` (i.e. no line numbers are displayed). The rarely used `number` option starts line numbering at the given value. `linenums=4` sets the starting line number to 4.
|
||||
* `linenums`- value may be `true`, `false`, or a `number`. When not specified, line numbers are automatically displayed when there are greater than 10 lines of code. The rarely used `number` option starts line numbering at the given value. `linenums=4` sets the starting line number to 4.
|
||||
|
||||
* `class`- code snippets can be styled with the CSS classes `no-box`, `code-shell`, and `avoid`.
|
||||
|
||||
@ -465,6 +465,8 @@ A couple of observations:
|
||||
1. Omitting the `header` is fine when the source of the fragment is obvious. We just said that this is a fragment of the `app.module.ts` file which was displayed immediately above, in full, with a header.
|
||||
There's no need to repeat the header.
|
||||
|
||||
1. The line numbers disappeared. By default, the doc viewer omits line numbers when there are fewer than 10 lines of code; it adds line numbers after that. You can turn line numbers on or off explicitly by setting the `linenums` attribute.
|
||||
|
||||
#### Example of bad code
|
||||
|
||||
Sometimes you want to display an example of bad code or bad design.
|
||||
@ -494,18 +496,18 @@ Code tabs display code much like _code examples_ do. The added advantage is tha
|
||||
|
||||
#### Code-tabs attributes
|
||||
|
||||
* `linenums`: The value can be `true`, `false` or a number indicating the starting line number. If not specified, it defaults to `false`.
|
||||
* `linenums`: The value can be `true`, `false` or a number indicating the starting line number. If not specified, line numbers are enabled only when code for a tab pane has greater than 10 lines of code.
|
||||
|
||||
#### Code-pane attributes
|
||||
|
||||
* `path` - a file in the content/examples folder
|
||||
* `header` - seen in the header of a tab
|
||||
* `linenums` - overrides the `linenums` property at the `code-tabs` level for this particular pane. The value can be `true`, `false` or a number indicating the starting line number. If not specified, it defaults to `false`.
|
||||
* `linenums` - overrides the `linenums` property at the `code-tabs` level for this particular pane. The value can be `true`, `false` or a number indicating the starting line number. If not specified, line numbers are enabled only when the number of lines of code are greater than 10.
|
||||
|
||||
The next example displays multiple code tabs, each with its own header.
|
||||
It demonstrates control over display of line numbers at both the `<code-tabs>` and `<code-pane>` levels.
|
||||
|
||||
<code-tabs linenums="true">
|
||||
<code-tabs linenums="false">
|
||||
<code-pane
|
||||
header="app.component.html"
|
||||
path="docs-style-guide/src/app/app.component.html">
|
||||
@ -513,7 +515,7 @@ It demonstrates control over display of line numbers at both the `<code-tabs>` a
|
||||
<code-pane
|
||||
header="app.component.ts"
|
||||
path="docs-style-guide/src/app/app.component.ts"
|
||||
linenums="false">
|
||||
linenums="true">
|
||||
</code-pane>
|
||||
<code-pane
|
||||
header="app.component.css (heroes)"
|
||||
@ -528,11 +530,11 @@ It demonstrates control over display of line numbers at both the `<code-tabs>` a
|
||||
|
||||
Here's the markup for that example.
|
||||
|
||||
Note how the `linenums` attribute in the `<code-tabs>` explicitly enables numbering for all panes.
|
||||
The `linenums` attribute in the second pane disables line numbering for _itself only_.
|
||||
Note how the `linenums` attribute in the `<code-tabs>` explicitly disables numbering for all panes.
|
||||
The `linenums` attribute in the second pane restores line numbering for _itself only_.
|
||||
|
||||
```html
|
||||
<code-tabs linenums="true">
|
||||
<code-tabs linenums="false">
|
||||
<code-pane
|
||||
header="app.component.html"
|
||||
path="docs-style-guide/src/app/app.component.html">
|
||||
@ -540,7 +542,7 @@ The `linenums` attribute in the second pane disables line numbering for _itself
|
||||
<code-pane
|
||||
header="app.component.ts"
|
||||
path="docs-style-guide/src/app/app.component.ts"
|
||||
linenums="false">
|
||||
linenums="true">
|
||||
</code-pane>
|
||||
<code-pane
|
||||
header="app.component.css (heroes)"
|
||||
@ -665,7 +667,7 @@ Examine the `src/app/app.component.ts` file which defines two nested _#docregion
|
||||
|
||||
The inner, `class-skeleton` region appears twice, once to capture the code that opens the class definition and once to capture the code that closes the class definition.
|
||||
|
||||
<code-example>
|
||||
<code-example linenums="false">
|
||||
// #docplaster
|
||||
...
|
||||
// #docregion class, class-skeleton
|
||||
|
@ -35,7 +35,9 @@ The ad banner uses a helper directive called `AdDirective` to
|
||||
mark valid insertion points in the template.
|
||||
|
||||
|
||||
<code-example path="dynamic-component-loader/src/app/ad.directive.ts" header="src/app/ad.directive.ts"></code-example>
|
||||
<code-example path="dynamic-component-loader/src/app/ad.directive.ts" header="src/app/ad.directive.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -60,7 +62,9 @@ To apply the `AdDirective`, recall the selector from `ad.directive.ts`,
|
||||
where to dynamically load components.
|
||||
|
||||
|
||||
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" header="src/app/ad-banner.component.ts (template)"></code-example>
|
||||
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" header="src/app/ad-banner.component.ts (template)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -87,7 +91,9 @@ With its `getAds()` method, `AdBannerComponent` cycles through the array of `AdI
|
||||
and loads a new component every 3 seconds by calling `loadComponent()`.
|
||||
|
||||
|
||||
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="class" header="src/app/ad-banner.component.ts (excerpt)"></code-example>
|
||||
<code-example path="dynamic-component-loader/src/app/ad-banner.component.ts" region="class" header="src/app/ad-banner.component.ts (excerpt)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -144,7 +150,9 @@ dynamically loaded components since they load at runtime.
|
||||
To ensure that the compiler still generates a factory,
|
||||
add dynamically loaded components to the `NgModule`'s `entryComponents` array:
|
||||
|
||||
<code-example path="dynamic-component-loader/src/app/app.module.ts" region="entry-components" header="src/app/app.module.ts (entry components)"></code-example>
|
||||
<code-example path="dynamic-component-loader/src/app/app.module.ts" region="entry-components" header="src/app/app.module.ts (entry components)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
|
@ -77,14 +77,18 @@ appropriate controls dynamically.
|
||||
via the `type` property.
|
||||
|
||||
|
||||
<code-example path="dynamic-form/src/app/question-textbox.ts" header="src/app/question-textbox.ts"></code-example>
|
||||
<code-example path="dynamic-form/src/app/question-textbox.ts" header="src/app/question-textbox.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
`DropdownQuestion` presents a list of choices in a select box.
|
||||
|
||||
|
||||
<code-example path="dynamic-form/src/app/question-dropdown.ts" header="src/app/question-dropdown.ts"></code-example>
|
||||
<code-example path="dynamic-form/src/app/question-dropdown.ts" header="src/app/question-dropdown.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -93,7 +97,9 @@ In a nutshell, the form group consumes the metadata from the question model and
|
||||
allows you to specify default values and validation rules.
|
||||
|
||||
|
||||
<code-example path="dynamic-form/src/app/question-control.service.ts" header="src/app/question-control.service.ts"></code-example>
|
||||
<code-example path="dynamic-form/src/app/question-control.service.ts" header="src/app/question-control.service.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
{@a form-component}
|
||||
|
||||
|
@ -1,5 +1,12 @@
|
||||
# Entry Components
|
||||
|
||||
#### Prerequisites:
|
||||
|
||||
A basic understanding of the following concepts:
|
||||
* [Bootstrapping](guide/bootstrapping).
|
||||
|
||||
<hr />
|
||||
|
||||
An entry component is any component that Angular loads imperatively, (which means you’re not referencing it in the template), by type. You specify an entry component by bootstrapping it in an NgModule, or including it in a routing definition.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
@ -1,6 +1,12 @@
|
||||
# Feature Modules
|
||||
|
||||
Feature modules are NgModules for the purpose of organizing code.
|
||||
Feature modules are NgModules for the purpose of organizing code.
|
||||
|
||||
#### Prerequisites
|
||||
A basic understanding of the following:
|
||||
* [Bootstrapping](guide/bootstrapping).
|
||||
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
|
||||
* [Frequently Used Modules](guide/frequent-ngmodules).
|
||||
|
||||
For the final sample app with a feature module that this page describes,
|
||||
see the <live-example></live-example>.
|
||||
@ -66,7 +72,8 @@ ng generate component customer-dashboard/CustomerDashboard
|
||||
This generates a folder for the new component within the customer-dashboard folder and updates the feature module with the `CustomerDashboardComponent` info:
|
||||
|
||||
|
||||
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="customer-dashboard-component" header="src/app/customer-dashboard/customer-dashboard.module.ts"></code-example>
|
||||
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="customer-dashboard-component" header="src/app/customer-dashboard/customer-dashboard.module.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -76,7 +83,8 @@ The `CustomerDashboardComponent` is now in the JavaScript import list at the top
|
||||
|
||||
To incorporate the feature module into your app, you have to let the root module, `app.module.ts`, know about it. Notice the `CustomerDashboardModule` export at the bottom of `customer-dashboard.module.ts`. This exposes it so that other modules can get to it. To import it into the `AppModule`, add it to the imports in `app.module.ts` and to the `imports` array:
|
||||
|
||||
<code-example path="feature-modules/src/app/app.module.ts" region="app-module" header="src/app/app.module.ts"></code-example>
|
||||
<code-example path="feature-modules/src/app/app.module.ts" region="app-module" header="src/app/app.module.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
|
||||
Now the `AppModule` knows about the feature module. If you were to add any service providers to the feature module, `AppModule` would know about those too, as would any other feature modules. However, NgModules don’t expose their components.
|
||||
@ -86,18 +94,21 @@ Now the `AppModule` knows about the feature module. If you were to add any servi
|
||||
|
||||
When the CLI generated the `CustomerDashboardComponent` for the feature module, it included a template, `customer-dashboard.component.html`, with the following markup:
|
||||
|
||||
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard/customer-dashboard.component.html" region="feature-template" header="src/app/customer-dashboard/customer-dashboard/customer-dashboard.component.html"></code-example>
|
||||
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard/customer-dashboard.component.html" region="feature-template" header="src/app/customer-dashboard/customer-dashboard/customer-dashboard.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
|
||||
To see this HTML in the `AppComponent`, you first have to export the `CustomerDashboardComponent` in the `CustomerDashboardModule`. In `customer-dashboard.module.ts`, just beneath the `declarations` array, add an `exports` array containing `CustomerDashboardComponent`:
|
||||
|
||||
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="component-exports" header="src/app/customer-dashboard/customer-dashboard.module.ts"></code-example>
|
||||
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="component-exports" header="src/app/customer-dashboard/customer-dashboard.module.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Next, in the `AppComponent`, `app.component.html`, add the tag `<app-customer-dashboard>`:
|
||||
|
||||
<code-example path="feature-modules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="feature-modules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
|
||||
Now, in addition to the title that renders by default, the `CustomerDashboardComponent` template renders too:
|
||||
|
@ -4,7 +4,7 @@ You develop applications in the context of an Angular [workspace](guide/glossary
|
||||
|
||||
The Angular CLI `ng new` command creates a workspace.
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example language="bash" linenums="false">
|
||||
ng new <my-project>
|
||||
</code-example>
|
||||
|
||||
@ -111,7 +111,7 @@ An `e2e/` folder at the top level contains source files for a set of end-to-end
|
||||
|
||||
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">
|
||||
<code-example language="none" linenums="false">
|
||||
e2e/
|
||||
src/ (end-to-end tests for my-app)
|
||||
app.e2e-spec.ts
|
||||
@ -131,13 +131,13 @@ A multi-project workspace is suitable for an enterprise that uses a single repos
|
||||
If you intend to have multiple projects in a workspace, you can skip the initial application generation when you create the workspace, and give the workspace a unique name.
|
||||
The following command creates a workspace with all of the workspace-wide configuration files, but no root-level application.
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example language="bash" linenums="false">
|
||||
ng new my-workspace --createApplication="false"
|
||||
</code-example>
|
||||
|
||||
You can then generate apps and libraries with names that are unique within the workspace.
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example language="bash" linenums="false">
|
||||
cd my-workspace
|
||||
ng generate application my-first-app
|
||||
</code-example>
|
||||
@ -148,7 +148,7 @@ The first explicitly generated application goes into the `projects/` folder alon
|
||||
Newly generated libraries are also added under `projects/`.
|
||||
When you create projects this way, the file structure of the workspace is entirely consistent with the structure of the [workspace configuration file](guide/workspace-config), `angular.json`.
|
||||
|
||||
<code-example language="none">
|
||||
<code-example language="none" linenums="false">
|
||||
my-workspace/
|
||||
... (workspace-wide config files)
|
||||
projects/ (generated applications and libraries)
|
||||
|
@ -29,7 +29,9 @@ either a list of validation errors, which results in an INVALID status, or null,
|
||||
You can then inspect the control's state by exporting `ngModel` to a local template variable.
|
||||
The following example exports `NgModel` into a variable called `name`:
|
||||
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-with-error-msg" header="template/hero-form-template.component.html (name)"></code-example>
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-with-error-msg" header="template/hero-form-template.component.html (name)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
Note the following:
|
||||
@ -90,7 +92,8 @@ built-in validators—this time, in function form. See below:
|
||||
|
||||
{@a reactive-component-class}
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="form-group" header="reactive/hero-form-reactive.component.ts (validator functions)"></code-example>
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="form-group" header="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Note that:
|
||||
|
||||
@ -103,7 +106,8 @@ for the template.
|
||||
|
||||
If you look at the template for the name input again, it is fairly similar to the template-driven example.
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg" header="reactive/hero-form-reactive.component.html (name with error msg)"></code-example>
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg" header="reactive/hero-form-reactive.component.html (name with error msg)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Key takeaways:
|
||||
|
||||
@ -121,7 +125,8 @@ Consider the `forbiddenNameValidator` function from previous
|
||||
[examples](guide/form-validation#reactive-component-class) in
|
||||
this guide. Here's what the definition of that function looks like:
|
||||
|
||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" header="shared/forbidden-name.directive.ts (forbiddenNameValidator)"></code-example>
|
||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" header="shared/forbidden-name.directive.ts (forbiddenNameValidator)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name and returns a validator function.
|
||||
|
||||
@ -143,7 +148,8 @@ at which point the form uses the last value emitted for validation.
|
||||
In reactive forms, custom validators are fairly simple to add. All you have to do is pass the function directly
|
||||
to the `FormControl`.
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="custom-validator" header="reactive/hero-form-reactive.component.ts (validator functions)"></code-example>
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="custom-validator" header="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
### Adding to template-driven forms
|
||||
|
||||
@ -155,7 +161,8 @@ The corresponding `ForbiddenValidatorDirective` serves as a wrapper around the `
|
||||
Angular recognizes the directive's role in the validation process because the directive registers itself
|
||||
with the `NG_VALIDATORS` provider, a provider with an extensible collection of validators.
|
||||
|
||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" header="shared/forbidden-name.directive.ts (providers)"></code-example>
|
||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" header="shared/forbidden-name.directive.ts (providers)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The directive class then implements the `Validator` interface, so that it can easily integrate
|
||||
with Angular forms. Here is the rest of the directive to help you get an idea of how it all
|
||||
@ -166,7 +173,9 @@ comes together:
|
||||
|
||||
Once the `ForbiddenValidatorDirective` is ready, you can simply add its selector, `appForbiddenName`, to any input element to activate it. For example:
|
||||
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-input" header="template/hero-form-template.component.html (forbidden-name-input)"></code-example>
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-input" header="template/hero-form-template.component.html (forbidden-name-input)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -236,7 +245,8 @@ const heroForm = new FormGroup({
|
||||
|
||||
The validator code is as follows:
|
||||
|
||||
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-validator" header="shared/identity-revealed.directive.ts"></code-example>
|
||||
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-validator" header="shared/identity-revealed.directive.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The identity validator implements the `ValidatorFn` interface. It takes an Angular control object as an argument and returns either null if the form is valid, or `ValidationErrors` otherwise.
|
||||
|
||||
@ -245,7 +255,8 @@ First we retrieve the child controls by calling the `FormGroup`'s [get](api/form
|
||||
If the values do not match, the hero's identity remains secret, and we can safely return null. Otherwise, the hero's identity is revealed and we must mark the form as invalid by returning an error object.
|
||||
|
||||
Next, to provide better user experience, we show an appropriate error message when the form is invalid.
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="cross-validation-error-message" header="reactive/hero-form-template.component.html"></code-example>
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="cross-validation-error-message" header="reactive/hero-form-template.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Note that we check if:
|
||||
- the `FormGroup` has the cross validation error returned by the `identityRevealed` validator,
|
||||
@ -254,13 +265,16 @@ Note that we check if:
|
||||
### Adding to template driven forms
|
||||
First we must create a directive that will wrap the validator function. We provide it as the validator using the `NG_VALIDATORS` token. If you are not sure why, or you do not fully understand the syntax, revisit the previous [section](guide/form-validation#adding-to-template-driven-forms).
|
||||
|
||||
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-directive" header="shared/identity-revealed.directive.ts"></code-example>
|
||||
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-directive" header="shared/identity-revealed.directive.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Next, we have to add the directive to the html template. Since the validator must be registered at the highest level in the form, we put the directive on the `form` tag.
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-register-validator" header="template/hero-form-template.component.html"></code-example>
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-register-validator" header="template/hero-form-template.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
To provide better user experience, we show an appropriate error message when the form is invalid.
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-error-message" header="template/hero-form-template.component.html"></code-example>
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-error-message" header="template/hero-form-template.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Note that we check if:
|
||||
- the form has the cross validation error returned by the `identityRevealed` validator,
|
||||
@ -299,7 +313,7 @@ To validate the potential alter ego, we need to consult a central database of al
|
||||
|
||||
Let's start by creating the validator class.
|
||||
|
||||
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator"></code-example>
|
||||
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator" linenums="false"></code-example>
|
||||
|
||||
As you can see, the `UniqueAlterEgoValidator` class implements the `AsyncValidator` interface. In the constructor, we inject the `HeroesService` that has the following interface:
|
||||
|
||||
|
@ -109,7 +109,9 @@ Using the Angular CLI command [`ng generate class`](cli/generate), generate a ne
|
||||
|
||||
With this content:
|
||||
|
||||
<code-example path="forms/src/app/hero.ts" header="src/app/hero.ts"></code-example>
|
||||
<code-example path="forms/src/app/hero.ts" header="src/app/hero.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
It's an anemic model with few requirements and no behavior. Perfect for the demo.
|
||||
|
||||
@ -120,7 +122,9 @@ The `alterEgo` is optional, so the constructor lets you omit it; note the questi
|
||||
|
||||
You can create a new hero like this:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="SkyDog"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" region="SkyDog">
|
||||
|
||||
</code-example>
|
||||
|
||||
## Create a form component
|
||||
|
||||
@ -138,7 +142,9 @@ Using the Angular CLI command [`ng generate component`](cli/generate), generate
|
||||
|
||||
With this content:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" header="src/app/hero-form/hero-form.component.ts (v1)" region="v1"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" header="src/app/hero-form/hero-form.component.ts (v1)" region="v1">
|
||||
|
||||
</code-example>
|
||||
|
||||
There’s nothing special about this component, nothing form-specific,
|
||||
nothing to distinguish it from any component you've written before.
|
||||
@ -170,7 +176,9 @@ Because template-driven forms are in their own module, you need to add the `Form
|
||||
|
||||
Update it with the following:
|
||||
|
||||
<code-example path="forms/src/app/app.module.ts" header="src/app/app.module.ts"></code-example>
|
||||
<code-example path="forms/src/app/app.module.ts" header="src/app/app.module.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -196,7 +204,9 @@ Update it with the following:
|
||||
|
||||
Replace the contents of its template with the following:
|
||||
|
||||
<code-example path="forms/src/app/app.component.html" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="forms/src/app/app.component.html" header="src/app/app.component.html">
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -211,7 +221,9 @@ Replace the contents of its template with the following:
|
||||
|
||||
Update the template file with the following contents:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="start" header="src/app/hero-form/hero-form.component.html"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="start" header="src/app/hero-form/hero-form.component.html">
|
||||
|
||||
</code-example>
|
||||
|
||||
The language is simply HTML5. You're presenting two of the `Hero` fields, `name` and `alterEgo`, and
|
||||
opening them up for user input in input boxes.
|
||||
@ -247,7 +259,9 @@ Bootstrap gives the form a little style.
|
||||
|
||||
To add the stylesheet, open `styles.css` and add the following import line at the top:
|
||||
|
||||
<code-example path="forms/src/styles.1.css" header="src/styles.css"></code-example>
|
||||
<code-example path="forms/src/styles.1.css" linenums="false" header="src/styles.css">
|
||||
|
||||
</code-example>
|
||||
|
||||
## Add powers with _*ngFor_
|
||||
|
||||
@ -260,7 +274,9 @@ a technique seen previously in the [Displaying Data](guide/displaying-data) page
|
||||
|
||||
Add the following HTML *immediately below* the *Alter Ego* group:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (powers)" region="powers"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (powers)" region="powers">
|
||||
|
||||
</code-example>
|
||||
|
||||
This code repeats the `<option>` tag for each power in the list of powers.
|
||||
The `pow` template input variable is a different power in each iteration;
|
||||
@ -291,7 +307,9 @@ makes binding the form to the model easy.
|
||||
|
||||
Find the `<input>` tag for *Name* and update it like this:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-1"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-1">
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -307,7 +325,9 @@ You need one more addition to display the data. Declare
|
||||
a template variable for the form. Update the `<form>` tag with
|
||||
`#heroForm="ngForm"` as follows:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="template-variable"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="template-variable">
|
||||
|
||||
</code-example>
|
||||
|
||||
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
|
||||
|
||||
@ -371,7 +391,9 @@ Then you can confirm that two-way data binding works *for the entire hero model*
|
||||
|
||||
After revision, the core of the form should look like this:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModel-2"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModel-2">
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -471,7 +493,9 @@ You can leverage those class names to change the appearance of the control.
|
||||
Temporarily add a [template reference variable](guide/template-syntax#ref-vars) named `spy`
|
||||
to the _Name_ `<input>` tag and use it to display the input's CSS classes.
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-2"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-2">
|
||||
|
||||
</code-example>
|
||||
|
||||
Now run the app and look at the _Name_ input box.
|
||||
Follow these steps *precisely*:
|
||||
@ -511,11 +535,15 @@ on the left of the input box:
|
||||
You achieve this effect by adding these class definitions to a new `forms.css` file
|
||||
that you add to the project as a sibling to `index.html`:
|
||||
|
||||
<code-example path="forms/src/assets/forms.css" header="src/assets/forms.css"></code-example>
|
||||
<code-example path="forms/src/assets/forms.css" header="src/assets/forms.css">
|
||||
|
||||
</code-example>
|
||||
|
||||
Update the `<head>` of `index.html` to include this style sheet:
|
||||
|
||||
<code-example path="forms/src/index.html" header="src/index.html (styles)" region="styles"></code-example>
|
||||
<code-example path="forms/src/index.html" linenums="false" header="src/index.html (styles)" region="styles">
|
||||
|
||||
</code-example>
|
||||
|
||||
## Show and hide validation error messages
|
||||
|
||||
@ -536,7 +564,9 @@ To achieve this effect, extend the `<input>` tag with the following:
|
||||
|
||||
Here's an example of an error message added to the _name_ input box:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="name-with-error-msg"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="name-with-error-msg">
|
||||
|
||||
</code-example>
|
||||
|
||||
You need a template reference variable to access the input box's Angular control from within the template.
|
||||
Here you created a variable called `name` and gave it the value "ngModel".
|
||||
@ -553,7 +583,9 @@ Here you created a variable called `name` and gave it the value "ngModel".
|
||||
You control visibility of the name error message by binding properties of the `name`
|
||||
control to the message `<div>` element's `hidden` property.
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg">
|
||||
|
||||
</code-example>
|
||||
|
||||
In this example, you hide the message when the control is valid or pristine;
|
||||
"pristine" means the user hasn't changed the value since it was displayed in this form.
|
||||
@ -577,9 +609,13 @@ power to valid values.
|
||||
Now you'll add a new hero in this form.
|
||||
Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method.
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-no-reset" header="src/app/hero-form/hero-form.component.html (New Hero button)"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-no-reset" header="src/app/hero-form/hero-form.component.html (New Hero button)">
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="new-hero" header="src/app/hero-form/hero-form.component.ts (New Hero method)"></code-example>
|
||||
</code-example>
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="new-hero" header="src/app/hero-form/hero-form.component.ts (New Hero method)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
Run the application again, click the *New Hero* button, and the form clears.
|
||||
The *required* bars to the left of the input box are red, indicating invalid `name` and `power` properties.
|
||||
@ -598,7 +634,9 @@ Replacing the hero object *did not restore the pristine state* of the form contr
|
||||
You have to clear all of the flags imperatively, which you can do
|
||||
by calling the form's `reset()` method after calling the `newHero()` method.
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-form-reset" header="src/app/hero-form/hero-form.component.html (Reset the form)"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-form-reset" header="src/app/hero-form/hero-form.component.html (Reset the form)">
|
||||
|
||||
</code-example>
|
||||
|
||||
Now clicking "New Hero" resets both the form and its control flags.
|
||||
|
||||
@ -613,7 +651,9 @@ A "form submit" is useless at the moment.
|
||||
To make it useful, bind the form's `ngSubmit` event property
|
||||
to the hero form component's `onSubmit()` method:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (ngSubmit)" region="ngSubmit"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (ngSubmit)" region="ngSubmit">
|
||||
|
||||
</code-example>
|
||||
|
||||
You'd already defined a template reference variable,
|
||||
`#heroForm`, and initialized it with the value "ngForm".
|
||||
@ -624,7 +664,9 @@ You'll bind the form's overall validity via
|
||||
the `heroForm` variable to the button's `disabled` property
|
||||
using an event binding. Here's the code:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (submit-button)" region="submit-button"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (submit-button)" region="submit-button">
|
||||
|
||||
</code-example>
|
||||
|
||||
If you run the application now, you find that the button is enabled—although
|
||||
it doesn't do anything useful yet.
|
||||
@ -661,13 +703,17 @@ hide the data entry area and display something else.
|
||||
Wrap the form in a `<div>` and bind
|
||||
its `hidden` property to the `HeroFormComponent.submitted` property.
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="edit-div"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="edit-div">
|
||||
|
||||
</code-example>
|
||||
|
||||
The main form is visible from the start because the
|
||||
`submitted` property is false until you submit the form,
|
||||
as this fragment from the `HeroFormComponent` shows:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" header="src/app/hero-form/hero-form.component.ts (submitted)" region="submitted"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" header="src/app/hero-form/hero-form.component.ts (submitted)" region="submitted">
|
||||
|
||||
</code-example>
|
||||
|
||||
When you click the *Submit* button, the `submitted` flag becomes true and the form disappears
|
||||
as planned.
|
||||
@ -675,7 +721,9 @@ as planned.
|
||||
Now the app needs to show something else while the form is in the submitted state.
|
||||
Add the following HTML below the `<div>` wrapper you just wrote:
|
||||
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="submitted"></code-example>
|
||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" header="src/app/hero-form/hero-form.component.html (excerpt)" region="submitted">
|
||||
|
||||
</code-example>
|
||||
|
||||
There's the hero again, displayed read-only with interpolation bindings.
|
||||
This `<div>` appears only while the component is in the submitted state.
|
||||
|
@ -1,5 +1,12 @@
|
||||
# Frequently Used Modules
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
A basic understanding of [Bootstrapping](guide/bootstrapping).
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
An Angular app needs at least one module that serves as the root module.
|
||||
As you add features to your app, you can add them in modules.
|
||||
The following are frequently used Angular modules with examples
|
||||
|
@ -454,9 +454,10 @@ A form of property [data binding](#data-binding) in which a [template expression
|
||||
That text can be concatenated with neighboring text before it is assigned to an element property
|
||||
or displayed between element tags, as in this example.
|
||||
|
||||
```html
|
||||
<label>My current hero is {{hero.name}}</label>
|
||||
```
|
||||
<code-example language="html" escape="html">
|
||||
<label>My current hero is {{hero.name}}</label>
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
Read more about [interpolation](guide/template-syntax#interpolation) in [Template Syntax](guide/template-syntax).
|
||||
@ -748,7 +749,7 @@ For more information, see [Schematics](guide/schematics) and [Integrating Librar
|
||||
Schematics come with their own command-line tool.
|
||||
Using Node 6.9 or above, install the Schematics CLI globally:
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example format="." language="bash">
|
||||
npm install -g @angular-devkit/schematics-cli
|
||||
</code-example>
|
||||
|
||||
@ -763,7 +764,7 @@ NgModules are delivered within scoped packages whose names begin with the Angula
|
||||
|
||||
Import a scoped package in the same way that you import a normal package.
|
||||
|
||||
<code-example path="architecture/src/app/app.component.ts" header="architecture/src/app/app.component.ts (import)" region="import">
|
||||
<code-example path="architecture/src/app/app.component.ts" linenums="false" header="architecture/src/app/app.component.ts (import)" region="import">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
@ -19,7 +19,7 @@ an NgModule, directive-level injectors follow the structure of the component hie
|
||||
|
||||
The choices you make about where to configure providers lead to differences in the final bundle size, service _scope_, and service _lifetime_.
|
||||
|
||||
When you specify providers in the `@Injectable()` decorator of the service itself (typically at the app root level), optimization tools such as those used by the CLI's production builds can perform *tree shaking*, which removes services that aren't used by your app. Tree shaking results in smaller bundle sizes.
|
||||
When you specify providers in the `@Injectable()` decorator of the service itself (typically at the app root level), optimization tools such as those used by the CLI's production builds can perform *tree shaking*, which removes services that aren't used by your app. Tree shaking results in smaller bundle sizes.
|
||||
|
||||
* Learn more about [tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers).
|
||||
|
||||
@ -29,12 +29,12 @@ You're likely to inject `UserService` in many places throughout the app and will
|
||||
<header>Platform injector</header>
|
||||
|
||||
When you use `providedIn:'root'`, you are configuring the root injector for the _app_, which is the injector for `AppModule`.
|
||||
The actual root of the entire injector hierarchy is a _platform injector_ that is the parent of app-root injectors.
|
||||
The actual root of the entire injector hierarchy is a _platform injector_ that is the parent of app-root injectors.
|
||||
This allows multiple apps to share a platform configuration. For example, a browser has only one URL bar, no matter how many apps you have running.
|
||||
|
||||
The platform injector is used internally during bootstrap, to configure platform-specific dependencies. You can configure additional platform-specific providers at the platform level by supplying `extraProviders` using the `platformBrowser()` function.
|
||||
The platform injector is used internally during bootstrap, to configure platform-specific dependencies. You can configure additional platform-specific providers at the platform level by supplying `extraProviders` using the `platformBrowser()` function.
|
||||
|
||||
Learn more about dependency resolution through the injector hierarchy:
|
||||
Learn more about dependency resolution through the injector hierarchy:
|
||||
[What you always wanted to know about Angular Dependency Injection tree](https://blog.angularindepth.com/angular-dependency-injection-and-tree-shakeable-tokens-4588a8f70d5d)
|
||||
|
||||
|
||||
@ -42,24 +42,24 @@ Learn more about dependency resolution through the injector hierarchy:
|
||||
|
||||
*NgModule-level* providers can be specified with `@NgModule()` `providers` metadata option, or in the `@Injectable()` `providedIn` option (with some module other than the root `AppModule`).
|
||||
|
||||
Use the `@NgModule()` `providers` option if a module is [lazy loaded](guide/lazy-loading-ngmodules). The module's own injector is configured with the provider when that module is loaded, and Angular can inject the corresponding services in any class it creates in that module. If you use the `@Injectable()` option `providedIn: MyLazyloadModule`, the provider could be shaken out at compile time, if it is not used anywhere else in the app.
|
||||
Use the `@NgModule()` `providers` option if a module is [lazy loaded](guide/lazy-loading-ngmodules). The module's own injector is configured with the provider when that module is loaded, and Angular can inject the corresponding services in any class it creates in that module. If you use the `@Injectable()` option `providedIn: MyLazyloadModule`, the provider could be shaken out at compile time, if it is not used anywhere else in the app.
|
||||
|
||||
* Learn more about [tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers).
|
||||
|
||||
For both root-level and module-level injectors, a service instance lives for the life of the app or module, and Angular injects this one service instance in every class that needs it.
|
||||
|
||||
*Component-level* providers configure each component instance's own injector.
|
||||
Angular can only inject the corresponding services in that component instance or one of its descendant component instances.
|
||||
Angular can't inject the same service instance anywhere else.
|
||||
*Component-level* providers configure each component instance's own injector.
|
||||
Angular can only inject the corresponding services in that component instance or one of its descendant component instances.
|
||||
Angular can't inject the same service instance anywhere else.
|
||||
|
||||
A component-provided service may have a limited lifetime.
|
||||
Each new instance of the component gets its own instance of the service.
|
||||
A component-provided service may have a limited lifetime.
|
||||
Each new instance of the component gets its own instance of the service.
|
||||
When the component instance is destroyed, so is that service instance.
|
||||
|
||||
In our sample app, `HeroComponent` is created when the application starts
|
||||
In our sample app, `HeroComponent` is created when the application starts
|
||||
and is never destroyed,
|
||||
so the `HeroService` instance created for `HeroComponent` lives for the life of the app.
|
||||
If you want to restrict `HeroService` access to `HeroComponent` and its nested
|
||||
so the `HeroService` instance created for `HeroComponent` lives for the life of the app.
|
||||
If you want to restrict `HeroService` access to `HeroComponent` and its nested
|
||||
`HeroListComponent`, provide `HeroService` at the component level, in `HeroComponent` metadata.
|
||||
|
||||
* See more [examples of component-level injection](#component-injectors) below.
|
||||
@ -67,32 +67,32 @@ If you want to restrict `HeroService` access to `HeroComponent` and its nested
|
||||
|
||||
{@a register-providers-injectable}
|
||||
|
||||
### @Injectable-level configuration
|
||||
### @Injectable-level configuration
|
||||
|
||||
The `@Injectable()` decorator identifies every service class. The `providedIn` metadata option for a service class configures a specific injector (typically `root`)
|
||||
to use the decorated class as a provider of the service.
|
||||
When an injectable class provides its own service to the `root` injector, the service is available anywhere the class is imported.
|
||||
to use the decorated class as a provider of the service.
|
||||
When an injectable class provides its own service to the `root` injector, the service is available anywhere the class is imported.
|
||||
|
||||
The following example configures a provider for `HeroService` using the `@Injectable()` decorator on the class.
|
||||
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.0.ts" header="src/app/heroes/heroes.service.ts"></code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.0.ts" header="src/app/heroes/heroes.service.ts" linenums="false"> </code-example>
|
||||
|
||||
This configuration tells Angular that the app's root injector is responsible for creating an
|
||||
This configuration tells Angular that the app's root injector is responsible for creating an
|
||||
instance of `HeroService` by invoking its constructor,
|
||||
and for making that instance available across the application.
|
||||
and for making that instance available across the application.
|
||||
|
||||
Providing a service with the app's root injector is a typical case,
|
||||
and the CLI sets up this kind of a provider automatically for you
|
||||
when generating a new service.
|
||||
when generating a new service.
|
||||
However, you might not always want to provide your service at the root level.
|
||||
You might, for instance, want users to explicitly opt-in to using the service.
|
||||
|
||||
Instead of specifying the `root` injector, you can set `providedIn` to a specific NgModule.
|
||||
Instead of specifying the `root` injector, you can set `providedIn` to a specific NgModule.
|
||||
|
||||
For example, in the following excerpt, the `@Injectable()` decorator configures a provider
|
||||
that is available in any injector that includes the `HeroModule`.
|
||||
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.4.ts" header="src/app/heroes/hero.service.ts"></code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.4.ts" header="src/app/heroes/hero.service.ts" linenums="false"> </code-example>
|
||||
|
||||
This is generally no different from configuring the injector of the NgModule itself,
|
||||
except that the service is tree-shakable if the NgModule doesn't use it.
|
||||
@ -108,16 +108,18 @@ and leave it up to the app whether to provide the service.
|
||||
You can configure a provider at the module level using the `providers` metadata option for a non-root NgModule, in order to limit the scope of the provider to that module.
|
||||
This is the equivalent of specifying the non-root module in the `@Injectable()` metadata, except that the service provided via `providers` is not tree-shakable.
|
||||
|
||||
You generally don't need to specify `AppModule` with `providedIn`, because the app's `root` injector is the `AppModule` injector.
|
||||
You generally don't need to specify `AppModule` with `providedIn`, because the app's `root` injector is the `AppModule` injector.
|
||||
However, if you configure a app-wide provider in the `@NgModule()` metadata for `AppModule`,
|
||||
it overrides one configured for `root` in the `@Injectable()` metadata.
|
||||
You can do this to configure a non-default provider of a service that is shared with multiple apps.
|
||||
it overrides one configured for `root` in the `@Injectable()` metadata.
|
||||
You can do this to configure a non-default provider of a service that is shared with multiple apps.
|
||||
|
||||
Here is an example of the case where the component router configuration includes
|
||||
a non-default [location strategy](guide/router#location-strategy) by listing its provider
|
||||
in the `providers` list of the `AppModule`.
|
||||
|
||||
<code-example path="dependency-injection-in-action/src/app/app.module.ts" region="providers" header="src/app/app.module.ts (providers)"></code-example>
|
||||
<code-example path="dependency-injection-in-action/src/app/app.module.ts" region="providers" header="src/app/app.module.ts (providers)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
{@a register-providers-component}
|
||||
@ -128,18 +130,19 @@ Individual components within an NgModule have their own injectors.
|
||||
You can limit the scope of a provider to a component and its children
|
||||
by configuring the provider at the component level using the `@Component` metadata.
|
||||
|
||||
The following example is a revised `HeroesComponent` that specifies `HeroService` in its `providers` array. `HeroService` can provide heroes to instances of this component, or to any child component instances.
|
||||
The following example is a revised `HeroesComponent` that specifies `HeroService` in its `providers` array. `HeroService` can provide heroes to instances of this component, or to any child component instances.
|
||||
|
||||
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" header="src/app/heroes/heroes.component.ts"></code-example>
|
||||
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" header="src/app/heroes/heroes.component.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
### Element injectors
|
||||
|
||||
An injector does not actually belong to a component, but rather to the component instance's anchor element in the DOM. A different component instance on a different DOM element uses a different injector.
|
||||
|
||||
Components are a special type of directive, and the `providers` property of
|
||||
`@Component()` is inherited from `@Directive()`.
|
||||
`@Component()` is inherited from `@Directive()`.
|
||||
Directives can also have dependencies, and you can configure providers
|
||||
in their `@Directive()` metadata.
|
||||
in their `@Directive()` metadata.
|
||||
When you configure a provider for a component or directive using the `providers` property, that provider belongs to the injector for the anchor DOM element. Components and directives on the same element share an injector.
|
||||
|
||||
<!--- TBD with examples
|
||||
@ -165,16 +168,16 @@ When a component requests a dependency, Angular tries to satisfy that dependency
|
||||
If the component's injector lacks the provider, it passes the request up to its parent component's injector.
|
||||
If that injector can't satisfy the request, it passes the request along to the next parent injector up the tree.
|
||||
The requests keep bubbling up until Angular finds an injector that can handle the request or runs out of ancestor injectors.
|
||||
If it runs out of ancestors, Angular throws an error.
|
||||
If it runs out of ancestors, Angular throws an error.
|
||||
|
||||
If you have registered a provider for the same DI token at different levels, the first one Angular encounters is the one it uses to provide the dependency. If, for example, a provider is registered locally in the component that needs a service, Angular doesn't look for another provider of the same service.
|
||||
If you have registered a provider for the same DI token at different levels, the first one Angular encounters is the one it uses to provide the dependency. If, for example, a provider is registered locally in the component that needs a service, Angular doesn't look for another provider of the same service.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You can cap the bubbling by adding the `@Host()` parameter decorator on the dependant-service parameter
|
||||
in a component's constructor.
|
||||
The hunt for providers stops at the injector for the host element of the component.
|
||||
in a component's constructor.
|
||||
The hunt for providers stops at the injector for the host element of the component.
|
||||
|
||||
* See an [example](guide/dependency-injection-in-action#qualify-dependency-lookup) of using `@Host` together with `@Optional`, another parameter decorator that lets you handle the null case if no provider is found.
|
||||
|
||||
@ -194,7 +197,7 @@ The guide sample offers some scenarios where you might want to do so.
|
||||
|
||||
### Scenario: service isolation
|
||||
|
||||
Architectural reasons may lead you to restrict access to a service to the application domain where it belongs.
|
||||
Architectural reasons may lead you to restrict access to a service to the application domain where it belongs.
|
||||
For example, the guide sample includes a `VillainsListComponent` that displays a list of villains.
|
||||
It gets those villains from a `VillainsService`.
|
||||
|
||||
@ -204,7 +207,9 @@ that would make the `VillainsService` available everywhere in the application, i
|
||||
Instead, you can provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this:
|
||||
|
||||
|
||||
<code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" header="src/app/villains-list.component.ts (metadata)" region="metadata"></code-example>
|
||||
<code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" linenums="false" header="src/app/villains-list.component.ts (metadata)" region="metadata">
|
||||
|
||||
</code-example>
|
||||
|
||||
By providing `VillainsService` in the `VillainsListComponent` metadata and nowhere else,
|
||||
the service becomes available only in the `VillainsListComponent` and its sub-component tree.
|
||||
@ -268,7 +273,9 @@ Every component would share the same service instance, and each component would
|
||||
To prevent this, we configure the component-level injector of `HeroTaxReturnComponent` to provide the service, using the `providers` property in the component metadata.
|
||||
|
||||
|
||||
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" header="src/app/hero-tax-return.component.ts (providers)" region="providers"></code-example>
|
||||
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" linenums="false" header="src/app/hero-tax-return.component.ts (providers)" region="providers">
|
||||
|
||||
</code-example>
|
||||
|
||||
The `HeroTaxReturnComponent` has its own provider of the `HeroTaxReturnService`.
|
||||
Recall that every component _instance_ has its own injector.
|
||||
|
@ -11,7 +11,7 @@ You can run the <live-example></live-example> that accompanies this guide.
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The sample app does not require a data server.
|
||||
It relies on the
|
||||
It relies on the
|
||||
[Angular _in-memory-web-api_](https://github.com/angular/in-memory-web-api/blob/master/README.md),
|
||||
which replaces the _HttpClient_ module's `HttpBackend`.
|
||||
The replacement service simulates the behavior of a REST-like backend.
|
||||
@ -22,50 +22,50 @@ Look at the `AppModule` _imports_ to see how it is configured.
|
||||
|
||||
## Setup
|
||||
|
||||
Before you can use the `HttpClient`, you need to import the Angular `HttpClientModule`.
|
||||
Before you can use the `HttpClient`, you need to import the Angular `HttpClientModule`.
|
||||
Most apps do so in the root `AppModule`.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/app.module.ts"
|
||||
region="sketch"
|
||||
header="app/app.module.ts (excerpt)">
|
||||
header="app/app.module.ts (excerpt)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Having imported `HttpClientModule` into the `AppModule`, you can inject the `HttpClient`
|
||||
into an application class as shown in the following `ConfigService` example.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="proto"
|
||||
header="app/config/config.service.ts (excerpt)">
|
||||
header="app/config/config.service.ts (excerpt)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
## Getting JSON data
|
||||
|
||||
Applications often request JSON data from the server.
|
||||
For example, the app might need a configuration file on the server, `config.json`,
|
||||
Applications often request JSON data from the server.
|
||||
For example, the app might need a configuration file on the server, `config.json`,
|
||||
that specifies resource URLs.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/assets/config.json"
|
||||
header="assets/config.json">
|
||||
header="assets/config.json" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The `ConfigService` fetches this file with a `get()` method on `HttpClient`.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="getConfig_1"
|
||||
header="app/config/config.service.ts (getConfig v.1)">
|
||||
header="app/config/config.service.ts (getConfig v.1)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
A component, such as `ConfigComponent`, injects the `ConfigService` and calls
|
||||
the `getConfig` service method.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.component.ts"
|
||||
region="v1"
|
||||
header="app/config/config.component.ts (showConfig v.1)">
|
||||
header="app/config/config.component.ts (showConfig v.1)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Because the service method returns an `Observable` of configuration data,
|
||||
@ -93,12 +93,12 @@ the component, even in simple cases like this one.
|
||||
|
||||
The subscribe callback above requires bracket notation to extract the data values.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.component.ts"
|
||||
region="v1_callback">
|
||||
region="v1_callback" linenums="false">
|
||||
</code-example>
|
||||
|
||||
You can't write `data.heroesUrl` because TypeScript correctly complains that the `data` object from the service does not have a `heroesUrl` property.
|
||||
You can't write `data.heroesUrl` because TypeScript correctly complains that the `data` object from the service does not have a `heroesUrl` property.
|
||||
|
||||
The `HttpClient.get()` method parsed the JSON server response into the anonymous `Object` type. It doesn't know what the shape of that object is.
|
||||
|
||||
@ -106,48 +106,48 @@ You can tell `HttpClient` the type of the response to make consuming the output
|
||||
|
||||
First, define an interface with the correct shape:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="config-interface">
|
||||
region="config-interface" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Then, specify that interface as the `HttpClient.get()` call's type parameter in the service:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="getConfig_2"
|
||||
header="app/config/config.service.ts (getConfig v.2)">
|
||||
region="getConfig_2"
|
||||
header="app/config/config.service.ts (getConfig v.2)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The callback in the updated component method receives a typed data object, which is
|
||||
easier and safer to consume:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.component.ts"
|
||||
region="v2"
|
||||
header="app/config/config.component.ts (showConfig v.2)">
|
||||
header="app/config/config.component.ts (showConfig v.2)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
### Reading the full response
|
||||
|
||||
The response body doesn't return all the data you may need. Sometimes servers return special headers or status codes to indicate certain conditions that are important to the application workflow.
|
||||
The response body doesn't return all the data you may need. Sometimes servers return special headers or status codes to indicate certain conditions that are important to the application workflow.
|
||||
|
||||
Tell `HttpClient` that you want the full response with the `observe` option:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="getConfigResponse">
|
||||
region="getConfigResponse" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Now `HttpClient.get()` returns an `Observable` of typed `HttpResponse` rather than just the JSON data.
|
||||
|
||||
The component's `showConfigResponse()` method displays the response headers as well as the configuration:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.component.ts"
|
||||
region="showConfigResponse"
|
||||
region="showConfigResponse"
|
||||
header="app/config/config.component.ts (showConfigResponse)"
|
||||
>
|
||||
linenums="false">
|
||||
</code-example>
|
||||
|
||||
As you can see, the response object has a `body` property of the correct type.
|
||||
@ -158,11 +158,11 @@ What happens if the request fails on the server, or if a poor network connection
|
||||
|
||||
You _could_ handle in the component by adding a second callback to the `.subscribe()`:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.component.ts"
|
||||
region="v3"
|
||||
region="v3"
|
||||
header="app/config/config.component.ts (showConfig v.3 with error handling)"
|
||||
>
|
||||
linenums="false">
|
||||
</code-example>
|
||||
|
||||
It's certainly a good idea to give the user some kind of feedback when data access fails.
|
||||
@ -180,15 +180,15 @@ Or something could go wrong on the client-side such as a network error that prev
|
||||
|
||||
The `HttpClient` captures both kinds of errors in its `HttpErrorResponse` and you can inspect that response to figure out what really happened.
|
||||
|
||||
Error inspection, interpretation, and resolution is something you want to do in the _service_,
|
||||
not in the _component_.
|
||||
Error inspection, interpretation, and resolution is something you want to do in the _service_,
|
||||
not in the _component_.
|
||||
|
||||
You might first devise an error handler like this one:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="handleError"
|
||||
header="app/config/config.service.ts (handleError)">
|
||||
region="handleError"
|
||||
header="app/config/config.service.ts (handleError)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Notice that this handler returns an RxJS [`ErrorObservable`](#rxjs) with a user-friendly error message.
|
||||
@ -198,10 +198,10 @@ even a "bad" one.
|
||||
Now you take the `Observables` returned by the `HttpClient` methods
|
||||
and _pipe them through_ to the error handler.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="getConfig_3"
|
||||
header="app/config/config.service.ts (getConfig v.3 with error handler)">
|
||||
region="getConfig_3"
|
||||
header="app/config/config.service.ts (getConfig v.3 with error handler)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
### `retry()`
|
||||
@ -215,10 +215,10 @@ The simplest is called `retry()` and it automatically re-subscribes to a failed
|
||||
|
||||
_Pipe_ it onto the `HttpClient` method result just before the error handler.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="getConfig"
|
||||
header="app/config/config.service.ts (getConfig with retry)">
|
||||
region="getConfig"
|
||||
header="app/config/config.service.ts (getConfig with retry)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
{@a rxjs}
|
||||
@ -229,17 +229,17 @@ You will encounter more RxJS artifacts as you continue below.
|
||||
|
||||
[RxJS](http://reactivex.io/rxjs/) is a library for composing asynchronous and callback-based code
|
||||
in a _functional, reactive style_.
|
||||
Many Angular APIs, including `HttpClient`, produce and consume RxJS `Observables`.
|
||||
Many Angular APIs, including `HttpClient`, produce and consume RxJS `Observables`.
|
||||
|
||||
RxJS itself is out-of-scope for this guide. You will find many learning resources on the web.
|
||||
While you can get by with a minimum of RxJS knowledge, you'll want to grow your RxJS skills over time in order to use `HttpClient` effectively.
|
||||
|
||||
If you're following along with these code snippets, note that you must import the RxJS observable and operator symbols that appear in those snippets. These `ConfigService` imports are typical.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/config/config.service.ts"
|
||||
region="rxjs-imports"
|
||||
header="app/config/config.service.ts (RxJS imports)">
|
||||
region="rxjs-imports"
|
||||
header="app/config/config.service.ts (RxJS imports)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
## Requesting non-JSON data
|
||||
@ -247,24 +247,24 @@ If you're following along with these code snippets, note that you must import th
|
||||
Not all APIs return JSON data. In this next example,
|
||||
a `DownloaderService` method reads a text file from the server
|
||||
and logs the file contents, before returning those contents to the caller
|
||||
as an `Observable<string>`.
|
||||
as an `Observable<string>`.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/downloader/downloader.service.ts"
|
||||
region="getTextFile"
|
||||
header="app/downloader/downloader.service.ts (getTextFile)">
|
||||
region="getTextFile"
|
||||
header="app/downloader/downloader.service.ts (getTextFile)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
`HttpClient.get()` returns a string rather than the default JSON because of the `responseType` option.
|
||||
|
||||
The RxJS `tap` operator (as in "wiretap") lets the code inspect good and error values passing through the observable without disturbing them.
|
||||
The RxJS `tap` operator (as in "wiretap") lets the code inspect good and error values passing through the observable without disturbing them.
|
||||
|
||||
A `download()` method in the `DownloaderComponent` initiates the request by subscribing to the service method.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/downloader/downloader.component.ts"
|
||||
region="download"
|
||||
header="app/downloader/downloader.component.ts (download)">
|
||||
region="download"
|
||||
header="app/downloader/downloader.component.ts (download)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
## Sending data to the server
|
||||
@ -279,28 +279,28 @@ The following sections excerpt methods of the sample's `HeroesService`.
|
||||
### Adding headers
|
||||
|
||||
Many servers require extra headers for save operations.
|
||||
For example, they may require a "Content-Type" header to explicitly declare
|
||||
For example, they may require a "Content-Type" header to explicitly declare
|
||||
the MIME type of the request body.
|
||||
Or perhaps the server requires an authorization token.
|
||||
|
||||
The `HeroesService` defines such headers in an `httpOptions` object that will be passed
|
||||
to every `HttpClient` save method.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.service.ts"
|
||||
region="http-options"
|
||||
header="app/heroes/heroes.service.ts (httpOptions)">
|
||||
region="http-options"
|
||||
header="app/heroes/heroes.service.ts (httpOptions)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
### Making a POST request
|
||||
|
||||
Apps often POST data to a server. They POST when submitting a form.
|
||||
Apps often POST data to a server. They POST when submitting a form.
|
||||
In the following example, the `HeroesService` posts when adding a hero to the database.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.service.ts"
|
||||
region="addHero"
|
||||
header="app/heroes/heroes.service.ts (addHero)">
|
||||
region="addHero"
|
||||
header="app/heroes/heroes.service.ts (addHero)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The `HttpClient.post()` method is similar to `get()` in that it has a type parameter
|
||||
@ -314,13 +314,13 @@ It takes two more parameters:
|
||||
|
||||
Of course it catches errors in much the same manner [described above](#error-details).
|
||||
|
||||
The `HeroesComponent` initiates the actual POST operation by subscribing to
|
||||
The `HeroesComponent` initiates the actual POST operation by subscribing to
|
||||
the `Observable` returned by this service method.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.component.ts"
|
||||
region="add-hero-subscribe"
|
||||
header="app/heroes/heroes.component.ts (addHero)">
|
||||
region="add-hero-subscribe"
|
||||
header="app/heroes/heroes.component.ts (addHero)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
When the server responds successfully with the newly added hero, the component adds
|
||||
@ -331,22 +331,22 @@ that hero to the displayed `heroes` list.
|
||||
This application deletes a hero with the `HttpClient.delete` method by passing the hero's id
|
||||
in the request URL.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.service.ts"
|
||||
region="deleteHero"
|
||||
header="app/heroes/heroes.service.ts (deleteHero)">
|
||||
region="deleteHero"
|
||||
header="app/heroes/heroes.service.ts (deleteHero)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The `HeroesComponent` initiates the actual DELETE operation by subscribing to
|
||||
The `HeroesComponent` initiates the actual DELETE operation by subscribing to
|
||||
the `Observable` returned by this service method.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.component.ts"
|
||||
region="delete-hero-subscribe"
|
||||
header="app/heroes/heroes.component.ts (deleteHero)">
|
||||
region="delete-hero-subscribe"
|
||||
header="app/heroes/heroes.component.ts (deleteHero)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The component isn't expecting a result from the delete operation, so it subscribes without a callback. Even though you are not using the result, you still have to subscribe. Calling the `subscribe()` method _executes_ the observable, which is what initiates the DELETE request.
|
||||
The component isn't expecting a result from the delete operation, so it subscribes without a callback. Even though you are not using the result, you still have to subscribe. Calling the `subscribe()` method _executes_ the observable, which is what initiates the DELETE request.
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
@ -355,9 +355,9 @@ You must call _subscribe()_ or nothing happens. Just calling `HeroesService.dele
|
||||
</div>
|
||||
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.component.ts"
|
||||
region="delete-hero-no-subscribe">
|
||||
region="delete-hero-no-subscribe" linenums="false">
|
||||
</code-example>
|
||||
|
||||
{@a always-subscribe}
|
||||
@ -400,10 +400,10 @@ req.subscribe();
|
||||
An app will send a PUT request to completely replace a resource with updated data.
|
||||
The following `HeroesService` example is just like the POST example.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.service.ts"
|
||||
region="updateHero"
|
||||
header="app/heroes/heroes.service.ts (updateHero)">
|
||||
region="updateHero"
|
||||
header="app/heroes/heroes.service.ts (updateHero)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
For the reasons [explained above](#always-subscribe), the caller (`HeroesComponent.update()` in this case) must `subscribe()` to the observable returned from the `HttpClient.put()`
|
||||
@ -427,15 +427,15 @@ You can do more.
|
||||
You can't directly modify the existing headers within the previous options
|
||||
object because instances of the `HttpHeaders` class are immutable.
|
||||
|
||||
Use the `set()` method instead.
|
||||
Use the `set()` method instead.
|
||||
It returns a clone of the current instance with the new changes applied.
|
||||
|
||||
Here's how you might update the authorization header (after the old token expired)
|
||||
Here's how you might update the authorization header (after the old token expired)
|
||||
before making the next request.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.service.ts"
|
||||
region="update-headers">
|
||||
region="update-headers" linenums="false">
|
||||
</code-example>
|
||||
|
||||
#### URL Parameters
|
||||
@ -443,9 +443,9 @@ before making the next request.
|
||||
Adding URL search parameters works a similar way.
|
||||
Here is a `searchHeroes` method that queries for heroes whose names contain the search term.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.service.ts"
|
||||
region="searchHeroes">
|
||||
region="searchHeroes" linenums="false">
|
||||
</code-example>
|
||||
|
||||
If there is a search term, the code constructs an options object with an HTML URL-encoded search parameter. If the term were "foo", the GET request URL would be `api/heroes/?name=foo`.
|
||||
@ -461,9 +461,9 @@ a search request for a package with that name to the NPM web API.
|
||||
|
||||
Here's a pertinent excerpt from the template:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/package-search/package-search.component.html"
|
||||
region="search"
|
||||
region="search"
|
||||
header="app/package-search/package-search.component.html (search)">
|
||||
</code-example>
|
||||
|
||||
@ -473,9 +473,9 @@ Sending a request for every keystroke could be expensive.
|
||||
It's better to wait until the user stops typing and then send a request.
|
||||
That's easy to implement with RxJS operators, as shown in this excerpt.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/package-search/package-search.component.ts"
|
||||
region="debounce"
|
||||
region="debounce"
|
||||
header="app/package-search/package-search.component.ts (excerpt)">
|
||||
</code-example>
|
||||
|
||||
@ -514,7 +514,7 @@ The `switchMap()` operator has three important characteristics.
|
||||
it cancels that request and sends a new one.
|
||||
|
||||
3. It returns service responses in their original request order, even if the
|
||||
server returns them out of order.
|
||||
server returns them out of order.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -526,14 +526,14 @@ consider moving it to a utility function or into the `PackageSearchService` itse
|
||||
|
||||
### Intercepting requests and responses
|
||||
|
||||
_HTTP Interception_ is a major feature of `@angular/common/http`.
|
||||
_HTTP Interception_ is a major feature of `@angular/common/http`.
|
||||
With interception, you declare _interceptors_ that inspect and transform HTTP requests from your application to the server.
|
||||
The same interceptors may also inspect and transform the server's responses on their way back to the application.
|
||||
Multiple interceptors form a _forward-and-backward_ chain of request/response handlers.
|
||||
|
||||
Interceptors can perform a variety of _implicit_ tasks, from authentication to logging, in a routine, standard way, for every HTTP request/response.
|
||||
Interceptors can perform a variety of _implicit_ tasks, from authentication to logging, in a routine, standard way, for every HTTP request/response.
|
||||
|
||||
Without interception, developers would have to implement these tasks _explicitly_
|
||||
Without interception, developers would have to implement these tasks _explicitly_
|
||||
for each `HttpClient` method call.
|
||||
|
||||
#### Write an interceptor
|
||||
@ -541,12 +541,13 @@ for each `HttpClient` method call.
|
||||
To implement an interceptor, declare a class that implements the `intercept()` method of the `HttpInterceptor` interface.
|
||||
|
||||
Here is a do-nothing _noop_ interceptor that simply passes the request through without touching it:
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/noop-interceptor.ts"
|
||||
header="app/http-interceptors/noop-interceptor.ts">
|
||||
header="app/http-interceptors/noop-interceptor.ts"
|
||||
linenums="false">
|
||||
</code-example>
|
||||
|
||||
The `intercept` method transforms a request into an `Observable` that eventually returns the HTTP response.
|
||||
The `intercept` method transforms a request into an `Observable` that eventually returns the HTTP response.
|
||||
In this sense, each interceptor is fully capable of handling the request entirely by itself.
|
||||
|
||||
Most interceptors inspect the request on the way in and forward the (perhaps altered) request to the `handle()` method of the `next` object which implements the [`HttpHandler`](api/common/http/HttpHandler) interface.
|
||||
@ -563,22 +564,22 @@ This _no-op_ interceptor simply calls `next.handle()` with the original request
|
||||
|
||||
#### The _next_ object
|
||||
|
||||
The `next` object represents the next interceptor in the chain of interceptors.
|
||||
The `next` object represents the next interceptor in the chain of interceptors.
|
||||
The final `next` in the chain is the `HttpClient` backend handler that sends the request to the server and receives the server's response.
|
||||
|
||||
|
||||
Most interceptors call `next.handle()` so that the request flows through to the next interceptor and, eventually, the backend handler.
|
||||
An interceptor _could_ skip calling `next.handle()`, short-circuit the chain, and [return its own `Observable`](#caching) with an artificial server response.
|
||||
An interceptor _could_ skip calling `next.handle()`, short-circuit the chain, and [return its own `Observable`](#caching) with an artificial server response.
|
||||
|
||||
This is a common middleware pattern found in frameworks such as Express.js.
|
||||
|
||||
#### Provide the interceptor
|
||||
|
||||
The `NoopInterceptor` is a service managed by Angular's [dependency injection (DI)](guide/dependency-injection) system.
|
||||
The `NoopInterceptor` is a service managed by Angular's [dependency injection (DI)](guide/dependency-injection) system.
|
||||
Like other services, you must provide the interceptor class before the app can use it.
|
||||
|
||||
Because interceptors are (optional) dependencies of the `HttpClient` service,
|
||||
you must provide them in the same injector (or a parent of the injector) that provides `HttpClient`.
|
||||
Because interceptors are (optional) dependencies of the `HttpClient` service,
|
||||
you must provide them in the same injector (or a parent of the injector) that provides `HttpClient`.
|
||||
Interceptors provided _after_ DI creates the `HttpClient` are ignored.
|
||||
|
||||
This app provides `HttpClient` in the app's root injector, as a side-effect of importing the `HttpClientModule` in `AppModule`.
|
||||
@ -587,35 +588,35 @@ You should provide interceptors in `AppModule` as well.
|
||||
After importing the `HTTP_INTERCEPTORS` injection token from `@angular/common/http`,
|
||||
write the `NoopInterceptor` provider like this:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/index.ts"
|
||||
region="noop-provider">
|
||||
region="noop-provider" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Note the `multi: true` option.
|
||||
This required setting tells Angular that `HTTP_INTERCEPTORS` is a token for a _multiprovider_
|
||||
Note the `multi: true` option.
|
||||
This required setting tells Angular that `HTTP_INTERCEPTORS` is a token for a _multiprovider_
|
||||
that injects an array of values, rather than a single value.
|
||||
|
||||
You _could_ add this provider directly to the providers array of the `AppModule`.
|
||||
However, it's rather verbose and there's a good chance that
|
||||
However, it's rather verbose and there's a good chance that
|
||||
you'll create more interceptors and provide them in the same way.
|
||||
You must also pay [close attention to the order](#interceptor-order)
|
||||
You must also pay [close attention to the order](#interceptor-order)
|
||||
in which you provide these interceptors.
|
||||
|
||||
Consider creating a "barrel" file that gathers all the interceptor providers into an `httpInterceptorProviders` array, starting with this first one, the `NoopInterceptor`.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/index.ts"
|
||||
region="interceptor-providers"
|
||||
header="app/http-interceptors/index.ts">
|
||||
header="app/http-interceptors/index.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Then import and add it to the `AppModule` _providers array_ like this:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/app.module.ts"
|
||||
region="interceptor-providers"
|
||||
header="app/app.module.ts (interceptor providers)">
|
||||
header="app/app.module.ts (interceptor providers)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
As you create new interceptors, add them to the `httpInterceptorProviders` array and
|
||||
@ -646,8 +647,8 @@ That's because interceptors work at a lower level than those `HttpClient` method
|
||||
|
||||
Many interceptors are only concerned with the outgoing request and simply return the event stream from `next.handle()` without modifying it.
|
||||
|
||||
But interceptors that examine and modify the response from `next.handle()`
|
||||
will see all of these events.
|
||||
But interceptors that examine and modify the response from `next.handle()`
|
||||
will see all of these events.
|
||||
Your interceptor should return _every event untouched_ unless it has a _compelling reason to do otherwise_.
|
||||
|
||||
#### Immutability
|
||||
@ -659,39 +660,39 @@ rendering them largely immutable.
|
||||
They are immutable for a good reason: the app may retry a request several times before it succeeds, which means that the interceptor chain may re-process the same request multiple times.
|
||||
If an interceptor could modify the original request object, the re-tried operation would start from the modified request rather than the original. Immutability ensures that interceptors see the same request for each try.
|
||||
|
||||
TypeScript will prevent you from setting `HttpRequest` readonly properties.
|
||||
TypeScript will prevent you from setting `HttpRequest` readonly properties.
|
||||
|
||||
```javascript
|
||||
// Typescript disallows the following assignment because req.url is readonly
|
||||
req.url = req.url.replace('http://', 'https://');
|
||||
```
|
||||
To alter the request, clone it first and modify the clone before passing it to `next.handle()`.
|
||||
To alter the request, clone it first and modify the clone before passing it to `next.handle()`.
|
||||
You can clone and modify the request in a single step as in this example.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/ensure-https-interceptor.ts"
|
||||
region="excerpt"
|
||||
header="app/http-interceptors/ensure-https-interceptor.ts (excerpt)">
|
||||
region="excerpt"
|
||||
header="app/http-interceptors/ensure-https-interceptor.ts (excerpt)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The `clone()` method's hash argument allows you to mutate specific properties of the request while copying the others.
|
||||
|
||||
##### The request body
|
||||
|
||||
The `readonly` assignment guard can't prevent deep updates and, in particular,
|
||||
The `readonly` assignment guard can't prevent deep updates and, in particular,
|
||||
it can't prevent you from modifying a property of a request body object.
|
||||
|
||||
```javascript
|
||||
req.body.name = req.body.name.trim(); // bad idea!
|
||||
```
|
||||
|
||||
If you must mutate the request body, copy it first, change the copy,
|
||||
If you must mutate the request body, copy it first, change the copy,
|
||||
`clone()` the request, and set the clone's body with the new body, as in the following example.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/trim-name-interceptor.ts"
|
||||
region="excerpt"
|
||||
header="app/http-interceptors/trim-name-interceptor.ts (excerpt)">
|
||||
region="excerpt"
|
||||
header="app/http-interceptors/trim-name-interceptor.ts (excerpt)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
##### Clearing the request body
|
||||
@ -709,21 +710,21 @@ If you set the cloned request body to `null`, Angular knows you intend to clear
|
||||
|
||||
#### Set default headers
|
||||
|
||||
Apps often use an interceptor to set default headers on outgoing requests.
|
||||
Apps often use an interceptor to set default headers on outgoing requests.
|
||||
|
||||
The sample app has an `AuthService` that produces an authorization token.
|
||||
Here is its `AuthInterceptor` that injects that service to get the token and
|
||||
adds an authorization header with that token to every outgoing request:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/auth-interceptor.ts"
|
||||
header="app/http-interceptors/auth-interceptor.ts">
|
||||
</code-example>
|
||||
|
||||
The practice of cloning a request to set new headers is so common that
|
||||
The practice of cloning a request to set new headers is so common that
|
||||
there's a `setHeaders` shortcut for it:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/auth-interceptor.ts"
|
||||
region="set-header-shortcut">
|
||||
</code-example>
|
||||
@ -736,16 +737,16 @@ An interceptor that alters headers can be used for a number of different operati
|
||||
|
||||
#### Logging
|
||||
|
||||
Because interceptors can process the request and response _together_, they can do things like time and log
|
||||
an entire HTTP operation.
|
||||
Because interceptors can process the request and response _together_, they can do things like time and log
|
||||
an entire HTTP operation.
|
||||
|
||||
Consider the following `LoggingInterceptor`, which captures the time of the request,
|
||||
the time of the response, and logs the outcome with the elapsed time
|
||||
with the injected `MessageService`.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/logging-interceptor.ts"
|
||||
region="excerpt"
|
||||
region="excerpt"
|
||||
header="app/http-interceptors/logging-interceptor.ts)">
|
||||
</code-example>
|
||||
|
||||
@ -760,20 +761,20 @@ Neither `tap` nor `finalize` touch the values of the observable stream returned
|
||||
Interceptors can handle requests by themselves, without forwarding to `next.handle()`.
|
||||
|
||||
For example, you might decide to cache certain requests and responses to improve performance.
|
||||
You can delegate caching to an interceptor without disturbing your existing data services.
|
||||
You can delegate caching to an interceptor without disturbing your existing data services.
|
||||
|
||||
The `CachingInterceptor` demonstrates this approach.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/caching-interceptor.ts"
|
||||
region="v1"
|
||||
header="app/http-interceptors/caching-interceptor.ts)">
|
||||
region="v1"
|
||||
header="app/http-interceptors/caching-interceptor.ts)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The `isCachable()` function determines if the request is cachable.
|
||||
In this sample, only GET requests to the npm package search api are cachable.
|
||||
|
||||
If the request is not cachable, the interceptor simply forwards the request
|
||||
If the request is not cachable, the interceptor simply forwards the request
|
||||
to the next handler in the chain.
|
||||
|
||||
If a cachable request is found in the cache, the interceptor returns an `of()` _observable_ with
|
||||
@ -782,7 +783,7 @@ the cached response, by-passing the `next` handler (and all other interceptors d
|
||||
If a cachable request is not in cache, the code calls `sendRequest`.
|
||||
|
||||
{@a send-request}
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/caching-interceptor.ts"
|
||||
region="send-request">
|
||||
</code-example>
|
||||
@ -798,16 +799,16 @@ It _pipes_ the response through the `tap()` operator,
|
||||
whose callback adds the response to the cache.
|
||||
|
||||
The original response continues untouched back up through the chain of interceptors
|
||||
to the application caller.
|
||||
to the application caller.
|
||||
|
||||
Data services, such as `PackageSearchService`, are unaware that
|
||||
Data services, such as `PackageSearchService`, are unaware that
|
||||
some of their `HttpClient` requests actually return cached responses.
|
||||
|
||||
{@a cache-refresh}
|
||||
#### Return a multi-valued _Observable_
|
||||
|
||||
The `HttpClient.get()` method normally returns an _observable_
|
||||
that either emits the data or an error.
|
||||
The `HttpClient.get()` method normally returns an _observable_
|
||||
that either emits the data or an error.
|
||||
Some folks describe it as a "_one and done_" observable.
|
||||
|
||||
But an interceptor can change this to an _observable_ that emits more than once.
|
||||
@ -816,7 +817,7 @@ A revised version of the `CachingInterceptor` optionally returns an _observable_
|
||||
immediately emits the cached response, sends the request to the NPM web API anyway,
|
||||
and emits again later with the updated search results.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/caching-interceptor.ts"
|
||||
region="intercept-refresh">
|
||||
</code-example>
|
||||
@ -832,8 +833,8 @@ and adds it to the request before calling `HttpClient.get()`.
|
||||
|
||||
</div>
|
||||
|
||||
The revised `CachingInterceptor` sets up a server request
|
||||
whether there's a cached value or not,
|
||||
The revised `CachingInterceptor` sets up a server request
|
||||
whether there's a cached value or not,
|
||||
using the same `sendRequest()` method described [above](#send-request).
|
||||
The `results$` observable will make the request when subscribed.
|
||||
|
||||
@ -848,15 +849,15 @@ Subscribers see a sequence of _two_ responses.
|
||||
### Listening to progress events
|
||||
|
||||
Sometimes applications transfer large amounts of data and those transfers can take a long time.
|
||||
File uploads are a typical example.
|
||||
File uploads are a typical example.
|
||||
Give the users a better experience by providing feedback on the progress of such transfers.
|
||||
|
||||
To make a request with progress events enabled, you can create an instance of `HttpRequest`
|
||||
To make a request with progress events enabled, you can create an instance of `HttpRequest`
|
||||
with the `reportProgress` option set true to enable tracking of progress events.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/uploader/uploader.service.ts"
|
||||
region="upload-request"
|
||||
region="upload-request"
|
||||
header="app/uploader/uploader.service.ts (upload request)">
|
||||
</code-example>
|
||||
|
||||
@ -872,24 +873,24 @@ When using [`HttpClient#request()`](api/common/http/HttpClient#request) with an
|
||||
Next, pass this request object to the `HttpClient.request()` method, which
|
||||
returns an `Observable` of `HttpEvents`, the same events processed by interceptors:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/uploader/uploader.service.ts"
|
||||
region="upload-body"
|
||||
header="app/uploader/uploader.service.ts (upload body)">
|
||||
region="upload-body"
|
||||
header="app/uploader/uploader.service.ts (upload body)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The `getEventMessage` method interprets each type of `HttpEvent` in the event stream.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/uploader/uploader.service.ts"
|
||||
region="getEventMessage"
|
||||
header="app/uploader/uploader.service.ts (getEventMessage)">
|
||||
region="getEventMessage"
|
||||
header="app/uploader/uploader.service.ts (getEventMessage)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The sample app for this guide doesn't have a server that accepts uploaded files.
|
||||
The `UploadInterceptor` in `app/http-interceptors/upload-interceptor.ts`
|
||||
The `UploadInterceptor` in `app/http-interceptors/upload-interceptor.ts`
|
||||
intercepts and short-circuits upload requests
|
||||
by returning an observable of simulated events.
|
||||
|
||||
@ -910,44 +911,45 @@ In order to prevent collisions in environments where multiple Angular apps share
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
*Note that `HttpClient` supports only the client half of the XSRF protection scheme.*
|
||||
Your backend service must be configured to set the cookie for your page, and to verify that
|
||||
the header is present on all eligible requests.
|
||||
*Note that `HttpClient` supports only the client half of the XSRF protection scheme.*
|
||||
Your backend service must be configured to set the cookie for your page, and to verify that
|
||||
the header is present on all eligible requests.
|
||||
If not, Angular's default protection will be ineffective.
|
||||
|
||||
</div>
|
||||
|
||||
### Configuring custom cookie/header names
|
||||
|
||||
If your backend service uses different names for the XSRF token cookie or header,
|
||||
If your backend service uses different names for the XSRF token cookie or header,
|
||||
use `HttpClientXsrfModule.withOptions()` to override the defaults.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/app/app.module.ts"
|
||||
region="xsrf">
|
||||
region="xsrf"
|
||||
linenums="false">
|
||||
</code-example>
|
||||
|
||||
## Testing HTTP requests
|
||||
|
||||
Like any external dependency, the HTTP backend needs to be mocked
|
||||
so your tests can simulate interaction with a remote server.
|
||||
The `@angular/common/http/testing` library makes
|
||||
so your tests can simulate interaction with a remote server.
|
||||
The `@angular/common/http/testing` library makes
|
||||
setting up such mocking straightforward.
|
||||
|
||||
### Mocking philosophy
|
||||
|
||||
Angular's HTTP testing library is designed for a pattern of testing wherein
|
||||
Angular's HTTP testing library is designed for a pattern of testing wherein
|
||||
the app executes code and makes requests first.
|
||||
|
||||
Then a test expects that certain requests have or have not been made,
|
||||
performs assertions against those requests,
|
||||
Then a test expects that certain requests have or have not been made,
|
||||
performs assertions against those requests,
|
||||
and finally provide responses by "flushing" each expected request.
|
||||
|
||||
|
||||
At the end, tests may verify that the app has made no unexpected requests.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You can run <live-example stackblitz="specs">these sample tests</live-example>
|
||||
You can run <live-example stackblitz="specs">these sample tests</live-example>
|
||||
in a live coding environment.
|
||||
|
||||
The tests described in this guide are in `src/testing/http-client.spec.ts`.
|
||||
@ -958,23 +960,23 @@ There are also tests of an application data service that call `HttpClient` in
|
||||
|
||||
### Setup
|
||||
|
||||
To begin testing calls to `HttpClient`,
|
||||
To begin testing calls to `HttpClient`,
|
||||
import the `HttpClientTestingModule` and the mocking controller, `HttpTestingController`,
|
||||
along with the other symbols your tests require.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="imports"
|
||||
header="app/testing/http-client.spec.ts (imports)">
|
||||
region="imports"
|
||||
header="app/testing/http-client.spec.ts (imports)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Then add the `HttpClientTestingModule` to the `TestBed` and continue with
|
||||
the setup of the _service-under-test_.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="setup"
|
||||
header="app/testing/http-client.spec.ts(setup)">
|
||||
region="setup"
|
||||
header="app/testing/http-client.spec.ts(setup)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Now requests made in the course of your tests will hit the testing backend instead of the normal backend.
|
||||
@ -984,44 +986,47 @@ so they can be referenced during the tests.
|
||||
|
||||
### Expecting and answering requests
|
||||
|
||||
Now you can write a test that expects a GET Request to occur and provides a mock response.
|
||||
Now you can write a test that expects a GET Request to occur and provides a mock response.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="get-test"
|
||||
header="app/testing/http-client.spec.ts(httpClient.get)">
|
||||
region="get-test"
|
||||
header="app/testing/http-client.spec.ts(httpClient.get)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The last step, verifying that no requests remain outstanding, is common enough for you to move it into an `afterEach()` step:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="afterEach">
|
||||
region="afterEach"
|
||||
linenums="false">
|
||||
</code-example>
|
||||
|
||||
#### Custom request expectations
|
||||
|
||||
If matching by URL isn't sufficient, it's possible to implement your own matching function.
|
||||
If matching by URL isn't sufficient, it's possible to implement your own matching function.
|
||||
For example, you could look for an outgoing request that has an authorization header:
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="predicate">
|
||||
region="predicate"
|
||||
linenums="false">
|
||||
</code-example>
|
||||
|
||||
As with the previous `expectOne()`,
|
||||
As with the previous `expectOne()`,
|
||||
the test will fail if 0 or 2+ requests satisfy this predicate.
|
||||
|
||||
#### Handling more than one request
|
||||
|
||||
If you need to respond to duplicate requests in your test, use the `match()` API instead of `expectOne()`.
|
||||
It takes the same arguments but returns an array of matching requests.
|
||||
Once returned, these requests are removed from future matching and
|
||||
It takes the same arguments but returns an array of matching requests.
|
||||
Once returned, these requests are removed from future matching and
|
||||
you are responsible for flushing and verifying them.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="multi-request">
|
||||
region="multi-request"
|
||||
linenums="false">
|
||||
</code-example>
|
||||
|
||||
### Testing for errors
|
||||
@ -1030,14 +1035,16 @@ You should test the app's defenses against HTTP requests that fail.
|
||||
|
||||
Call `request.flush()` with an error message, as seen in the following example.
|
||||
|
||||
<code-example
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="404">
|
||||
region="404"
|
||||
linenums="false">
|
||||
</code-example>
|
||||
|
||||
Alternatively, you can call `request.error()` with an `ErrorEvent`.
|
||||
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="network-error">
|
||||
region="network-error"
|
||||
linenums="false">
|
||||
</code-example>
|
||||
|
@ -10,8 +10,8 @@ an AOT-compiled app, translated into French.
|
||||
{@a angular-i18n}
|
||||
## Angular and i18n
|
||||
|
||||
*Internationalization* is the process of designing and preparing your app to be usable in different languages.
|
||||
*Localization* is the process of translating your internationalized app into specific languages for particular locales.
|
||||
*Internationalization* is the process of designing and preparing your app to be usable in different languages.
|
||||
*Localization* is the process of translating your internationalized app into specific languages for particular locales.
|
||||
|
||||
Angular simplifies the following aspects of internationalization:
|
||||
* Displaying dates, number, percentages, and currencies in a local format.
|
||||
@ -19,7 +19,7 @@ Angular simplifies the following aspects of internationalization:
|
||||
* Handling plural forms of words.
|
||||
* Handling alternative text.
|
||||
|
||||
For localization, you can use the [Angular CLI](cli) to generate most of the boilerplate necessary to create files for translators, and to publish your app in multiple languages.
|
||||
For localization, you can use the [Angular CLI](cli) to generate most of the boilerplate necessary to create files for translators, and to publish your app in multiple languages.
|
||||
After you have set up your app to use i18n, the CLI can help you with the following steps:
|
||||
* Extracting localizable text into a file that you can send out to be translated.
|
||||
* Building and serving the app for a given locale, using the translated text.
|
||||
@ -85,7 +85,8 @@ The CLI imports the locale data for you when you use the parameter `--configurat
|
||||
|
||||
If you want to import locale data for other languages, you can do it manually:
|
||||
|
||||
<code-example path="i18n/doc-files/app.locale_data.ts" region="import-locale" header="src/app/app.module.ts"></code-example>
|
||||
<code-example path="i18n/doc-files/app.locale_data.ts" region="import-locale" header="src/app/app.module.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The first parameter is an object containing the locale data imported from `@angular/common/locales`.
|
||||
By default, the imported locale data is registered with the locale id that is defined in the Angular
|
||||
@ -99,7 +100,8 @@ The files in `@angular/common/locales` contain most of the locale data that you
|
||||
need, but some advanced formatting options might only be available in the extra dataset that you can
|
||||
import from `@angular/common/locales/extra`. An error message informs you when this is the case.
|
||||
|
||||
<code-example path="i18n/doc-files/app.locale_data_extra.ts" region="import-locale-extra" header="src/app/app.module.ts"></code-example>
|
||||
<code-example path="i18n/doc-files/app.locale_data_extra.ts" region="import-locale-extra" header="src/app/app.module.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -130,9 +132,9 @@ The i18n template translation process has four phases:
|
||||
|
||||
* `--i18nFile`=*path to the translation file*
|
||||
* `--i18nFormat`=*format of the translation file*
|
||||
* `--i18nLocale`= *locale id*
|
||||
* `--i18nLocale`= *locale id*
|
||||
|
||||
The command replaces the original messages with translated text, and generates a new version of the app in the target language.
|
||||
The command replaces the original messages with translated text, and generates a new version of the app in the target language.
|
||||
|
||||
You need to build and deploy a separate version of the app for each supported language.
|
||||
|
||||
@ -144,11 +146,13 @@ text is to be translated.
|
||||
|
||||
In the example below, an `<h1>` tag displays a simple English language greeting, "Hello i18n!"
|
||||
|
||||
<code-example path="i18n/doc-files/app.component.html" region="greeting" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="i18n/doc-files/app.component.html" region="greeting" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
To mark the greeting for translation, add the `i18n` attribute to the `<h1>` tag.
|
||||
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -167,7 +171,8 @@ To translate a text message accurately, the translator may need additional infor
|
||||
You can add a description of the text message as the value of the `i18n` attribute, as shown in the
|
||||
example below:
|
||||
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-desc" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-desc" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The translator may also need to know the meaning or intent of the text message within this particular
|
||||
app context.
|
||||
@ -175,7 +180,8 @@ app context.
|
||||
You add context by beginning the `i18n` attribute value with the _meaning_ and
|
||||
separating it from the _description_ with the `|` character: `<meaning>|<description>`
|
||||
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-meaning" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-meaning" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
All occurrences of a text message that have the same meaning will have the same translation.
|
||||
A text message that is associated with different meanings can have different translations.
|
||||
@ -193,7 +199,8 @@ text messages with different descriptions (not different meanings), then they ar
|
||||
The angular i18n extractor tool generates a file with a translation unit entry for each `i18n`
|
||||
attribute in a template. By default, it assigns each translation unit a unique id such as this one:
|
||||
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="generated-id"></code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="generated-id" linenums="false">
|
||||
</code-example>
|
||||
|
||||
When you change the translatable text, the extractor tool generates a new id for that translation unit.
|
||||
You must then update the translation file with the new id.
|
||||
@ -201,12 +208,14 @@ You must then update the translation file with the new id.
|
||||
Alternatively, you can specify a custom id in the `i18n` attribute by using the prefix `@@`.
|
||||
The example below defines the custom id `introductionHeader`:
|
||||
|
||||
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-solo-id' header='app/app.component.html'></code-example>
|
||||
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-solo-id' header='app/app.component.html' linenums="false">
|
||||
</code-example>
|
||||
|
||||
When you specify a custom id, the extractor tool and compiler generate a translation unit with that
|
||||
custom id.
|
||||
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="custom-id"></code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="custom-id" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The custom id is persistent. The extractor tool does not change it when the translatable text changes.
|
||||
Therefore, you do not need to update the translation. This approach makes maintenance easier.
|
||||
@ -217,11 +226,13 @@ You can use a custom id in combination with a description by including both in t
|
||||
`i18n` attribute. In the example below, the `i18n` attribute value includes a description, followed
|
||||
by the custom `id`:
|
||||
|
||||
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-id' header='app/app.component.html'></code-example>
|
||||
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-id' header='app/app.component.html' linenums="false">
|
||||
</code-example>
|
||||
|
||||
You also can add a meaning, as shown in this example:
|
||||
|
||||
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-meaning-and-id' header='app/app.component.html'></code-example>
|
||||
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-meaning-and-id' header='app/app.component.html' linenums="false">
|
||||
</code-example>
|
||||
|
||||
#### Define unique custom ids
|
||||
|
||||
@ -264,7 +275,8 @@ However, if you don't want to create a new DOM element merely to facilitate tran
|
||||
you can wrap the text in an `<ng-container>` element.
|
||||
The `<ng-container>` is transformed into an html comment:
|
||||
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-ng-container" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-ng-container" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
{@a translate-attributes}
|
||||
### Translate attributes
|
||||
@ -272,13 +284,15 @@ The `<ng-container>` is transformed into an html comment:
|
||||
Displayed text is sometimes supplied as the value of an attribute, rather than the content of tag.
|
||||
For example, if your template has an image with a `title` attribute, the text value of the `title` attribute needs to be translated.
|
||||
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-title" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-title" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
To mark an attribute for translation, add an attribute in the form of `i18n-x`,
|
||||
where `x` is the name of the attribute to translate. The following example shows how to mark the
|
||||
`title` attribute for translation by adding the `i18n-title` attribute on the `img` tag:
|
||||
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-title-translate" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-title-translate" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
This technique works for any attribute of any element.
|
||||
|
||||
@ -287,8 +301,8 @@ syntax.
|
||||
|
||||
## Regular expressions for plurals and selections
|
||||
|
||||
Different languages have different pluralization rules and grammatical constructions that add
|
||||
complexity to the translation task.
|
||||
Different languages have different pluralization rules and grammatical constructions that add
|
||||
complexity to the translation task.
|
||||
You can use regular expressions with the `plural` and `select` clauses to provide patterns that aid translation in these cases.
|
||||
|
||||
{@a plural-ICU}
|
||||
@ -302,7 +316,8 @@ Other languages might express the cardinality differently.
|
||||
The example below shows how to use a `plural` ICU expression to display one of those three options
|
||||
based on when the update occurred:
|
||||
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-plural" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-plural" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
* The first parameter is the key. It is bound to the component property (`minutes`), which determines
|
||||
the number of minutes.
|
||||
@ -357,7 +372,8 @@ The following format message in the component template binds to the component's
|
||||
which outputs one of the following string values: "male", "female" or "other".
|
||||
The message maps those values to the appropriate translations:
|
||||
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
{@a nesting-ICUS}
|
||||
### Nesting plural and select ICU expressions
|
||||
@ -379,7 +395,7 @@ Open a terminal window at the root of the app project and run the CLI command `x
|
||||
ng xi18n
|
||||
</code-example>
|
||||
|
||||
By default, the command creates a file named `messages.xlf` in your `src/` folder.
|
||||
By default, the command creates a file named `messages.xlf` in your `src/` folder.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -395,7 +411,7 @@ For more information, see the [Angular Ahead-of-Time Webpack Plugin documentatio
|
||||
{@a other-formats}
|
||||
### Output options
|
||||
|
||||
You can supply command options to change the format, the name, the location, and the source locale of the extracted file.
|
||||
You can supply command options to change the format, the name, the location, and the source locale of the extracted file.
|
||||
For example, to create a file in the `src/locale` folder, specify the output path:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
@ -456,7 +472,7 @@ file. This information is not used by Angular, but external translation tools ma
|
||||
|
||||
The `ng xi18n` command generates a translation source file named `messages.xlf` in the project `src`
|
||||
folder.
|
||||
The next step is to translate the display strings in this source file into language-specific
|
||||
The next step is to translate the display strings in this source file into language-specific
|
||||
translation files. The example in this guide creates a French translation file.
|
||||
|
||||
{@a localization-folder}
|
||||
@ -502,7 +518,8 @@ This sample file is easy to translate without a special editor or knowledge of F
|
||||
|
||||
1. Open `messages.fr.xlf` and find the first `<trans-unit>` section:
|
||||
|
||||
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello-before" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello-before" header="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
> This XML element represents the translation of the `<h1>` greeting tag that you marked with the
|
||||
`i18n` attribute earlier in this guide.
|
||||
@ -517,11 +534,13 @@ This sample file is easy to translate without a special editor or knowledge of F
|
||||
and context provided by the source, description, and meaning elements to guide your selection of
|
||||
the appropriate French translation.
|
||||
|
||||
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello" header="src/locale/messages.fr.xlf (<trans-unit>, after translation)"></code-example>
|
||||
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-hello" header="src/locale/messages.fr.xlf (<trans-unit>, after translation)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
3. Translate the other text nodes the same way:
|
||||
|
||||
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-other-nodes" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
> <code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-other-nodes" header="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
@ -547,7 +566,8 @@ must be just below the translation unit for the logo.
|
||||
|
||||
To translate a `plural`, translate its ICU format match values:
|
||||
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-plural" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-plural" header="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
You can add or remove plural cases, with each language having its own cardinality. (See
|
||||
[CLDR plural rules](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html).)
|
||||
@ -557,7 +577,8 @@ You can add or remove plural cases, with each language having its own cardinalit
|
||||
|
||||
Below is the content of our example `select` ICU expression in the component template:
|
||||
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="i18n/src/app/app.component.html" region="i18n-select" header="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The extraction tool broke that into two translation units because ICU expressions are extracted
|
||||
separately.
|
||||
@ -567,16 +588,19 @@ In place of the `select` is a placeholder, `<x id="ICU">`, that represents the `
|
||||
Translate the text and move around the placeholder if necessary, but don't remove it. If you remove
|
||||
the placeholder, the ICU expression will not be present in your translated app.
|
||||
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-1" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-1" header="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The second translation unit, immediately below the first one, contains the `select` message.
|
||||
Translate that as well.
|
||||
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-2" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-select-2" header="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Here they are together, after translation:
|
||||
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-select" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-select" header="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
{@a translate-nested}
|
||||
### Translate a nested expression
|
||||
@ -584,15 +608,18 @@ Here they are together, after translation:
|
||||
A nested expression is similar to the previous examples. As in the previous example, there are
|
||||
two translation units. The first one contains the text outside of the nested expression:
|
||||
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-1" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-1" header="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The second unit contains the complete nested expression:
|
||||
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-2" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-2" header="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
And both together:
|
||||
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested" header="src/locale/messages.fr.xlf (<trans-unit>)"></code-example>
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested" header="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The entire template translation is complete. The next section describes how to load that translation
|
||||
into the app.
|
||||
@ -756,7 +783,8 @@ behavior of the compiler. You can use it to specify the translation providers:
|
||||
|
||||
Then provide the `LOCALE_ID` in the main module:
|
||||
|
||||
<code-example path="i18n/doc-files/app.module.ts" header="src/app/app.module.ts"></code-example>
|
||||
<code-example path="i18n/doc-files/app.module.ts" header="src/app/app.module.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
|
||||
{@a missing-translation}
|
||||
@ -809,4 +837,4 @@ For example, if the French version of your application is served from https://my
|
||||
}
|
||||
```
|
||||
|
||||
For more details about how to create scripts to generate an app in multiple languages and how to set up Apache 2 and NGINX to serve them from different subdirectories, read [this tutorial by Philippe Martin](https://dev.to/angular/deploying-an-i18n-angular-app-with-angular-cli-2fb9).
|
||||
For more details about how to create scripts to generate an app in multiple languages and how to set up Apache 2 to serve them from different subdirectories, read [this tutorial by Philippe Martin](https://medium.com/@feloy/deploying-an-i18n-angular-app-with-angular-cli-fc788f17e358#.1xq4iy6fp).
|
||||
|
@ -1,13 +1,23 @@
|
||||
# Lazy Loading Feature Modules
|
||||
|
||||
#### Prerequisites
|
||||
A basic understanding of the following:
|
||||
* [Feature Modules](guide/feature-modules).
|
||||
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
|
||||
* [Frequently Used Modules](guide/frequent-ngmodules).
|
||||
* [Types of Feature Modules](guide/module-types).
|
||||
* [Routing and Navigation](guide/router).
|
||||
|
||||
For the final sample app with two lazy loaded modules that this page describes, see the
|
||||
<live-example></live-example>.
|
||||
|
||||
<hr>
|
||||
|
||||
## High level view
|
||||
|
||||
By default, NgModules are eagerly loaded, which means that as soon as the app loads, so do all the NgModules, whether or not they are immediately necessary. For large apps with lots of routes, consider lazy loading—a design pattern that loads NgModules as needed. Lazy loading helps keep initial
|
||||
bundle sizes smaller, which in turn helps decrease load times.
|
||||
|
||||
For the final sample app with two lazy loaded modules that this page describes, see the
|
||||
<live-example></live-example>.
|
||||
|
||||
There are three main steps to setting up a lazy loaded feature module:
|
||||
|
||||
1. Create the feature module.
|
||||
@ -88,7 +98,9 @@ placeholder markup in `app.component.html` with a custom nav
|
||||
so you can easily navigate to your modules in the browser:
|
||||
|
||||
|
||||
<code-example path="lazy-loading-ngmodules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="lazy-loading-ngmodules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -126,7 +138,9 @@ Each feature module acts as a doorway via the router. In the `AppRoutingModule`,
|
||||
In `AppRoutingModule`, update the `routes` array with the following:
|
||||
|
||||
|
||||
<code-example path="lazy-loading-ngmodules/src/app/app-routing.module.ts" region="const-routes" header="src/app/app-routing.module.ts"></code-example>
|
||||
<code-example path="lazy-loading-ngmodules/src/app/app-routing.module.ts" region="const-routes" header="src/app/app-routing.module.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
The import statements stay the same. The first two paths are the routes to the `CustomersModule` and the `OrdersModule` respectively. Notice that the lazy loading syntax uses `loadChildren` followed by a function that uses the browser's built-in `import('...')` syntax for dynamic imports. The import path is the relative path to the module.
|
||||
@ -136,7 +150,9 @@ The import statements stay the same. The first two paths are the routes to the `
|
||||
Next, take a look at `customers.module.ts`. If you’re using the CLI and following the steps outlined in this page, you don’t have to do anything here. The feature module is like a connector between the `AppRoutingModule` and the feature routing module. The `AppRoutingModule` imports the feature module, `CustomersModule`, and `CustomersModule` in turn imports the `CustomersRoutingModule`.
|
||||
|
||||
|
||||
<code-example path="lazy-loading-ngmodules/src/app/customers/customers.module.ts" region="customers-module" header="src/app/customers/customers.module.ts"></code-example>
|
||||
<code-example path="lazy-loading-ngmodules/src/app/customers/customers.module.ts" region="customers-module" header="src/app/customers/customers.module.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -147,14 +163,18 @@ The `customers.module.ts` file imports the `CustomersRoutingModule` and `Custome
|
||||
|
||||
The next step is in `customers-routing.module.ts`. First, import the component at the top of the file with the other JavaScript import statements. Then, add the route to `CustomerListComponent`.
|
||||
|
||||
<code-example path="lazy-loading-ngmodules/src/app/customers/customers-routing.module.ts" region="customers-routing-module" header="src/app/customers/customers-routing.module.ts"></code-example>
|
||||
<code-example path="lazy-loading-ngmodules/src/app/customers/customers-routing.module.ts" region="customers-routing-module" header="src/app/customers/customers-routing.module.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
Notice that the `path` is set to an empty string. This is because the path in `AppRoutingModule` is already set to `customers`, so this route in the `CustomersRoutingModule`, is already within the `customers` context. Every route in this routing module is a child route.
|
||||
|
||||
Repeat this last step of importing the `OrdersListComponent` and configuring the Routes array for the `orders-routing.module.ts`:
|
||||
|
||||
<code-example path="lazy-loading-ngmodules/src/app/orders/orders-routing.module.ts" region="orders-routing-module-detail" header="src/app/orders/orders-routing.module.ts (excerpt)"></code-example>
|
||||
<code-example path="lazy-loading-ngmodules/src/app/orders/orders-routing.module.ts" region="orders-routing-module-detail" header="src/app/orders/orders-routing.module.ts (excerpt)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
Now, if you view the app in the browser, the three buttons take you to each module.
|
||||
|
||||
|
@ -23,7 +23,7 @@ Each interface has a single hook method whose name is the interface name prefixe
|
||||
For example, the `OnInit` interface has a hook method named `ngOnInit()`
|
||||
that Angular calls shortly after creating the component:
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" header="peek-a-boo.component.ts (excerpt)"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" header="peek-a-boo.component.ts (excerpt)" linenums="false"></code-example>
|
||||
|
||||
No directive or component will implement all of the lifecycle hooks.
|
||||
Angular only calls a directive/component hook method *if it is defined*.
|
||||
@ -339,13 +339,13 @@ The heroes will never know they're being watched.
|
||||
The sneaky spy directive is simple, consisting almost entirely of `ngOnInit()` and `ngOnDestroy()` hooks
|
||||
that log messages to the parent via an injected `LoggerService`.
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" header="src/app/spy.directive.ts"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" header="src/app/spy.directive.ts" linenums="false"></code-example>
|
||||
|
||||
You can apply the spy to any native or component element and it'll be initialized and destroyed
|
||||
at the same time as that element.
|
||||
Here it is attached to the repeated hero `<div>`:
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" header="src/app/spy.component.html"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" header="src/app/spy.component.html" linenums="false"></code-example>
|
||||
|
||||
Each spy's birth and death marks the birth and death of the attached hero `<div>`
|
||||
with an entry in the *Hook Log* as seen here:
|
||||
@ -425,7 +425,7 @@ You risk memory leaks if you neglect to do so.
|
||||
Angular calls its `ngOnChanges()` method whenever it detects changes to ***input properties*** of the component (or directive).
|
||||
This example monitors the `OnChanges` hook.
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" header="on-changes.component.ts (excerpt)"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" header="on-changes.component.ts (excerpt)" linenums="false"></code-example>
|
||||
|
||||
The `ngOnChanges()` method takes an object that maps each changed property name to a
|
||||
[SimpleChange](api/core/SimpleChange) object holding the current and previous property values.
|
||||
@ -433,7 +433,7 @@ This hook iterates over the changed properties and logs them.
|
||||
|
||||
The example component, `OnChangesComponent`, has two input properties: `hero` and `power`.
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" header="src/app/on-changes.component.ts"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" header="src/app/on-changes.component.ts" linenums="false"></code-example>
|
||||
|
||||
The host `OnChangesParentComponent` binds to them like this:
|
||||
|
||||
@ -468,7 +468,7 @@ Use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch
|
||||
|
||||
The *DoCheck* sample extends the *OnChanges* sample with the following `ngDoCheck()` hook:
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" header="DoCheckComponent (ngDoCheck)"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" header="DoCheckComponent (ngDoCheck)" linenums="false"></code-example>
|
||||
|
||||
This code inspects certain _values of interest_, capturing and comparing their current state against previous values.
|
||||
It writes a special message to the log when there are no substantive changes to the `hero` or the `power`
|
||||
@ -497,25 +497,25 @@ The *AfterView* sample explores the `AfterViewInit()` and `AfterViewChecked()` h
|
||||
|
||||
Here's a child view that displays a hero's name in an `<input>`:
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" header="ChildComponent"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" header="ChildComponent" linenums="false"></code-example>
|
||||
|
||||
The `AfterViewComponent` displays this child view *within its template*:
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" header="AfterViewComponent (template)"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" header="AfterViewComponent (template)" linenums="false"></code-example>
|
||||
|
||||
The following hooks take action based on changing values *within the child view*,
|
||||
which can only be reached by querying for the child view via the property decorated with
|
||||
[@ViewChild](api/core/ViewChild).
|
||||
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" header="AfterViewComponent (class excerpts)"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" header="AfterViewComponent (class excerpts)" linenums="false"></code-example>
|
||||
|
||||
{@a wait-a-tick}
|
||||
|
||||
### Abide by the unidirectional data flow rule
|
||||
The `doSomething()` method updates the screen when the hero name exceeds 10 characters.
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" header="AfterViewComponent (doSomething)"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" header="AfterViewComponent (doSomething)" linenums="false"></code-example>
|
||||
|
||||
Why does the `doSomething()` method wait a tick before updating `comment`?
|
||||
|
||||
@ -559,7 +559,7 @@ Consider this variation on the [previous _AfterView_](guide/lifecycle-hooks#afte
|
||||
This time, instead of including the child view within the template, it imports the content from
|
||||
the `AfterContentComponent`'s parent. Here's the parent's template:
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" header="AfterContentParentComponent (template excerpt)"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" header="AfterContentParentComponent (template excerpt)" linenums="false"></code-example>
|
||||
|
||||
Notice that the `<app-child>` tag is tucked between the `<after-content>` tags.
|
||||
Never put content between a component's element tags *unless you intend to project that content
|
||||
@ -567,7 +567,7 @@ into the component*.
|
||||
|
||||
Now look at the component's template:
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" header="AfterContentComponent (template)"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" header="AfterContentComponent (template)" linenums="false"></code-example>
|
||||
|
||||
The `<ng-content>` tag is a *placeholder* for the external content.
|
||||
It tells Angular where to insert that content.
|
||||
@ -603,7 +603,7 @@ The following *AfterContent* hooks take action based on changing values in a *co
|
||||
which can only be reached by querying for them via the property decorated with
|
||||
[@ContentChild](api/core/ContentChild).
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" header="AfterContentComponent (class excerpts)"></code-example>
|
||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" header="AfterContentComponent (class excerpts)" linenums="false"></code-example>
|
||||
|
||||
{@a no-unidirectional-flow-worries}
|
||||
|
||||
|
@ -1,5 +1,16 @@
|
||||
|
||||
|
||||
# Types of Feature Modules
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
A basic understanding of the following concepts:
|
||||
* [Feature Modules](guide/feature-modules).
|
||||
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
|
||||
* [Frequently Used Modules](guide/frequent-ngmodules).
|
||||
|
||||
<hr>
|
||||
|
||||
There are five general categories of feature modules which
|
||||
tend to fall into the following groups:
|
||||
|
||||
|
@ -1,5 +1,15 @@
|
||||
# NgModule API
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
A basic understanding of the following concepts:
|
||||
* [Bootstrapping](guide/bootstrapping).
|
||||
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
|
||||
|
||||
<hr />
|
||||
|
||||
## Purpose of `@NgModule`
|
||||
|
||||
At a high level, NgModules are a way to organize Angular apps
|
||||
and they accomplish this through the metadata in the `@NgModule`
|
||||
decorator.
|
||||
|
@ -1,5 +1,13 @@
|
||||
# NgModule FAQs
|
||||
|
||||
|
||||
#### Prerequisites:
|
||||
|
||||
A basic understanding of the following concepts:
|
||||
* [NgModules](guide/ngmodules).
|
||||
|
||||
<hr />
|
||||
|
||||
NgModules help organize an application into cohesive blocks of functionality.
|
||||
|
||||
This page answers the questions many developers ask about NgModule design and implementation.
|
||||
@ -470,7 +478,8 @@ You can throw an error or take other remedial action.
|
||||
Certain NgModules, such as `BrowserModule`, implement such a guard.
|
||||
Here is a custom constructor for an NgModule called `GreetingModule`.
|
||||
|
||||
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts (Constructor)"></code-example>
|
||||
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts (Constructor)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -1,9 +1,13 @@
|
||||
# JavaScript Modules vs. NgModules
|
||||
|
||||
#### Prerequisites
|
||||
A basic understanding of [JavaScript/ECMAScript modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/).
|
||||
|
||||
<hr>
|
||||
|
||||
JavaScript and Angular use modules to organize code, and
|
||||
though they organize it differently, Angular apps rely on both.
|
||||
|
||||
|
||||
## JavaScript modules
|
||||
|
||||
In JavaScript, modules are individual files with JavaScript code in them. To make what’s in them available, you write an export statement, usually after the relevant code, like this:
|
||||
@ -20,8 +24,6 @@ import { AppComponent } from './app.component';
|
||||
|
||||
JavaScript modules help you namespace, preventing accidental global variables.
|
||||
|
||||
For more information on JavaScript modules, see [JavaScript/ECMAScript modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/).
|
||||
|
||||
## NgModules
|
||||
|
||||
<!-- KW-- perMisko: let's discuss. This does not answer the question why it is different. Also, last sentence is confusing.-->
|
||||
|
@ -1,5 +1,13 @@
|
||||
# NgModules
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
A basic understanding of the following concepts:
|
||||
* [Bootstrapping](guide/bootstrapping).
|
||||
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
|
||||
|
||||
<hr>
|
||||
|
||||
**NgModules** configure the injector and the compiler and help organize related things together.
|
||||
|
||||
An NgModule is a class marked by the `@NgModule` decorator.
|
||||
@ -12,6 +20,7 @@ For an example app showcasing all the techniques that NgModules related pages
|
||||
cover, see the <live-example></live-example>. For explanations on the individual techniques, visit the relevant NgModule pages under the NgModules
|
||||
section.
|
||||
|
||||
|
||||
## Angular modularity
|
||||
|
||||
Modules are a great way to organize an application and extend it with capabilities from external libraries.
|
||||
@ -48,14 +57,12 @@ You then import these modules into the root module.
|
||||
|
||||
## The basic NgModule
|
||||
|
||||
The [Angular CLI](cli) generates the following basic `AppModule` when creating a new app.
|
||||
The [Angular CLI](cli) generates the following basic app module when creating a new app.
|
||||
|
||||
|
||||
<code-example path="ngmodules/src/app/app.module.1.ts" header="src/app/app.module.ts (default AppModule)">
|
||||
// @NgModule decorator with its metadata
|
||||
<code-example path="bootstrapping/src/app/app.module.ts" region="whole-ngmodule" header="src/app/app.module.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
At the top are the import statements. The next section is where you configure the `@NgModule` by stating what components and directives belong to it (`declarations`) as well as which other modules it uses (`imports`). For more information on the structure of an `@NgModule`, be sure to read [Bootstrapping](guide/bootstrapping).
|
||||
At the top are the import statements. The next section is where you configure the `@NgModule` by stating what components and directives belong to it (`declarations`) as well as which other modules it uses (`imports`). This page builds on [Bootstrapping](guide/bootstrapping), which covers the structure of an NgModule in detail. If you need more information on the structure of an `@NgModule`, be sure to read [Bootstrapping](guide/bootstrapping).
|
||||
|
||||
<hr />
|
||||
|
||||
|
@ -26,14 +26,18 @@ In this page, you'll use pipes to transform a component's birthday property into
|
||||
a human-friendly date.
|
||||
|
||||
|
||||
<code-example path="pipes/src/app/hero-birthday1.component.ts" header="src/app/hero-birthday1.component.ts"></code-example>
|
||||
<code-example path="pipes/src/app/hero-birthday1.component.ts" header="src/app/hero-birthday1.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Focus on the component's template.
|
||||
|
||||
|
||||
<code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" header="src/app/app.component.html" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -75,7 +79,9 @@ Modify the birthday template to give the date pipe a format parameter.
|
||||
After formatting the hero's April 15th birthday, it renders as **<samp>04/15/88</samp>**:
|
||||
|
||||
|
||||
<code-example path="pipes/src/app/app.component.html" region="format-birthday" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="pipes/src/app/app.component.html" region="format-birthday" header="src/app/app.component.html" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -89,7 +95,9 @@ Write a second component that *binds* the pipe's format parameter
|
||||
to the component's `format` property. Here's the template for that component:
|
||||
|
||||
|
||||
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" header="src/app/hero-birthday2.component.ts (template)"></code-example>
|
||||
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" header="src/app/hero-birthday2.component.ts (template)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -98,7 +106,9 @@ That method toggles the component's `format` property between a short form
|
||||
(`'shortDate'`) and a longer form (`'fullDate'`).
|
||||
|
||||
|
||||
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" header="src/app/hero-birthday2.component.ts (class)"></code-example>
|
||||
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" header="src/app/hero-birthday2.component.ts (class)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -133,7 +143,9 @@ the birthday is chained to the `DatePipe` and on to the `UpperCasePipe`.
|
||||
The birthday displays as **<samp>APR 15, 1988</samp>**.
|
||||
|
||||
|
||||
<code-example path="pipes/src/app/app.component.html" region="chained-birthday" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="pipes/src/app/app.component.html" region="chained-birthday" header="src/app/app.component.html" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -141,7 +153,9 @@ This example—which displays **<samp>FRIDAY, APRIL 15, 1988</samp>**—
|
||||
the same pipes as above, but passes in a parameter to `date` as well.
|
||||
|
||||
|
||||
<code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" header="src/app/app.component.html" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -152,7 +166,9 @@ You can write your own custom pipes.
|
||||
Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers:
|
||||
|
||||
|
||||
<code-example path="pipes/src/app/exponential-strength.pipe.ts" header="src/app/exponential-strength.pipe.ts"></code-example>
|
||||
<code-example path="pipes/src/app/exponential-strength.pipe.ts" header="src/app/exponential-strength.pipe.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -184,7 +200,8 @@ Technically, it's optional; Angular looks for and executes the `transform` metho
|
||||
|
||||
Now you need a component to demonstrate the pipe.
|
||||
|
||||
<code-example path="pipes/src/app/power-booster.component.ts" header="src/app/power-booster.component.ts"></code-example>
|
||||
<code-example path="pipes/src/app/power-booster.component.ts" header="src/app/power-booster.component.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<figure>
|
||||
<img src='generated/images/guide/pipes/power-booster.png' alt="Power Booster">
|
||||
@ -254,13 +271,17 @@ In the next example, the component uses the default, aggressive change detection
|
||||
its display of every hero in the `heroes` array. Here's the template:
|
||||
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" header="src/app/flying-heroes.component.html (v1)"></code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" header="src/app/flying-heroes.component.html (v1)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
The companion component class provides heroes, adds heroes into the array, and can reset the array.
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" header="src/app/flying-heroes.component.ts (v1)"></code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" header="src/app/flying-heroes.component.ts (v1)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -272,13 +293,17 @@ If you added the ability to remove or change a hero, Angular would detect those
|
||||
|
||||
Add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly.
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" header="src/app/flying-heroes.component.html (flyers)"></code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" header="src/app/flying-heroes.component.html (flyers)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Here's the `FlyingHeroesPipe` implementation, which follows the pattern for custom pipes described earlier.
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" header="src/app/flying-heroes.pipe.ts"></code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" header="src/app/flying-heroes.pipe.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -290,7 +315,9 @@ It's just using a different change-detection algorithm that ignores changes to t
|
||||
|
||||
Notice how a hero is added:
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" header="src/app/flying-heroes.component.ts"></code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" header="src/app/flying-heroes.component.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -338,7 +365,9 @@ You make a pipe impure by setting its pure flag to false. You could make the `Fl
|
||||
impure like this:
|
||||
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" header="src/app/flying-heroes.pipe.ts"></code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" header="src/app/flying-heroes.pipe.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -412,14 +441,18 @@ The only difference is the `pure` flag in the pipe metadata.
|
||||
This is a good candidate for an impure pipe because the `transform` function is trivial and fast.
|
||||
|
||||
|
||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" header="src/app/flying-heroes.pipe.ts (filter)" region="filter"></code-example>
|
||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" linenums="false" header="src/app/flying-heroes.pipe.ts (filter)" region="filter">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
You can derive a `FlyingHeroesImpureComponent` from `FlyingHeroesComponent`.
|
||||
|
||||
|
||||
<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>
|
||||
<code-example path="pipes/src/app/flying-heroes-impure.component.html" linenums="false" header="src/app/flying-heroes-impure.component.html (excerpt)" region="template-flying-heroes">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
|
@ -1,10 +1,16 @@
|
||||
# Providers
|
||||
|
||||
A provider is an instruction to the DI system on how to obtain a value for a dependency. Most of the time, these dependencies are services that you create and provide.
|
||||
#### Prerequisites:
|
||||
* A basic understanding of [Bootstrapping](guide/bootstrapping).
|
||||
* Familiarity with [Frequently Used Modules](guide/frequent-ngmodules).
|
||||
|
||||
For the final sample app using the provider that this page describes,
|
||||
see the <live-example></live-example>.
|
||||
|
||||
<hr>
|
||||
|
||||
A provider is an instruction to the DI system on how to obtain a value for a dependency. Most of the time, these dependencies are services that you create and provide.
|
||||
|
||||
## Providing a service
|
||||
|
||||
If you already have an app that was created with the [Angular CLI](cli), you can create a service using the [`ng generate`](cli/generate) CLI command in the root project directory. Replace _User_ with the name of your service.
|
||||
@ -15,7 +21,7 @@ ng generate service User
|
||||
|
||||
This command creates the following `UserService` skeleton:
|
||||
|
||||
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts"></code-example>
|
||||
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.ts" linenums="false"> </code-example>
|
||||
|
||||
You can now inject `UserService` anywhere in your application.
|
||||
|
||||
@ -32,11 +38,11 @@ You should always provide your service in the root injector unless there is a ca
|
||||
|
||||
It's also possible to specify that a service should be provided in a particular `@NgModule`. For example, if you don't want `UserService` to be available to applications unless they import a `UserModule` you've created, you can specify that the service should be provided in the module:
|
||||
|
||||
<code-example path="providers/src/app/user.service.1.ts" header="src/app/user.service.ts"></code-example>
|
||||
<code-example path="providers/src/app/user.service.1.ts" header="src/app/user.service.ts" linenums="false"> </code-example>
|
||||
|
||||
The example above shows the preferred way to provide a service in a module. This method is preferred because it enables tree-shaking of the service if nothing injects it. If it's not possible to specify in the service which module should provide it, you can also declare a provider for the service within the module:
|
||||
|
||||
<code-example path="providers/src/app/user.module.ts" header="src/app/user.module.ts"></code-example>
|
||||
<code-example path="providers/src/app/user.module.ts" header="src/app/user.module.ts" linenums="false"> </code-example>
|
||||
|
||||
## Limiting provider scope by lazy loading modules
|
||||
|
||||
@ -61,7 +67,8 @@ method is helpful for when you want to eagerly load a module that needs a servic
|
||||
Providing a service in the component limits the service only to that component (other components in
|
||||
the same module can’t access it.)
|
||||
|
||||
<code-example path="providers/src/app/app.component.ts" region="component-providers" header="src/app/app.component.ts"></code-example>
|
||||
<code-example path="providers/src/app/app.component.ts" region="component-providers" header="src/app/app.component.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
|
||||
## Providing services in modules vs. components
|
||||
|
@ -12,7 +12,7 @@ Try the <live-example title="Reactive Forms in Stackblitz">Reactive Forms live-e
|
||||
|
||||
## Introduction to reactive forms
|
||||
|
||||
Reactive forms use an explicit and immutable approach to managing the state of a form at a given point in time. Each change to the form state returns a new state, which maintains the integrity of the model between changes. Reactive forms are built around observable streams, where form inputs and values are provided as streams of input values, which can be accessed synchronously.
|
||||
Reactive forms use an explicit and immutable approach to managing the state of a form at a given point in time. Each change to the form state returns a new state, which maintains the integrity of the model between changes. Reactive forms are built around observable streams, where form inputs and values are provided as streams of input values, which can be accessed synchronously.
|
||||
|
||||
Reactive forms also provide a straightforward path to testing because you are assured that your data is consistent and predictable when requested. Any consumers of the streams have access to manipulate that data safely.
|
||||
|
||||
@ -26,9 +26,11 @@ This section describes how to add a single form control. In the example, the use
|
||||
|
||||
To use reactive forms, import `ReactiveFormsModule` from the `@angular/forms` package and add it to your NgModule's `imports` array.
|
||||
|
||||
<code-example path="reactive-forms/src/app/app.module.ts" region="imports" header="src/app/app.module.ts (excerpt)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/app.module.ts" region="imports" header="src/app/app.module.ts (excerpt)">
|
||||
|
||||
### Step 2: Generating and importing a new form control
|
||||
</code-example>
|
||||
|
||||
### Step 2: Generating and importing a new form control
|
||||
|
||||
Generate a component for the control.
|
||||
|
||||
@ -40,15 +42,19 @@ Generate a component for the control.
|
||||
|
||||
The `FormControl` class is the basic building block when using reactive forms. To register a single form control, import the `FormControl` class into your component and create a new instance of the form control to save as a class property.
|
||||
|
||||
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.ts" region="create-control" header="src/app/name-editor/name-editor.component.ts"></code-example>
|
||||
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.ts" region="create-control" header="src/app/name-editor/name-editor.component.ts">
|
||||
|
||||
Use the constructor of `FormControl` to set its initial value, which in this case is an empty string. By creating these controls in your component class, you get immediate access to listen for, update, and validate the state of the form input.
|
||||
</code-example>
|
||||
|
||||
Use the constructor of `FormControl` to set its initial value, which in this case is an empty string. By creating these controls in your component class, you get immediate access to listen for, update, and validate the state of the form input.
|
||||
|
||||
### Step 3: Registering the control in the template
|
||||
|
||||
After you create the control in the component class, you must associate it with a form control element in the template. Update the template with the form control using the `formControl` binding provided by `FormControlDirective` included in `ReactiveFormsModule`.
|
||||
|
||||
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="control-binding" header="src/app/name-editor/name-editor.component.html"></code-example>
|
||||
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="control-binding" linenums="false" header="src/app/name-editor/name-editor.component.html">
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -60,9 +66,11 @@ Using the template binding syntax, the form control is now registered to the `na
|
||||
|
||||
#### Displaying the component
|
||||
|
||||
The form control assigned to `name` is displayed when the component is added to a template.
|
||||
The form control assigned to `name` is displayed when the component is added to a template.
|
||||
|
||||
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-name-editor" header="src/app/app.component.html (name editor)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-name-editor" linenums="false" header="src/app/app.component.html (name editor)">
|
||||
|
||||
</code-example>
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/reactive-forms/name-editor-1.png" alt="Name Editor">
|
||||
@ -70,21 +78,23 @@ The form control assigned to `name` is displayed when the component is added to
|
||||
|
||||
## Managing control values
|
||||
|
||||
Reactive forms give you access to the form control state and value at a point in time. You can manipulate
|
||||
Reactive forms give you access to the form control state and value at a point in time. You can manipulate
|
||||
the current state and value through the component class or the component template. The following examples display the value of the form control instance and change it.
|
||||
|
||||
{@a display-value}
|
||||
|
||||
### Displaying a form control value
|
||||
|
||||
You can display the value in these ways:
|
||||
You can display the value in these ways:
|
||||
|
||||
* Through the `valueChanges` observable where you can listen for changes in the form's value in the template using `AsyncPipe` or in the component class using the `subscribe()` method.
|
||||
* With the `value` property. which gives you a snapshot of the current value.
|
||||
* Through the `valueChanges` observable where you can listen for changes in the form's value in the template using `AsyncPipe` or in the component class using the `subscribe()` method.
|
||||
* With the `value` property. which gives you a snapshot of the current value.
|
||||
|
||||
The following example shows you how to display the current value using interpolation in the template.
|
||||
|
||||
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="display-value" header="src/app/name-editor/name-editor.component.html (control value)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="display-value" linenums="false" header="src/app/name-editor/name-editor.component.html (control value)">
|
||||
|
||||
</code-example>
|
||||
|
||||
The displayed value changes as you update the form control element.
|
||||
|
||||
@ -94,7 +104,7 @@ Read about other `FormControl` properties and methods in the [Reactive forms API
|
||||
|
||||
### Replacing a form control value
|
||||
|
||||
Reactive forms have methods to change a control's value programmatically, which gives you the flexibility to update the value without user interaction. A form control instance provides a `setValue()` method that updates the value of the form control and validates the structure of the value provided against the control's structure. For example, when retrieving form data from a backend API or service, use the `setValue()` method to update the control to its new value, replacing the old value entirely.
|
||||
Reactive forms have methods to change a control's value programmatically, which gives you the flexibility to update the value without user interaction. A form control instance provides a `setValue()` method that updates the value of the form control and validates the structure of the value provided against the control's structure. For example, when retrieving form data from a backend API or service, use the `setValue()` method to update the control to its new value, replacing the old value entirely.
|
||||
|
||||
The following example adds a method to the component class to update the value of the control to *Nancy* using the `setValue()` method.
|
||||
|
||||
@ -102,9 +112,11 @@ The following example adds a method to the component class to update the value o
|
||||
|
||||
</code-example>
|
||||
|
||||
Update the template with a button to simulate a name update. When you click the **Update Name** button, the value entered in the form control element is reflected as its current value.
|
||||
Update the template with a button to simulate a name update. When you click the **Update Name** button, the value entered in the form control element is reflected as its current value.
|
||||
|
||||
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="update-value" header="src/app/name-editor/name-editor.component.html (update value)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/name-editor/name-editor.component.html" region="update-value" linenums="false" header="src/app/name-editor/name-editor.component.html (update value)">
|
||||
|
||||
</code-example>
|
||||
|
||||
The form model is the source of truth for the control, so when you click the button, the value of the input is changed within the component class, overriding its current value.
|
||||
|
||||
@ -150,17 +162,21 @@ The individual form controls are now collected within a group. A `FormGroup` ins
|
||||
|
||||
A form group tracks the status and changes for each of its controls, so if one of the controls changes, the parent control also emits a new status or value change. The model for the group is maintained from its members. After you define the model, you must update the template to reflect the model in the view.
|
||||
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroup" header="src/app/profile-editor/profile-editor.component.html (template form group)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroup" linenums="false" header="src/app/profile-editor/profile-editor.component.html (template form group)">
|
||||
|
||||
</code-example>
|
||||
|
||||
Note that just as a form group contains a group of controls, the *profile form* `FormGroup` is bound to the `form` element with the `FormGroup` directive, creating a communication layer between the model and the form containing the inputs. The `formControlName` input provided by the `FormControlName` directive binds each individual input to the form control defined in `FormGroup`. The form controls communicate with their respective elements. They also communicate changes to the form group instance, which provides the source of truth for the model value.
|
||||
|
||||
### Saving form data
|
||||
|
||||
The `ProfileEditor` component accepts input from the user, but in a real scenario you want to capture the form value and make available for further processing outside the component. The `FormGroup` directive listens for the `submit` event emitted by the `form` element and emits an `ngSubmit` event that you can bind to a callback function.
|
||||
The `ProfileEditor` component accepts input from the user, but in a real scenario you want to capture the form value and make available for further processing outside the component. The `FormGroup` directive listens for the `submit` event emitted by the `form` element and emits an `ngSubmit` event that you can bind to a callback function.
|
||||
|
||||
Add an `ngSubmit` event listener to the `form` tag with the `onSubmit()` callback method.
|
||||
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="ng-submit" header="src/app/profile-editor/profile-editor.component.html (submit event)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="ng-submit" linenums="false" header="src/app/profile-editor/profile-editor.component.html (submit event)">
|
||||
|
||||
</code-example>
|
||||
|
||||
The `onSubmit()` method in the `ProfileEditor` component captures the current value of `profileForm`. Use `EventEmitter` to keep the form encapsulated and to provide the form value outside the component. The following example uses `console.warn` to log a message to the browser console.
|
||||
|
||||
@ -168,11 +184,13 @@ The `onSubmit()` method in the `ProfileEditor` component captures the current va
|
||||
|
||||
</code-example>
|
||||
|
||||
The `submit` event is emitted by the `form` tag using the native DOM event. You trigger the event by clicking a button with `submit` type. This allows the user to press the **Enter** key to submit the completed form.
|
||||
The `submit` event is emitted by the `form` tag using the native DOM event. You trigger the event by clicking a button with `submit` type. This allows the user to press the **Enter** key to submit the completed form.
|
||||
|
||||
Use a `button` element to add a button to the bottom of the form to trigger the form submission.
|
||||
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="submit-button" header="src/app/profile-editor/profile-editor.component.html (submit button)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="submit-button" linenums="false" header="src/app/profile-editor/profile-editor.component.html (submit button)">
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -184,7 +202,9 @@ Use a `button` element to add a button to the bottom of the form to trigger the
|
||||
|
||||
To display the `ProfileEditor` component that contains the form, add it to a component template.
|
||||
|
||||
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-profile-editor" header="src/app/app.component.html (profile editor)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-profile-editor" linenums="false" header="src/app/app.component.html (profile editor)">
|
||||
|
||||
</code-example>
|
||||
|
||||
`ProfileEditor` allows you to manage the form control instances for the `firstName` and `lastName` controls within the form group instance.
|
||||
|
||||
@ -200,7 +220,9 @@ When building complex forms, managing the different areas of information is easi
|
||||
|
||||
An address is a good example of information that can be grouped together. Form groups can accept both form control and form group instances as children. This makes composing complex form models easier to maintain and logically group together. To create a nested group in `profileForm`, add a nested `address` element to the form group instance.
|
||||
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" region="nested-formgroup" header="src/app/profile-editor/profile-editor.component.ts (nested form group)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" region="nested-formgroup" linenums="false" header="src/app/profile-editor/profile-editor.component.ts (nested form group)">
|
||||
|
||||
</code-example>
|
||||
|
||||
In this example, `address group` combines the current `firstName` and `lastName` controls with the new `street`, `city`, `state`, and `zip` controls. Even though the `address` element in the form group is a child of the overall `profileForm` element in the form group, the same rules apply with value and status changes. Changes in status and value from the nested form group propagate to the parent form group, maintaining consistency with the overall model.
|
||||
|
||||
@ -210,7 +232,9 @@ After you update the model in the component class, update the template to connec
|
||||
|
||||
Add the `address` form group containing the `street`, `city`, `state`, and `zip` fields to the `ProfileEditor` template.
|
||||
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroupname" header="src/app/profile-editor/profile-editor.component.html (template nested form group)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroupname" linenums="false" header="src/app/profile-editor/profile-editor.component.html (template nested form group)">
|
||||
|
||||
</code-example>
|
||||
|
||||
The `ProfileEditor` form is displayed as one group, but the model is broken down further to represent the logical grouping areas.
|
||||
|
||||
@ -230,11 +254,11 @@ When updating the value for a form group instance that contains multiple control
|
||||
|
||||
### Patching the model value
|
||||
|
||||
There are two ways to update the model value:
|
||||
There are two ways to update the model value:
|
||||
|
||||
* Use the `setValue()` method to set a new value for an individual control. The `setValue()` method strictly adheres to the structure of the form group and replaces the entire value for the control.
|
||||
* Use the `setValue()` method to set a new value for an individual control. The `setValue()` method strictly adheres to the structure of the form group and replaces the entire value for the control.
|
||||
|
||||
* Use the `patchValue()` method to replace any properties defined in the object that have changed in the form model.
|
||||
* Use the `patchValue()` method to replace any properties defined in the object that have changed in the form model.
|
||||
|
||||
The strict checks of the `setValue()` method help catch nesting errors in complex forms, while `patchValue()` fails silently on those errors.
|
||||
|
||||
@ -246,13 +270,15 @@ In `ProfileEditorComponent`, use the `updateProfile` method with the example bel
|
||||
|
||||
Simulate an update by adding a button to the template to update the user profile on demand.
|
||||
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="patch-value" header="src/app/profile-editor/profile-editor.component.html (update value)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="patch-value" linenums="false" header="src/app/profile-editor/profile-editor.component.html (update value)">
|
||||
|
||||
</code-example>
|
||||
|
||||
When a user clicks the button, the `profileForm` model is updated with new values for `firstName` and `street`. Notice that `street` is provided in an object inside the `address` property. This is necessary because the `patchValue()` method applies the update against the model structure. `PatchValue()` only updates properties that the form model defines.
|
||||
|
||||
## Generating form controls with FormBuilder
|
||||
|
||||
Creating form control instances manually can become repetitive when dealing with multiple forms. The `FormBuilder` service provides convenient methods for generating controls.
|
||||
Creating form control instances manually can become repetitive when dealing with multiple forms. The `FormBuilder` service provides convenient methods for generating controls.
|
||||
|
||||
The following section refactors the `ProfileEditor` component to use the form builder service to create form control and form group instances.
|
||||
|
||||
@ -274,7 +300,7 @@ The `FormBuilder` service is an injectable provider that is provided with the re
|
||||
|
||||
### Step 3: Generating form controls
|
||||
|
||||
The `FormBuilder` service has three methods: `control()`, `group()`, and `array()`. These are factory methods for generating instances in your component classes including form controls, form groups, and form arrays.
|
||||
The `FormBuilder` service has three methods: `control()`, `group()`, and `array()`. These are factory methods for generating instances in your component classes including form controls, form groups, and form arrays.
|
||||
|
||||
Use the `group` method to create the `profileForm` controls.
|
||||
|
||||
@ -295,7 +321,7 @@ Compare using the form builder to creating the instances manually.
|
||||
<code-tabs>
|
||||
|
||||
<code-pane path="reactive-forms/src/app/profile-editor/profile-editor.component.1.ts" region="formgroup-compare" header="src/app/profile-editor/profile-editor.component.ts (instances)">
|
||||
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane path="reactive-forms/src/app/profile-editor/profile-editor.component.2.ts" region="formgroup-compare" header="src/app/profile-editor/profile-editor.component.ts (form builder)">
|
||||
@ -330,7 +356,9 @@ In the `ProfileEditor` component, add the `Validators.required` static method as
|
||||
|
||||
HTML5 has a set of built-in attributes that you can use for native validation, including `required`, `minlength`, and `maxlength`. You can take advantage of these optional attributes on your form input elements. Add the `required` attribute to the `firstName` input element.
|
||||
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="required-attribute" header="src/app/profile-editor/profile-editor.component.html (required attribute)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="required-attribute" linenums="false" header="src/app/profile-editor/profile-editor.component.html (required attribute)">
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
@ -344,7 +372,9 @@ When you add a required field to the form control, its initial status is invalid
|
||||
|
||||
Display the current status of `profileForm` using interpolation.
|
||||
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="display-status" header="src/app/profile-editor/profile-editor.component.html (display status)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="display-status" linenums="false" header="src/app/profile-editor/profile-editor.component.html (display status)">
|
||||
|
||||
</code-example>
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/reactive-forms/profile-editor-3.png" alt="Profile Editor Validation">
|
||||
@ -380,7 +410,7 @@ The aliases control in the form group instance is now populated with a single co
|
||||
|
||||
### Step 3: Accessing the FormArray control
|
||||
|
||||
A getter provides easy access to the aliases in the form array instance compared to repeating the `profileForm.get()` method to get each instance. The form array instance represents an undefined number of controls in an array. It's convenient to access a control through a getter, and this approach is easy to repeat for additional controls.
|
||||
A getter provides easy access to the aliases in the form array instance compared to repeating the `profileForm.get()` method to get each instance. The form array instance represents an undefined number of controls in an array. It's convenient to access a control through a getter, and this approach is easy to repeat for additional controls.
|
||||
|
||||
Use the getter syntax to create an `aliases` class property to retrieve the alias's form array control from the parent form group.
|
||||
|
||||
@ -404,13 +434,15 @@ In the template, each control is displayed as a separate input field.
|
||||
|
||||
### Step 4: Displaying the form array in the template
|
||||
|
||||
To attach the aliases from your form model, you must add it to the template. Similar to the `formGroupName` input provided by `FormGroupNameDirective`, `formArrayName` binds communication from the form array instance to the template with `FormArrayNameDirective`.
|
||||
To attach the aliases from your form model, you must add it to the template. Similar to the `formGroupName` input provided by `FormGroupNameDirective`, `formArrayName` binds communication from the form array instance to the template with `FormArrayNameDirective`.
|
||||
|
||||
Add the template HTML below after the `<div>` closing the `formGroupName` element.
|
||||
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="formarrayname" header="src/app/profile-editor/profile-editor.component.html (aliases form array template)"></code-example>
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="formarrayname" linenums="false" header="src/app/profile-editor/profile-editor.component.html (aliases form array template)">
|
||||
|
||||
The `*ngFor` directive iterates over each form control instance provided by the aliases form array instance. Because form array elements are unnamed, you assign the index to the `i` variable and pass it to each control to bind it to the `formControlName` input.
|
||||
</code-example>
|
||||
|
||||
The `*ngFor` directive iterates over each form control instance provided by the aliases form array instance. Because form array elements are unnamed, you assign the index to the `i` variable and pass it to each control to bind it to the `formControlName` input.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/reactive-forms/profile-editor-4.png" alt="Profile Editor Aliases">
|
||||
@ -522,7 +554,7 @@ Listed below are the base classes and services used to create and manage form co
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
@ -15,7 +15,8 @@ The [AnimationOptions](https://angular.io/api/animations/AnimationOptions) inter
|
||||
|
||||
To create a reusable animation, use the [`animation()`](https://angular.io/api/animations/animation) method to define an animation in a separate `.ts` file and declare this animation definition as a `const` export variable. You can then import and reuse this animation in any of your app components using the [`useAnimation()`](https://angular.io/api/animations/useAnimation) API.
|
||||
|
||||
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="reusable" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="reusable" language="typescript" linenums="false">
|
||||
</code-example>
|
||||
|
||||
In the above code snippet, `transAnimation` is made reusable by declaring it as an export variable.
|
||||
|
||||
@ -26,7 +27,8 @@ In the above code snippet, `transAnimation` is made reusable by declaring it as
|
||||
|
||||
You can import the reusable `transAnimation` variable in your component class and reuse it using the `useAnimation()` method as shown below.
|
||||
|
||||
<code-example path="animations/src/app/open-close.component.3.ts" header="src/app/open-close.component.ts" region="reusable" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/open-close.component.3.ts" header="src/app/open-close.component.ts" region="reusable" language="typescript" linenums="false">
|
||||
</code-example>
|
||||
|
||||
## More on Angular animations
|
||||
|
||||
@ -35,4 +37,4 @@ You may also be interested in the following:
|
||||
* [Introduction to Angular animations](guide/animations)
|
||||
* [Transition and triggers](guide/transition-and-triggers)
|
||||
* [Complex animation Sequences](guide/complex-animation-sequences)
|
||||
* [Route transition animations](guide/route-animations)
|
||||
* [Route transition animations](guide/route-animations)
|
@ -43,7 +43,8 @@ Use the `RouterModule.forRoot` method to define a set of routes. Also, import th
|
||||
|
||||
The following configuration defines the possible routes for the application.
|
||||
|
||||
<code-example path="animations/src/app/app.module.ts" header="src/app/app.module.ts" region="route-animation-data" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/app.module.ts" linenums="false" header="src/app/app.module.ts" region="route-animation-data" language="typescript">
|
||||
</code-example>
|
||||
|
||||
The `home` and `about` paths are associated with the `HomeComponent` and `AboutComponent` views. The route configuration tells the Angular router to instantiate the `HomeComponent` and `AboutComponent` views when the navigation matches the corresponding path.
|
||||
|
||||
@ -61,11 +62,13 @@ After configuring the routes, tell the Angular router where to render the views
|
||||
|
||||
The `<router-outlet>` container has an attribute directive that contains data about active routes and their states, based on the `data` property that we set in the route configuration.
|
||||
|
||||
<code-example path="animations/src/app/app.component.html" header="src/app/app.component.html" region="route-animations-outlet"></code-example>
|
||||
<code-example path="animations/src/app/app.component.html" header="src/app/app.component.html" region="route-animations-outlet">
|
||||
</code-example>
|
||||
|
||||
`AppComponent` defines a method that can detect when a view changes. The method assigns an animation state value to the animation trigger (`@routeAnimation`) based on the route configuration `data` property value. Here's an example of an `AppComponent` method that detects when a route change happens.
|
||||
|
||||
<code-example path="animations/src/app/app.component.ts" header="src/app/app.component.ts" region="prepare-router-outlet" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/app.component.ts" linenums="false" header="src/app/app.component.ts" region="prepare-router-outlet" language="typescript">
|
||||
</code-example>
|
||||
|
||||
Here, the `prepareRoute()` method takes the value of the output directive (established through `#outlet="outlet"`) and returns a string value representing the state of the animation based on the custom data of the current active route. You can use this data to control which transition to execute for each route.
|
||||
|
||||
@ -76,7 +79,8 @@ Animations can be defined directly inside your components. For this example we a
|
||||
The following code snippet defines a reusable animation named `slideInAnimation`.
|
||||
|
||||
|
||||
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="route-animations" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/animations.ts" linenums="false" header="src/app/animations.ts" region="route-animations" language="typescript">
|
||||
</code-example>
|
||||
|
||||
The animation definition does several things:
|
||||
|
||||
@ -93,13 +97,15 @@ A route change activates the animation trigger, and a transition matching the st
|
||||
|
||||
Make the animation definition available in your application by adding the reusable animation (`slideInAnimation`) to the `animations` metadata of the `AppComponent`.
|
||||
|
||||
<code-example path="animations/src/app/app.component.ts" header="src/app/app.component.ts" region="define" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/app.component.ts" linenums="false" header="src/app/app.component.ts" region="define" language="typescript">
|
||||
</code-example>
|
||||
|
||||
### Styling the host and child components
|
||||
|
||||
During a transition, a new view is inserted directly after the old one and both elements appear on screen at the same time. To prevent this, apply additional styling to the host view, and to the removed and inserted child views. The host view must use relative positioning, and the child views must use absolute positioning. Adding styling to the views animates the containers in place, without the DOM moving things around.
|
||||
|
||||
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="style-view" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/animations.ts" linenums="false" header="src/app/animations.ts" region="style-view" language="typescript">
|
||||
</code-example>
|
||||
|
||||
### Querying the view containers
|
||||
|
||||
@ -107,7 +113,8 @@ Use the `query()` method to find and animate elements within the current host co
|
||||
|
||||
Let's assume that we are routing from the *Home => About*.
|
||||
|
||||
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="query" language="typescript"></code-example>
|
||||
<code-example path="animations/src/app/animations.ts" linenums="false" header="src/app/animations.ts" region="query" language="typescript" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The animation code does the following after styling the views:
|
||||
|
||||
|
@ -53,7 +53,9 @@ If the `app` folder is the application root, as it is for the sample application
|
||||
set the `href` value *exactly* as shown here.
|
||||
|
||||
|
||||
<code-example path="router/src/index.html" header="src/index.html (base-href)" region="base-href"></code-example>
|
||||
<code-example path="router/src/index.html" linenums="false" header="src/index.html (base-href)" region="base-href">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -67,7 +69,9 @@ It is not part of the Angular core. It is in its own library package, `@angular/
|
||||
Import what you need from it as you would from any other Angular package.
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.module.1.ts" header="src/app/app.module.ts (import)" region="import-router"></code-example>
|
||||
<code-example path="router/src/app/app.module.1.ts" linenums="false" header="src/app/app.module.ts (import)" region="import-router">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -96,7 +100,9 @@ The following example creates five route definitions, configures the router via
|
||||
and adds the result to the `AppModule`'s `imports` array.
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.module.0.ts" header="src/app/app.module.ts (excerpt)"></code-example>
|
||||
<code-example path="router/src/app/app.module.0.ts" linenums="false" header="src/app/app.module.ts (excerpt)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -171,7 +177,9 @@ an anchor tag.
|
||||
Consider the following template:
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.component.1.html" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="router/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html">
|
||||
|
||||
</code-example>
|
||||
|
||||
The `RouterLink` directives on the anchor tags give the router control over those elements.
|
||||
The navigation paths are fixed, so you can assign a string to the `routerLink` (a "one-time" binding).
|
||||
@ -909,7 +917,9 @@ In order to use the Router, you must first register the `RouterModule` from the
|
||||
</div>
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.module.1.ts" header="src/app/app.module.ts (first-config)" region="first-config"></code-example>
|
||||
<code-example path="router/src/app/app.module.1.ts" linenums="false" header="src/app/app.module.ts (first-config)" region="first-config">
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -937,7 +947,9 @@ The router outlet serves as a placeholder when the routed components will be ren
|
||||
|
||||
The corresponding component template looks like this:
|
||||
|
||||
<code-example path="router/src/app/app.component.1.html" header="src/app/app.component.html"></code-example>
|
||||
<code-example path="router/src/app/app.component.1.html" linenums="false" header="src/app/app.component.html">
|
||||
|
||||
</code-example>
|
||||
|
||||
{@a wildcard}
|
||||
|
||||
@ -961,13 +973,17 @@ Be sure it is the _last_ route in the configuration.
|
||||
|
||||
To test this feature, add a button with a `RouterLink` to the `HeroListComponent` template and set the link to `"/sidekicks"`.
|
||||
|
||||
<code-example path="router/src/app/hero-list/hero-list.component.1.html" header="src/app/hero-list/hero-list.component.html (excerpt)"></code-example>
|
||||
<code-example path="router/src/app/hero-list/hero-list.component.1.html" linenums="false" header="src/app/hero-list/hero-list.component.html (excerpt)">
|
||||
|
||||
</code-example>
|
||||
|
||||
The application will fail if the user clicks that button because you haven't defined a `"/sidekicks"` route yet.
|
||||
|
||||
Instead of adding the `"/sidekicks"` route, define a `wildcard` route instead and have it navigate to a simple `PageNotFoundComponent`.
|
||||
|
||||
<code-example path="router/src/app/app.module.1.ts" header="src/app/app.module.ts (wildcard)" region="wildcard"></code-example>
|
||||
<code-example path="router/src/app/app.module.1.ts" linenums="false" header="src/app/app.module.ts (wildcard)" region="wildcard">
|
||||
|
||||
</code-example>
|
||||
|
||||
Create the `PageNotFoundComponent` to display when users visit invalid URLs.
|
||||
|
||||
@ -975,7 +991,9 @@ Create the `PageNotFoundComponent` to display when users visit invalid URLs.
|
||||
ng generate component page-not-found
|
||||
</code-example>
|
||||
|
||||
<code-example path="router/src/app/page-not-found/page-not-found.component.html" header="src/app/page-not-found.component.html (404 component)"></code-example>
|
||||
<code-example path="router/src/app/page-not-found/page-not-found.component.html" linenums="false" header="src/app/page-not-found.component.html (404 component)">
|
||||
|
||||
</code-example>
|
||||
|
||||
Now when the user visits `/sidekicks`, or any other invalid URL, the browser displays "Page not found".
|
||||
The browser address bar continues to point to the invalid URL.
|
||||
@ -1004,7 +1022,8 @@ Add the default route somewhere _above_ the wildcard route.
|
||||
It's just above the wildcard route in the following excerpt showing the complete `appRoutes` for this milestone.
|
||||
|
||||
|
||||
<code-example path="router/src/app/app-routing.module.1.ts" header="src/app/app-routing.module.ts (appRoutes)" region="appRoutes"></code-example>
|
||||
<code-example path="router/src/app/app-routing.module.1.ts" linenums="false" header="src/app/app-routing.module.ts (appRoutes)" region="appRoutes">
|
||||
</code-example>
|
||||
|
||||
|
||||
A redirect route requires a `pathMatch` property to tell the router how to match a URL to the path of a route.
|
||||
@ -1295,12 +1314,16 @@ By re-exporting the `RouterModule` here the components declared in `AppModule` w
|
||||
|
||||
After these steps, the file should look like this.
|
||||
|
||||
<code-example path="router/src/app/app-routing.module.1.ts" header="src/app/app-routing.module.ts"></code-example>
|
||||
<code-example path="router/src/app/app-routing.module.1.ts" header="src/app/app-routing.module.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
Next, update the `app.module.ts` file, removing `RouterModule.forRoot` in
|
||||
the `imports` array.
|
||||
|
||||
<code-example path="router/src/app/app.module.2.ts" header="src/app/app.module.ts"></code-example>
|
||||
<code-example path="router/src/app/app.module.2.ts" header="src/app/app.module.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -1419,7 +1442,9 @@ Next, you'll update the `HeroesModule` metadata.
|
||||
|
||||
* Import and add the `HeroDetailComponent` and `HeroListComponent` to the `declarations` array in the `HeroesModule`.
|
||||
|
||||
<code-example path="router/src/app/heroes/heroes.module.ts" header="src/app/heroes/heroes.module.ts"></code-example>
|
||||
<code-example path="router/src/app/heroes/heroes.module.ts" header="src/app/heroes/heroes.module.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -1543,7 +1568,9 @@ In any other module, you must call the **`RouterModule.forChild`** method to reg
|
||||
The updated `HeroesRoutingModule` looks like this:
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" header="src/app/heroes/heroes-routing.module.ts"></code-example>
|
||||
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" header="src/app/heroes/heroes-routing.module.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -1575,7 +1602,9 @@ Remove the `HeroListComponent` import and the `/heroes` route from the `app-rout
|
||||
These are concerns at the top level of the application itself.
|
||||
|
||||
|
||||
<code-example path="router/src/app/app-routing.module.2.ts" header="src/app/app-routing.module.ts (v2)"></code-example>
|
||||
<code-example path="router/src/app/app-routing.module.2.ts" linenums="false" header="src/app/app-routing.module.ts (v2)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -1589,7 +1618,9 @@ Remove the `HeroListComponent` from the `AppModule`'s `declarations` because it'
|
||||
After these steps, the `AppModule` should look like this:
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.module.3.ts" header="src/app/app.module.ts"></code-example>
|
||||
<code-example path="router/src/app/app.module.3.ts" header="src/app/app.module.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -1601,7 +1632,9 @@ After these steps, the `AppModule` should look like this:
|
||||
Look at the module `imports` array. Notice that the `AppRoutingModule` is _last_.
|
||||
Most importantly, it comes _after_ the `HeroesModule`.
|
||||
|
||||
<code-example path="router/src/app/app.module.3.ts" region="module-imports" header="src/app/app.module.ts (module-imports)"></code-example>
|
||||
<code-example path="router/src/app/app.module.3.ts" region="module-imports" header="src/app/app.module.ts (module-imports)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -1645,7 +1678,9 @@ Return to the `HeroesRoutingModule` and look at the route definitions again.
|
||||
The route to `HeroDetailComponent` has a twist.
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" header="src/app/heroes/heroes-routing.module.ts (excerpt)" region="hero-detail-route"></code-example>
|
||||
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" linenums="false" header="src/app/heroes/heroes-routing.module.ts (excerpt)" region="hero-detail-route">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -1699,7 +1734,9 @@ Accordingly, the _link parameters array_ has *two* items: the routing _path_ an
|
||||
`id` of the selected hero.
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" linenums="false" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -1728,14 +1765,18 @@ the `HeroDetailComponent` via the `ActivatedRoute` service.
|
||||
Import the `Router`, `ActivatedRoute`, and `ParamMap` tokens from the router package.
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (activated route)" region="imports"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (activated route)" region="imports">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Import the `switchMap` operator because you need it later to process the `Observable` route parameters.
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (switchMap operator import)" region="rxjs-operator-import"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (switchMap operator import)" region="rxjs-operator-import">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -1746,13 +1787,17 @@ As usual, you write a constructor that asks Angular to inject services
|
||||
that the component requires and reference them as private variables.
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (constructor)" region="ctor"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (constructor)" region="ctor">
|
||||
|
||||
</code-example>
|
||||
|
||||
Later, in the `ngOnInit` method, you use the `ActivatedRoute` service to retrieve the parameters for the route,
|
||||
pull the hero `id` from the parameters and retrieve the hero to display.
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit)" region="ngOnInit"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit)" region="ngOnInit">
|
||||
|
||||
</code-example>
|
||||
|
||||
The `paramMap` processing is a bit tricky. When the map changes, you `get()`
|
||||
the `id` parameter from the changed parameters.
|
||||
@ -1889,7 +1934,9 @@ You can access the parameters directly without subscribing or adding observable
|
||||
It's much simpler to write and read:
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.2.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit snapshot)" region="snapshot"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.2.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (ngOnInit snapshot)" region="snapshot">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -1920,7 +1967,9 @@ that you can bind to a `[routerLink]` directive.
|
||||
It holds the _path to the `HeroListComponent`_:
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (excerpt)" region="gotoHeroes"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (excerpt)" region="gotoHeroes">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
{@a optional-route-parameters}
|
||||
@ -1980,7 +2029,9 @@ When navigating to the `HeroDetailComponent` you specified the _required_ `id` o
|
||||
*route parameter* and made it the second item of the [_link parameters array_](#link-parameters-array).
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" linenums="false" header="src/app/heroes/hero-list/hero-list.component.html (link-parameters-array)" region="link-parameters-array">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -1988,7 +2039,9 @@ The router embedded the `id` value in the navigation URL because you had defined
|
||||
as a route parameter with an `:id` placeholder token in the route `path`:
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" header="src/app/heroes/heroes-routing.module.ts (hero-detail-route)" region="hero-detail-route"></code-example>
|
||||
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" linenums="false" header="src/app/heroes/heroes-routing.module.ts (hero-detail-route)" region="hero-detail-route">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -1996,7 +2049,9 @@ When the user clicks the back button, the `HeroDetailComponent` constructs anoth
|
||||
which it uses to navigate back to the `HeroListComponent`.
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (gotoHeroes)" region="gotoHeroes"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.1.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (gotoHeroes)" region="gotoHeroes">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -2011,7 +2066,9 @@ For demonstration purposes, there's an extra junk parameter (`foo`) in the objec
|
||||
Here's the revised navigation statement:
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" header="src/app/heroes/hero-detail/hero-detail.component.ts (go to heroes)" region="gotoHeroes"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.3.ts" linenums="false" header="src/app/heroes/hero-detail/hero-detail.component.ts (go to heroes)" region="gotoHeroes">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -2091,21 +2148,27 @@ This time you'll be navigating in the opposite direction, from the `HeroDetailCo
|
||||
First you extend the router import statement to include the `ActivatedRoute` service symbol:
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" header="src/app/heroes/hero-list/hero-list.component.ts (import)" region="import-router"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" linenums="false" header="src/app/heroes/hero-list/hero-list.component.ts (import)" region="import-router">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Import the `switchMap` operator to perform an operation on the `Observable` of route parameter map.
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" header="src/app/heroes/hero-list/hero-list.component.ts (rxjs imports)" region="rxjs-imports"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" linenums="false" header="src/app/heroes/hero-list/hero-list.component.ts (rxjs imports)" region="rxjs-imports">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Then you inject the `ActivatedRoute` in the `HeroListComponent` constructor.
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" header="src/app/heroes/hero-list/hero-list.component.ts (constructor and ngOnInit)" region="ctor"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.ts" linenums="false" header="src/app/heroes/hero-list/hero-list.component.ts (constructor and ngOnInit)" region="ctor">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -2118,11 +2181,15 @@ The binding adds the `selected` CSS class when the comparison returns `true` and
|
||||
Look for it within the repeated `<li>` tag as shown here:
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.html" header="src/app/heroes/hero-list/hero-list.component.html"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.html" linenums="false" header="src/app/heroes/hero-list/hero-list.component.html">
|
||||
|
||||
</code-example>
|
||||
|
||||
Add some styles to apply when the list item is selected.
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.css" region="selected" header="src/app/heroes/hero-list/hero-list.component.css"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.css" linenums="false" region="selected" header="src/app/heroes/hero-list/hero-list.component.css">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -2148,16 +2215,22 @@ This section shows you how to add some [animations](guide/animations) to the `He
|
||||
|
||||
First import the `BrowserAnimationsModule` and add it to the `imports` array:
|
||||
|
||||
<code-example path="router/src/app/app.module.ts" header="src/app/app.module.ts (animations-module)" region="animations-module"></code-example>
|
||||
<code-example path="router/src/app/app.module.ts" linenums="false" header="src/app/app.module.ts (animations-module)" region="animations-module">
|
||||
|
||||
</code-example>
|
||||
|
||||
Next, add a `data` object to the routes for `HeroListComponent` and `HeroDetailComponent`. Transitions are based on `states` and you'll use the `animation` data from the route to provide a named animation `state` for the transitions.
|
||||
|
||||
<code-example path="router/src/app/heroes/heroes-routing.module.2.ts" header="src/app/heroes/heroes-routing.module.ts (animation data)"></code-example>
|
||||
<code-example path="router/src/app/heroes/heroes-routing.module.2.ts" header="src/app/heroes/heroes-routing.module.ts (animation data)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
Create an `animations.ts` file in the root `src/app/` folder. The contents look like this:
|
||||
|
||||
<code-example path="router/src/app/animations.ts" header="src/app/animations.ts (excerpt)"></code-example>
|
||||
<code-example path="router/src/app/animations.ts" linenums="false" header="src/app/animations.ts (excerpt)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
This file does the following:
|
||||
@ -2175,18 +2248,24 @@ Back in the `AppComponent`, import the `RouterOutlet` token from the `@angular/r
|
||||
|
||||
Add an `animations` array to the `@Component` metadata's that contains the `slideInAnimation`.
|
||||
|
||||
<code-example path="router/src/app/app.component.2.ts" header="src/app/app.component.ts (animations)" region="animation-imports"></code-example>
|
||||
<code-example path="router/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (animations)" region="animation-imports">
|
||||
|
||||
</code-example>
|
||||
|
||||
In order to use the routable animations, you'll need to wrap the `RouterOutlet` inside an element. You'll
|
||||
use the `@routeAnimation` trigger and bind it to the element.
|
||||
|
||||
For the `@routeAnimation` transitions to key off states, you'll need to provide it with the `data` from the `ActivatedRoute`. The `RouterOutlet` is exposed as an `outlet` template variable, so you bind a reference to the router outlet. A variable of `routerOutlet` is an ideal choice.
|
||||
|
||||
<code-example path="router/src/app/app.component.2.html" header="src/app/app.component.html (router outlet)"></code-example>
|
||||
<code-example path="router/src/app/app.component.2.html" linenums="false" header="src/app/app.component.html (router outlet)">
|
||||
|
||||
</code-example>
|
||||
|
||||
The `@routeAnimation` property is bound to the `getAnimationData` with the provided `routerOutlet` reference, so you'll need to define that function in the `AppComponent`. The `getAnimationData` function returns the animation property from the `data` provided through the `ActivatedRoute`. The `animation` property matches the `transition` names you used in the `slideInAnimation` defined in `animations.ts`.
|
||||
|
||||
<code-example path="router/src/app/app.component.2.ts" header="src/app/app.component.ts (router outlet)" region="function-binding"></code-example>
|
||||
<code-example path="router/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (router outlet)" region="function-binding">
|
||||
|
||||
</code-example>
|
||||
|
||||
When switching between the two routes, the `HeroDetailComponent` and `HeroListComponent` will ease in from the left when routed to and will slide to the right when navigating away.
|
||||
|
||||
@ -2484,7 +2563,9 @@ Begin by imitating the heroes feature:
|
||||
You'll use mock crises instead of mock heroes:
|
||||
|
||||
|
||||
<code-example path="router/src/app/crisis-center/mock-crises.ts" header="src/app/crisis-center/mock-crises.ts"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/mock-crises.ts" header="src/app/crisis-center/mock-crises.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
The resulting crisis center is a foundation for introducing a new concept—**child routing**.
|
||||
@ -2541,7 +2622,8 @@ Generate a `CrisisCenter` component in the `crisis-center` folder:
|
||||
|
||||
Update the component template to look like this:
|
||||
|
||||
<code-example path="router/src/app/crisis-center/crisis-center/crisis-center.component.html" header="src/app/crisis-center/crisis-center/crisis-center.component.html"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/crisis-center/crisis-center.component.html" linenums="false" header="src/app/crisis-center/crisis-center/crisis-center.component.html">
|
||||
</code-example>
|
||||
|
||||
The `CrisisCenterComponent` has the following in common with the `AppComponent`:
|
||||
|
||||
@ -2568,12 +2650,14 @@ As a host page for the "Crisis Center" feature, generate a `CrisisCenterHome` co
|
||||
|
||||
Update the template with a welcome message to the `Crisis Center`.
|
||||
|
||||
<code-example path="router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.html" header="src/app/crisis-center/crisis-center-home/crisis-center-home.component.html"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/crisis-center-home/crisis-center-home.component.html" linenums="false" header="src/app/crisis-center/crisis-center-home/crisis-center-home.component.html">
|
||||
</code-example>
|
||||
|
||||
Update the `crisis-center-routing.module.ts` you renamed after copying it from `heroes-routing.module.ts` file.
|
||||
This time, you define **child routes** *within* the parent `crisis-center` route.
|
||||
|
||||
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.1.ts" header="src/app/crisis-center/crisis-center-routing.module.ts (Routes)" region="routes"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.1.ts" linenums="false" header="src/app/crisis-center/crisis-center-routing.module.ts (Routes)" region="routes">
|
||||
</code-example>
|
||||
|
||||
|
||||
Notice that the parent `crisis-center` route has a `children` property
|
||||
@ -2619,7 +2703,9 @@ The absolute URL for the latter example, including the `localhost` origin, is
|
||||
Here's the complete `crisis-center-routing.module.ts` file with its imports.
|
||||
|
||||
|
||||
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.1.ts" header="src/app/crisis-center/crisis-center-routing.module.ts (excerpt)"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.1.ts" linenums="false" header="src/app/crisis-center/crisis-center-routing.module.ts (excerpt)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -2637,7 +2723,7 @@ _before_ the `AppRoutingModule`:
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane path="router/src/app/app.module.4.ts" header="src/app/app.module.ts (import CrisisCenterModule)" region="crisis-center-module">
|
||||
<code-pane path="router/src/app/app.module.4.ts" linenums="false" header="src/app/app.module.ts (import CrisisCenterModule)" region="crisis-center-module">
|
||||
|
||||
</code-pane>
|
||||
|
||||
@ -2649,7 +2735,9 @@ The feature routes are now provided by the `HeroesModule` and the `CrisisCenter`
|
||||
The `app-routing.module.ts` file retains the top-level application routes such as the default and wildcard routes.
|
||||
|
||||
|
||||
<code-example path="router/src/app/app-routing.module.3.ts" header="src/app/app-routing.module.ts (v3)" region="v3"></code-example>
|
||||
<code-example path="router/src/app/app-routing.module.3.ts" linenums="false" header="src/app/app-routing.module.ts (v3)" region="v3">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -2726,7 +2814,9 @@ The `ActivatedRoute` is implicit in a `RouterLink` directive.
|
||||
Update the `gotoCrises` method of the `CrisisDetailComponent` to navigate back to the *Crisis Center* list using relative path navigation.
|
||||
|
||||
|
||||
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (relative navigation)" region="gotoCrises-navigate"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" linenums="false" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (relative navigation)" region="gotoCrises-navigate">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
Notice that the path goes up a level using the `../` syntax.
|
||||
@ -2757,7 +2847,9 @@ Multiple outlets can be displaying different content, determined by different ro
|
||||
Add an outlet named "popup" in the `AppComponent`, directly below the unnamed outlet.
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.component.4.html" header="src/app/app.component.html (outlets)" region="outlets"></code-example>
|
||||
<code-example path="router/src/app/app.component.4.html" linenums="false" header="src/app/app.component.html (outlets)" region="outlets">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -2831,7 +2923,9 @@ That's a peculiarity covered [below](#clear-secondary-routes).
|
||||
|
||||
Open the `AppRoutingModule` and add a new `compose` route to the `appRoutes`.
|
||||
|
||||
<code-example path="router/src/app/app-routing.module.3.ts" header="src/app/app-routing.module.ts (compose route)" region="compose"></code-example>
|
||||
<code-example path="router/src/app/app-routing.module.3.ts" linenums="false" header="src/app/app-routing.module.ts (compose route)" region="compose">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -2842,7 +2936,9 @@ This route now targets the popup outlet and the `ComposeMessageComponent` will d
|
||||
The user needs a way to open the popup.
|
||||
Open the `AppComponent` and add a "Contact" link.
|
||||
|
||||
<code-example path="router/src/app/app.component.4.html" header="src/app/app.component.html (contact-link)" region="contact-link"></code-example>
|
||||
<code-example path="router/src/app/app.component.4.html" linenums="false" header="src/app/app.component.html (contact-link)" region="contact-link">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -2935,7 +3031,9 @@ That's why the popup stays visible as you navigate among the crises and heroes.
|
||||
Clicking the "send" or "cancel" buttons _does_ clear the popup view.
|
||||
To see how, look at the `closePopup()` method again:
|
||||
|
||||
<code-example path="router/src/app/compose-message/compose-message.component.ts" header="src/app/compose-message/compose-message.component.ts (closePopup)" region="closePopup"></code-example>
|
||||
<code-example path="router/src/app/compose-message/compose-message.component.ts" linenums="false" header="src/app/compose-message/compose-message.component.ts (closePopup)" region="closePopup">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3168,11 +3266,11 @@ feature module, a dashboard route and two unfinished components to manage crises
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane header="src/app/admin/admin/admin.component.html" path="router/src/app/admin/admin/admin.component.html">
|
||||
<code-pane header="src/app/admin/admin/admin.component.html" linenums="false" path="router/src/app/admin/admin/admin.component.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane header="src/app/admin/admin-dashboard/admin-dashboard.component.html" path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.html">
|
||||
<code-pane header="src/app/admin/admin-dashboard/admin-dashboard.component.html" linenums="false" path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
@ -3180,11 +3278,11 @@ feature module, a dashboard route and two unfinished components to manage crises
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane header="src/app/admin/manage-crises/manage-crises.component.html" path="router/src/app/admin/manage-crises/manage-crises.component.html">
|
||||
<code-pane header="src/app/admin/manage-crises/manage-crises.component.html" linenums="false" path="router/src/app/admin/manage-crises/manage-crises.component.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane header="src/app/admin/manage-heroes/manage-heroes.component.html" path="router/src/app/admin/manage-heroes/manage-heroes.component.html">
|
||||
<code-pane header="src/app/admin/manage-heroes/manage-heroes.component.html" linenums="false" path="router/src/app/admin/manage-heroes/manage-heroes.component.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
@ -3211,7 +3309,9 @@ is considered a match to any route within the admin feature area. You only want
|
||||
The initial admin routing configuration:
|
||||
|
||||
|
||||
<code-example path="router/src/app/admin/admin-routing.module.1.ts" header="src/app/admin/admin-routing.module.ts (admin routing)" region="admin-routes"></code-example>
|
||||
<code-example path="router/src/app/admin/admin-routing.module.1.ts" linenums="false" header="src/app/admin/admin-routing.module.ts (admin routing)" region="admin-routes">
|
||||
|
||||
</code-example>
|
||||
|
||||
Looking at the child route under the `AdminComponent`, there is a `path` and a `children`
|
||||
property but it's not using a `component`.
|
||||
@ -3227,14 +3327,18 @@ Next, import the `AdminModule` into `app.module.ts` and add it to the `imports`
|
||||
to register the admin routes.
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.module.4.ts" header="src/app/app.module.ts (admin module)" region="admin-module"></code-example>
|
||||
<code-example path="router/src/app/app.module.4.ts" linenums="false" header="src/app/app.module.ts (admin module)" region="admin-module">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Add an "Admin" link to the `AppComponent` shell so that users can get to this feature.
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.component.5.html" header="src/app/app.component.html (template)"></code-example>
|
||||
<code-example path="router/src/app/app.component.5.html" linenums="false" header="src/app/app.component.html (template)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3263,7 +3367,9 @@ At the moment you're interested in seeing how guards work so the first version d
|
||||
It simply logs to console and `returns` true immediately, allowing navigation to proceed:
|
||||
|
||||
|
||||
<code-example path="router/src/app/auth/auth.guard.1.ts" header="src/app/auth/auth.guard.ts (excerpt)"></code-example>
|
||||
<code-example path="router/src/app/auth/auth.guard.1.ts" linenums="false" header="src/app/auth/auth.guard.ts (excerpt)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3271,7 +3377,9 @@ Next, open `admin-routing.module.ts `, import the `AuthGuard` class, and
|
||||
update the admin route with a `canActivate` guard property that references it:
|
||||
|
||||
|
||||
<code-example path="router/src/app/admin/admin-routing.module.2.ts" header="src/app/admin/admin-routing.module.ts (guarded admin route)" region="admin-route"></code-example>
|
||||
<code-example path="router/src/app/admin/admin-routing.module.2.ts" linenums="false" header="src/app/admin/admin-routing.module.ts (guarded admin route)" region="admin-route">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3293,7 +3401,9 @@ The `AuthGuard` should call an application service that can login a user and ret
|
||||
|
||||
Update the `AuthService` to log in the user:
|
||||
|
||||
<code-example path="router/src/app/auth/auth.service.ts" header="src/app/auth/auth.service.ts (excerpt)"></code-example>
|
||||
<code-example path="router/src/app/auth/auth.service.ts" linenums="false" header="src/app/auth/auth.service.ts (excerpt)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3306,7 +3416,9 @@ The `redirectUrl` property will store the attempted URL so you can navigate to i
|
||||
Revise the `AuthGuard` to call it.
|
||||
|
||||
|
||||
<code-example path="router/src/app/auth/auth.guard.2.ts" header="src/app/auth/auth.guard.ts (v2)"></code-example>
|
||||
<code-example path="router/src/app/auth/auth.guard.2.ts" linenums="false" header="src/app/auth/auth.guard.ts (v2)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3384,7 +3496,9 @@ async checks and a `boolean` for sync checks.
|
||||
This one returns a `boolean`:
|
||||
|
||||
|
||||
<code-example path="router/src/app/auth/auth.guard.3.ts" header="src/app/auth/auth.guard.ts (excerpt)" region="can-activate-child"></code-example>
|
||||
<code-example path="router/src/app/auth/auth.guard.3.ts" linenums="false" header="src/app/auth/auth.guard.ts (excerpt)" region="can-activate-child">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3392,7 +3506,9 @@ Add the same `AuthGuard` to the `component-less` admin route to protect all othe
|
||||
instead of adding the `AuthGuard` to each route individually.
|
||||
|
||||
|
||||
<code-example path="router/src/app/admin/admin-routing.module.3.ts" header="src/app/admin/admin-routing.module.ts (excerpt)" region="can-activate-child"></code-example>
|
||||
<code-example path="router/src/app/admin/admin-routing.module.3.ts" linenums="false" header="src/app/admin/admin-routing.module.ts (excerpt)" region="can-activate-child">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3441,7 +3557,9 @@ discards the changes when the user presses the *Cancel* button.
|
||||
Both buttons navigate back to the crisis list after save or cancel.
|
||||
|
||||
|
||||
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (cancel and save methods)" region="cancel-save"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" linenums="false" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (cancel and save methods)" region="cancel-save">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3475,7 +3593,9 @@ Generate a `Dialog` service to handle user confirmation.
|
||||
|
||||
Add a `confirm()` method to the `DialogService` to prompt the user to confirm their intent. The `window.confirm` is a _blocking_ action that displays a modal dialog and waits for user interaction.
|
||||
|
||||
<code-example path="router/src/app/dialog.service.ts" header="src/app/dialog.service.ts"></code-example>
|
||||
<code-example path="router/src/app/dialog.service.ts" header="src/app/dialog.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
It returns an `Observable` that *resolves* when the user eventually decides what to do: either
|
||||
to discard changes and navigate away (`true`) or to preserve the pending changes and stay in the crisis editor (`false`).
|
||||
@ -3497,7 +3617,9 @@ It need only detect that the component has a `canDeactivate()` method and call i
|
||||
This approach makes the guard reusable.
|
||||
|
||||
|
||||
<code-example path="router/src/app/can-deactivate.guard.ts" header="src/app/can-deactivate.guard.ts"></code-example>
|
||||
<code-example path="router/src/app/can-deactivate.guard.ts" header="src/app/can-deactivate.guard.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3510,14 +3632,18 @@ wanted to use this guard for this component and needed to get
|
||||
the component's properties or confirm whether the router should allow navigation away from it.
|
||||
|
||||
|
||||
<code-example path="router/src/app/can-deactivate.guard.1.ts" header="src/app/can-deactivate.guard.ts (component-specific)"></code-example>
|
||||
<code-example path="router/src/app/can-deactivate.guard.1.ts" linenums="false" header="src/app/can-deactivate.guard.ts (component-specific)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Looking back at the `CrisisDetailComponent`, it implements the confirmation workflow for unsaved changes.
|
||||
|
||||
|
||||
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (excerpt)" region="canDeactivate"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" linenums="false" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (excerpt)" region="canDeactivate">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3530,7 +3656,9 @@ to resolve to truthy (navigate) or falsy (stay put).
|
||||
Add the `Guard` to the crisis detail route in `crisis-center-routing.module.ts` using the `canDeactivate` array property.
|
||||
|
||||
|
||||
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.3.ts" header="src/app/crisis-center/crisis-center-routing.module.ts (can deactivate guard)"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.3.ts" linenums="false" header="src/app/crisis-center/crisis-center-routing.module.ts (can deactivate guard)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
Now you have given the user a safeguard against unsaved changes.
|
||||
@ -3576,7 +3704,9 @@ Generate a `CrisisDetailResolver` service file within the `Crisis Center` featur
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="router/src/app/crisis-center/crisis-detail-resolver.service.1.ts" header="src/app/crisis-center/crisis-detail-resolver.service.ts (generated)"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/crisis-detail-resolver.service.1.ts" header="src/app/crisis-center/crisis-detail-resolver.service.ts (generated)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3598,13 +3728,17 @@ Observable completes after retrieving the first value from the Observable return
|
||||
|
||||
If it doesn't return a valid `Crisis`, return an empty `Observable`, canceling the previous in-flight navigation to the `CrisisDetailComponent` and navigate the user back to the `CrisisListComponent`. The update resolver service looks like this:
|
||||
|
||||
<code-example path="router/src/app/crisis-center/crisis-detail-resolver.service.ts" header="src/app/crisis-center/crisis-detail-resolver.service.ts"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/crisis-detail-resolver.service.ts" header="src/app/crisis-center/crisis-detail-resolver.service.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
Import this resolver in the `crisis-center-routing.module.ts`
|
||||
and add a `resolve` object to the `CrisisDetailComponent` route configuration.
|
||||
|
||||
|
||||
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.4.ts" header="src/app/crisis-center/crisis-center-routing.module.ts (resolver)"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/crisis-center-routing.module.4.ts" linenums="false" header="src/app/crisis-center/crisis-center-routing.module.ts (resolver)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3614,7 +3748,9 @@ that's where you said it should be when you re-configured the route.
|
||||
It will be there when the `CrisisDetailComponent` ask for it.
|
||||
|
||||
|
||||
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (ngOnInit v2)" region="ngOnInit"></code-example>
|
||||
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" linenums="false" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (ngOnInit v2)" region="ngOnInit">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3717,7 +3853,9 @@ Add an `anchor` element so you can jump to a certain point on the page.
|
||||
Add the `NavigationExtras` object to the `router.navigate()` method that navigates you to the `/login` route.
|
||||
|
||||
|
||||
<code-example path="router/src/app/auth/auth.guard.4.ts" header="src/app/auth/auth.guard.ts (v3)"></code-example>
|
||||
<code-example path="router/src/app/auth/auth.guard.4.ts" linenums="false" header="src/app/auth/auth.guard.ts (v3)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3728,7 +3866,9 @@ and provide the `queryParamsHandling` and `preserveFragment` to pass along the c
|
||||
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>
|
||||
<code-example path="router/src/app/auth/login/login.component.ts" linenums="false" header="src/app/auth/login/login.component.ts (preserve)" region="preserve">
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -3745,7 +3885,9 @@ As you'll be navigating to the *Admin Dashboard* route after logging in, you'll
|
||||
query parameters and fragment.
|
||||
|
||||
|
||||
<code-example path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (v2)"></code-example>
|
||||
<code-example path="router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts" linenums="false" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (v2)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3817,7 +3959,9 @@ The `loadChildren` property takes a function that returns a promise using the br
|
||||
The path is the location of the `AdminModule` (relative to the app root).
|
||||
After the code is requested and loaded, the `Promise` resolves an object that contains the `NgModule`, in this case the `AdminModule`.
|
||||
|
||||
<code-example path="router/src/app/app-routing.module.5.ts" region="admin-1" header="app-routing.module.ts (load children)"></code-example>
|
||||
<code-example path="router/src/app/app-routing.module.5.ts" region="admin-1" header="app-routing.module.ts (load children)">
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
@ -3876,7 +4020,9 @@ Add it to the `AuthGuard` class's `implements` list.
|
||||
Then implement `canLoad()` as follows:
|
||||
|
||||
|
||||
<code-example path="router/src/app/auth/auth.guard.ts" header="src/app/auth/auth.guard.ts (CanLoad guard)" region="canLoad"></code-example>
|
||||
<code-example path="router/src/app/auth/auth.guard.ts" linenums="false" header="src/app/auth/auth.guard.ts (CanLoad guard)" region="canLoad">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3888,7 +4034,9 @@ array property for the `admin` route.
|
||||
The completed admin route looks like this:
|
||||
|
||||
|
||||
<code-example path="router/src/app/app-routing.module.5.ts" region="admin" header="app-routing.module.ts (lazy admin route)"></code-example>
|
||||
<code-example path="router/src/app/app-routing.module.5.ts" region="admin" header="app-routing.module.ts (lazy admin route)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -3987,7 +4135,9 @@ The second argument in the `RouterModule.forRoot()` method takes an object for a
|
||||
The `preloadingStrategy` is one of those options.
|
||||
Add the `PreloadAllModules` token to the `forRoot()` call:
|
||||
|
||||
<code-example path="router/src/app/app-routing.module.6.ts" header="src/app/app-routing.module.ts (preload all)" region="forRoot"></code-example>
|
||||
<code-example path="router/src/app/app-routing.module.6.ts" linenums="false" header="src/app/app-routing.module.ts (preload all)" region="forRoot">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -4032,7 +4182,9 @@ Recall that you can add anything to the `data` property of a route.
|
||||
Set the `data.preload` flag in the `crisis-center` route in the `AppRoutingModule`.
|
||||
|
||||
|
||||
<code-example path="router/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts (route data preload)" region="preload-v2"></code-example>
|
||||
<code-example path="router/src/app/app-routing.module.ts" linenums="false" header="src/app/app-routing.module.ts (route data preload)" region="preload-v2">
|
||||
|
||||
</code-example>
|
||||
|
||||
Generate a new `SelectivePreloadingStrategy` service.
|
||||
|
||||
@ -4041,7 +4193,9 @@ Generate a new `SelectivePreloadingStrategy` service.
|
||||
</code-example>
|
||||
|
||||
|
||||
<code-example path="router/src/app/selective-preloading-strategy.service.ts" header="src/app/selective-preloading-strategy.service.ts (excerpt)"></code-example>
|
||||
<code-example path="router/src/app/selective-preloading-strategy.service.ts" linenums="false" header="src/app/selective-preloading-strategy.service.ts (excerpt)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -4079,7 +4233,9 @@ Now edit the `AdminDashboardComponent` to display the log of preloaded routes.
|
||||
When you're done it looks like this.
|
||||
|
||||
|
||||
<code-example path="router/src/app/admin/admin-dashboard/admin-dashboard.component.ts" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (preloaded modules)"></code-example>
|
||||
<code-example path="router/src/app/admin/admin-dashboard/admin-dashboard.component.ts" linenums="false" header="src/app/admin/admin-dashboard/admin-dashboard.component.ts (preloaded modules)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -4101,7 +4257,9 @@ You've setup the routes for navigating around your application. You've used navi
|
||||
|
||||
Let's take the `Hero` routes and migrate them to new URLs. The `Router` checks for redirects in your configuration before navigating, so each redirect is triggered when needed. To support this change, you'll add redirects from the old routes to the new routes in the `heroes-routing.module`.
|
||||
|
||||
<code-example path="router/src/app/heroes/heroes-routing.module.ts" header="src/app/heroes/heroes-routing.module.ts (heroes redirects)"></code-example>
|
||||
<code-example path="router/src/app/heroes/heroes-routing.module.ts" linenums="false" header="src/app/heroes/heroes-routing.module.ts (heroes redirects)">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
You'll notice two different types of redirects. The first change is from `/heroes` to `/superheroes` without any parameters. This is a straightforward redirect, unlike the change from `/hero/:id` to `/superhero/:id`, which includes the `:id` route parameter. Router redirects also use powerful pattern matching, so the `Router` inspects the URL and replaces route parameters in the `path` with their appropriate destination. Previously, you navigated to a URL such as `/hero/15` with a route parameter `id` of `15`.
|
||||
@ -4119,15 +4277,21 @@ Before updating the `app-routing.module.ts`, you'll need to consider an importan
|
||||
|
||||
So instead, you'll update the empty path route in `app-routing.module.ts` to redirect to `/superheroes`.
|
||||
|
||||
<code-example path="router/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts (superheroes redirect)"></code-example>
|
||||
<code-example path="router/src/app/app-routing.module.ts" linenums="false" header="src/app/app-routing.module.ts (superheroes redirect)">
|
||||
|
||||
</code-example>
|
||||
|
||||
`RouterLink`s aren't tied to route configuration, so you'll need to update the associated router links so they remain active when the new route is active. You'll update the `app.component.ts` template for the `/heroes` routerLink.
|
||||
|
||||
<code-example path="router/src/app/app.component.html" header="src/app/app.component.html (superheroes active routerLink)"></code-example>
|
||||
<code-example path="router/src/app/app.component.html" linenums="false" header="src/app/app.component.html (superheroes active routerLink)">
|
||||
|
||||
</code-example>
|
||||
|
||||
Update the `goToHeroes()` method in the `hero-detail.component.ts` to navigate back to `/superheroes` with the optional route parameters.
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.ts" region="redirect" header="src/app/heroes/hero-detail/hero-detail.component.ts (goToHeroes)"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-detail/hero-detail.component.ts" linenums="false" region="redirect" header="src/app/heroes/hero-detail/hero-detail.component.ts (goToHeroes)">
|
||||
|
||||
</code-example>
|
||||
|
||||
With the redirects setup, all previous routes now point to their new destinations and both URLs still function as intended.
|
||||
|
||||
@ -4148,7 +4312,9 @@ examining its `config` property.
|
||||
For example, update the `AppModule` as follows and look in the browser console window
|
||||
to see the finished route configuration.
|
||||
|
||||
<code-example path="router/src/app/app.module.7.ts" header="src/app/app.module.ts (inspect the router config)" region="inspect-config"></code-example>
|
||||
<code-example path="router/src/app/app.module.7.ts" linenums="false" header="src/app/app.module.ts (inspect the router config)" region="inspect-config">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
{@a final-app}
|
||||
@ -4187,21 +4353,27 @@ A link parameters array holds the following ingredients for router navigation:
|
||||
You can bind the `RouterLink` directive to such an array like this:
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (h-anchor)" region="h-anchor"></code-example>
|
||||
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (h-anchor)" region="h-anchor">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
You've written a two element array when specifying a route parameter like this:
|
||||
|
||||
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" header="src/app/heroes/hero-list/hero-list.component.html (nav-to-detail)" region="nav-to-detail"></code-example>
|
||||
<code-example path="router/src/app/heroes/hero-list/hero-list.component.1.html" linenums="false" header="src/app/heroes/hero-list/hero-list.component.html (nav-to-detail)" region="nav-to-detail">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
You can provide optional route parameters in an object like this:
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (cc-query-params)" region="cc-query-params"></code-example>
|
||||
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (cc-query-params)" region="cc-query-params">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -4211,7 +4383,9 @@ The moment you add a child router, such as the crisis center, you create new lin
|
||||
Recall that you specified a default child route for the crisis center so this simple `RouterLink` is fine.
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (cc-anchor-w-default)" region="cc-anchor-w-default"></code-example>
|
||||
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (cc-anchor-w-default)" region="cc-anchor-w-default">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -4227,7 +4401,9 @@ Take it a step further. Consider the following router link that
|
||||
navigates from the root of the application down to the *Dragon Crisis*:
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (Dragon-anchor)" region="Dragon-anchor"></code-example>
|
||||
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (Dragon-anchor)" region="Dragon-anchor">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -4242,7 +4418,9 @@ navigates from the root of the application down to the *Dragon Crisis*:
|
||||
If you wanted to, you could redefine the `AppComponent` template with *Crisis Center* routes exclusively:
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.component.3.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
||||
<code-example path="router/src/app/app.component.3.ts" linenums="false" header="src/app/app.component.ts (template)" region="template">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -4375,7 +4553,9 @@ If the `app` folder is the application root, as it is for this application,
|
||||
set the `href` value in **`index.html`** *exactly* as shown here.
|
||||
|
||||
|
||||
<code-example path="router/src/index.html" header="src/index.html (base-href)" region="base-href"></code-example>
|
||||
<code-example path="router/src/index.html" linenums="false" header="src/index.html (base-href)" region="base-href">
|
||||
|
||||
</code-example>
|
||||
|
||||
#### HTML5 URLs and the *<base href>*
|
||||
|
||||
@ -4388,7 +4568,9 @@ The preferred way to configure the strategy is to add a
|
||||
tag in the `<head>` of the `index.html`.
|
||||
|
||||
|
||||
<code-example path="router/src/index.html" header="src/index.html (base-href)" region="base-href"></code-example>
|
||||
<code-example path="router/src/index.html" linenums="false" header="src/index.html (base-href)" region="base-href">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
@ -4414,4 +4596,6 @@ providing the `useHash: true` in an object as the second argument of the `Router
|
||||
in the `AppModule`.
|
||||
|
||||
|
||||
<code-example path="router/src/app/app.module.6.ts" header="src/app/app.module.ts (hash URL strategy)"></code-example>
|
||||
<code-example path="router/src/app/app.module.6.ts" linenums="false" header="src/app/app.module.ts (hash URL strategy)">
|
||||
|
||||
</code-example>
|
||||
|
@ -32,7 +32,7 @@ The context also defines a *merge strategy* that determines how changes are merg
|
||||
When you create a new blank schematic with the [Schematics CLI](#cli), the generated entry function is a *rule factory*.
|
||||
A `RuleFactory`object defines a higher-order function that creates a `Rule`.
|
||||
|
||||
<code-example language="TypeScript">
|
||||
<code-example language="TypeScript" linenums="false">
|
||||
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
|
||||
|
||||
// You don't have to export the function as default.
|
||||
@ -49,7 +49,7 @@ You need a rule, for example, to define how a template in the schematic is to be
|
||||
|
||||
Rules can make use of utilities provided with the `@schematics/angular` package. Look for helper functions for working with modules, dependencies, TypeScript, AST, JSON, Angular CLI workspaces and projects, and more.
|
||||
|
||||
<code-example language="none">
|
||||
<code-example language="none" linenums="false">
|
||||
|
||||
import {
|
||||
JsonAstObject,
|
||||
@ -78,7 +78,7 @@ You can see examples of schema files for the Angular CLI command schematics in [
|
||||
Schematics come with their own command-line tool.
|
||||
Using Node 6.9 or above, install the Schematics command line tool globally:
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example language="bash" linenums="false">
|
||||
npm install -g @angular-devkit/schematics-cli
|
||||
</code-example>
|
||||
|
||||
@ -94,7 +94,7 @@ See [Schematics for Libraries](guide/schematics-for-libraries).
|
||||
|
||||
The following command creates a new schematic named `hello-world` in a new project folder of the same name.
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example language="bash" linenums="false">
|
||||
schematics blank --name=hello-world
|
||||
</code-example>
|
||||
|
||||
@ -102,7 +102,7 @@ The `blank` schematic is provided by the Schematics CLI. The command creates a n
|
||||
|
||||
Go to the collection folder, install your npm dependencies, and open your new collection in your favorite editor to see the generated files. For example, if you are using VSCode:
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example language="bash" linenums="false">
|
||||
cd hello-world
|
||||
npm install
|
||||
npm run build
|
||||
@ -118,14 +118,14 @@ Each schematic name must be unique within the collection.
|
||||
Use the `schematics` command to run a named schematic.
|
||||
Provide the path to the project folder, the schematic name, and any mandatory options, in the following format.
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example language="bash" linenums="false">
|
||||
schematics <path-to-schematics-project>:<schematics-name> --<required-option>=<value>
|
||||
</code-example>
|
||||
|
||||
The path can be absolute or relative to the current working directory where the command is executed.
|
||||
For example, to run the schematic we just generated (which has no required options), use the following command.
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example language="bash" linenums="false">
|
||||
schematics .:hello-world
|
||||
</code-example>
|
||||
|
||||
@ -133,7 +133,7 @@ schematics .:hello-world
|
||||
|
||||
To add a schematic to an existing collection, use the same command you use to start a new schematics project, but run the command inside the project folder.
|
||||
|
||||
<code-example language="bash">
|
||||
<code-example language="bash" linenums="false">
|
||||
cd hello-world
|
||||
schematics blank --name=goodbye-world
|
||||
</code-example>
|
||||
@ -147,7 +147,7 @@ The top level of the root project folder for a collection contains configuration
|
||||
The `src/` folder contains subfolders for named schematics in the collection, and a schema, `collection.json`, which describes the collected schematics.
|
||||
Each schematic is created with a name, description, and factory function.
|
||||
|
||||
<code-example language="none">
|
||||
<code-example language="none" linenums="false">
|
||||
{
|
||||
"$schema":
|
||||
"../node_modules/@angular-devkit/schematics/collection-schema.json",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user