Compare commits
210 Commits
Author | SHA1 | Date | |
---|---|---|---|
5abb9360d8 | |||
16c59aecf1 | |||
47d1216f6b | |||
5e614bfda8 | |||
efecf36eb5 | |||
b68d29791f | |||
136cd097c8 | |||
7be198a8e3 | |||
b4ee74667b | |||
67444e70ab | |||
09e55eabf3 | |||
1c838eb976 | |||
24ec9e57ad | |||
190330a612 | |||
9417086f6c | |||
535ff0564a | |||
e8c8ec7075 | |||
03d2813c20 | |||
fb5b2a89cd | |||
ea495d958f | |||
2edb87e7f8 | |||
903d28fe86 | |||
6945f7978e | |||
27afe01910 | |||
36b6110e7d | |||
24a1e146da | |||
2256920292 | |||
2a25ac2ac9 | |||
56693339c2 | |||
c976b88dcf | |||
3e08794abf | |||
b598e884f6 | |||
cf916a03d3 | |||
ee6498f37e | |||
1a0b2ff4fb | |||
dfb331cd18 | |||
11c926ce47 | |||
a0119b1144 | |||
c938fb4619 | |||
2f49a23d64 | |||
c6eaaf5b3d | |||
5e38ec8acc | |||
0cd5964f67 | |||
f3938a6a2b | |||
3baa74449c | |||
df4e97c81e | |||
d629f2c6a8 | |||
6ee47d5e76 | |||
9226b421e8 | |||
b06847f43d | |||
a54a752147 | |||
d00a2e8920 | |||
86981b395d | |||
feec963106 | |||
09fc669b4f | |||
cb339b87f3 | |||
fdcf877f83 | |||
5b5b9897c9 | |||
0e7365724e | |||
4ef1a3cd97 | |||
38b5ed05ea | |||
642c015f23 | |||
0977d95802 | |||
d374787db6 | |||
a634deb885 | |||
bcbd7ed8f0 | |||
f7de2be3f3 | |||
1e97d511c7 | |||
7e1e00c21e | |||
481f4b7412 | |||
4bc0084e5a | |||
b6864494a1 | |||
0021437ee1 | |||
d91ecd2c8b | |||
d0018e6bf6 | |||
587ca854cc | |||
8b9f03d9fa | |||
40f1f94fe0 | |||
2270467d60 | |||
8efda5b353 | |||
7b7f2d9c1b | |||
c469e25cf2 | |||
a21cde2960 | |||
9b774348b3 | |||
ce219ccfa2 | |||
53bbb01047 | |||
c6741bf36a | |||
cc06bf50f3 | |||
21e78ad022 | |||
05e855092b | |||
2817764433 | |||
145639d0f8 | |||
3eb327b67b | |||
be6af26dc1 | |||
637e81e9bb | |||
abc3cbb33f | |||
27eb8f2723 | |||
81671cea9a | |||
843fc7df9f | |||
cfe4732e41 | |||
48cabb44c7 | |||
9d0db2fe4b | |||
069146e7f3 | |||
fc895ba189 | |||
9a51528072 | |||
7fc38d4947 | |||
b1f84ec78f | |||
f351e8a5b1 | |||
ff2c342356 | |||
01b05b3f7c | |||
3b78f9eb0f | |||
f6ca619343 | |||
ab69c31fc5 | |||
38de40c9ec | |||
53352c40c3 | |||
dd3cab6eb1 | |||
ea1a3d3603 | |||
cf1b436010 | |||
cdcbb53a2d | |||
db2db6e7f5 | |||
1df6b16263 | |||
f4871212b7 | |||
2a23eca917 | |||
b5da4c6676 | |||
b8c2f3b93f | |||
75cf86e70f | |||
bf25165354 | |||
a2ba4448da | |||
ac6b2b4dc3 | |||
b6dffa9763 | |||
54b57def58 | |||
e2b3cc8118 | |||
5e701a2c88 | |||
27a3df146d | |||
8cce7efbb2 | |||
d225c3a6e3 | |||
b23582d57e | |||
d3c6e512a3 | |||
a873fab10c | |||
ecef0dd65e | |||
1cc74eaece | |||
e2b3be87cb | |||
3ca6c928f9 | |||
5933d16f3e | |||
0f839bcfd0 | |||
edda94edfa | |||
b5e36eaa3e | |||
08a1f51704 | |||
9690e36f78 | |||
68e741227b | |||
50a2327520 | |||
b07346c8e3 | |||
8e95849751 | |||
cd143f67af | |||
dd85351162 | |||
c1c68889de | |||
866f4be782 | |||
80e4019c83 | |||
d9f3316021 | |||
424a3b95aa | |||
7e883c2319 | |||
6a7fd70ddf | |||
73f9db53c2 | |||
2bf526bbfb | |||
8bd7d5befc | |||
aa163bea93 | |||
2f73c554c9 | |||
fb2d15fda1 | |||
018507fa02 | |||
95bb72d322 | |||
ce6e6c3f81 | |||
e4b1bdbfa0 | |||
6477c9e04a | |||
3f380b3b78 | |||
c4c0e9dbc5 | |||
4ddf57c58d | |||
624f01c311 | |||
6a713521e3 | |||
57a5ba3438 | |||
1b6fb5bc19 | |||
bd9da268c1 | |||
1e58a2194f | |||
da81c9cb54 | |||
ef6d26d4fa | |||
13bc1cd853 | |||
1fa1379318 | |||
d9efacc07e | |||
47f5b5f2d4 | |||
019fe77f64 | |||
a0e3a8e0f7 | |||
8908156eae | |||
2ee265fb1f | |||
116512b06d | |||
1d841973eb | |||
3694e1a38c | |||
88d8813ccc | |||
49bfb63442 | |||
48848c9ca4 | |||
a1f1ea993d | |||
be8a403bc9 | |||
c378402af4 | |||
91ce8c17ff | |||
786be967fd | |||
e88d5e0df2 | |||
f39f01e00b | |||
ce750e6f5b | |||
beba944843 | |||
50981cbb1f | |||
76e40eed35 | |||
d491a20f3b |
@ -1,4 +1,6 @@
|
||||
node_modules
|
||||
dist
|
||||
aio/content
|
||||
aio/node_modules
|
||||
aio/tools/examples/shared/node_modules
|
||||
integration/bazel
|
||||
|
2
.bazelrc
@ -33,7 +33,7 @@ test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test
|
||||
# eventually a surprising failure with auto-discovery of the C++ toolchain in
|
||||
# MacOS High Sierra.
|
||||
# See https://github.com/bazelbuild/bazel/issues/4603
|
||||
build --symlink_prefix=dist/
|
||||
build --symlink_prefix=/
|
||||
|
||||
# Performance: avoid stat'ing input files
|
||||
build --watchfs
|
||||
|
@ -23,13 +23,31 @@ var_2: &browsers_docker_image circleci/node:10.12-browsers
|
||||
# cache changes. For example:
|
||||
# 1) yarn lock file changes --> cached "node_modules" are different.
|
||||
# 2) bazel repository definitions change --> cached bazel repositories are different.
|
||||
var_3: &cache_key v2-angular-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-node-10.12
|
||||
# **NOTE 1 **: If you change the cache key prefix, also sync the restore_cache fallback to match.
|
||||
# **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks.
|
||||
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
|
||||
var_3: &cache_key v3-angular-node-10.12-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||
|
||||
# Define common ENV vars
|
||||
var_4: &define_env_vars
|
||||
# Initializes the CI environment by setting up common environment variables.
|
||||
var_4: &init_environment
|
||||
run:
|
||||
name: Define environment variables
|
||||
command: ./.circleci/env.sh
|
||||
name: Initializing environment (setting up variables, overwriting Yarn)
|
||||
# Overwrite the yarn installed in the docker container with our own version.
|
||||
command: |
|
||||
./.circleci/env.sh
|
||||
ourYarn=$(realpath ./third_party/github.com/yarnpkg/yarn/releases/download/v1.13.0/bin/yarn.js)
|
||||
sudo chmod a+x $ourYarn
|
||||
sudo ln -fs $ourYarn /usr/local/bin/yarn
|
||||
echo "Yarn version: $(yarn --version)"
|
||||
|
||||
# Add GitHub to known hosts.
|
||||
mkdir -p ~/.ssh
|
||||
echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts
|
||||
|
||||
# use git+ssh instead of https
|
||||
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
|
||||
git config --global gc.auto 0 || true
|
||||
|
||||
|
||||
var_5: &setup_bazel_remote_execution
|
||||
run:
|
||||
@ -45,11 +63,20 @@ var_6: &job_defaults
|
||||
docker:
|
||||
- image: *default_docker_image
|
||||
|
||||
# After checkout, rebase on top of master.
|
||||
# Similar to travis behavior, but not quite the same.
|
||||
# See https://discuss.circleci.com/t/1662
|
||||
# After checkout, rebase on top of target branch.
|
||||
var_7: &post_checkout
|
||||
post: git pull --ff-only origin "refs/pull/${CI_PULL_REQUEST//*pull\//}/merge"
|
||||
run:
|
||||
name: Rebase PR on target branch
|
||||
command: >
|
||||
if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
|
||||
# User is required for rebase.
|
||||
git config user.name "angular-ci"
|
||||
git config user.email "angular-ci"
|
||||
# Rebase PR on top of target branch.
|
||||
node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER}
|
||||
else
|
||||
echo "This build is not over a PR, nothing to do."
|
||||
fi
|
||||
|
||||
var_8: &yarn_install
|
||||
run:
|
||||
@ -67,17 +94,63 @@ var_9: &setup_circleci_bazel_config
|
||||
name: Setting up CircleCI bazel configuration
|
||||
command: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
|
||||
var_10: &restore_cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- *cache_key
|
||||
# This fallback should be the cache_key without variables.
|
||||
- v3-angular-node-10.12-
|
||||
|
||||
# Branch filter that can be specified for jobs that should only run on publish branches
|
||||
# (e.g. master or the patch branch)
|
||||
var_12: &publish_branches_filter
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
# e.g. 7.0.x, 7.1.x, etc.
|
||||
- /\d+\.\d+\.x/
|
||||
|
||||
# Workspace initially persisted by the `install` job, and then enhanced by `test_aio` and
|
||||
# `build-npm-packages`.
|
||||
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
|
||||
# https://circleci.com/blog/deep-diving-into-circleci-workspaces/
|
||||
var_13: &attach_workspace
|
||||
attach_workspace:
|
||||
at: ~/
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
setup:
|
||||
<<: *job_defaults
|
||||
steps:
|
||||
- checkout
|
||||
- *post_checkout
|
||||
# This cache is saved in the build-npm-packages so that Bazel cache is also included.
|
||||
- *restore_cache
|
||||
- *init_environment
|
||||
- *yarn_install
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
# Make the bazel directories and add a file to them if they don't exist already so that
|
||||
# persist_to_workspace does not fail.
|
||||
- run: |
|
||||
if [ ! -d ~/bazel_repository_cache ]; then
|
||||
mkdir ~/bazel_repository_cache
|
||||
touch ~/bazel_repository_cache/MARKER
|
||||
fi
|
||||
# Persist any changes at this point to be reused by further jobs.
|
||||
# **NOTE 1 **: Folders persisted here should be kept in sync with `var_13: &attach_workspace`.
|
||||
# **NOTE 2 **: To add new content to the workspace, always persist on the same root.
|
||||
- persist_to_workspace:
|
||||
root: ~/
|
||||
paths:
|
||||
- ./ng
|
||||
- ./bazel_repository_cache
|
||||
|
||||
lint:
|
||||
<<: *job_defaults
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *yarn_install
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
|
||||
- run: 'yarn bazel:format -mode=check ||
|
||||
(echo "BUILD files not formatted. Please run ''yarn bazel:format''" ; exit 1)'
|
||||
@ -85,19 +158,15 @@ jobs:
|
||||
- run: 'yarn bazel:lint ||
|
||||
(echo -e "\n.bzl files have lint errors. Please run ''yarn bazel:lint-fix''"; exit 1)'
|
||||
|
||||
- run: ./node_modules/.bin/gulp lint
|
||||
- run: yarn gulp lint
|
||||
|
||||
test:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- *yarn_install
|
||||
|
||||
# Setup remote execution and run RBE-compatible tests.
|
||||
- *setup_bazel_remote_execution
|
||||
@ -106,24 +175,14 @@ jobs:
|
||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
- run: yarn bazel test //... --build_tag_filters=-ivy-only,local --test_tag_filters=-ivy-only,local
|
||||
|
||||
- save_cache:
|
||||
key: *cache_key
|
||||
paths:
|
||||
- "node_modules"
|
||||
- "~/bazel_repository_cache"
|
||||
|
||||
# Temporary job to test what will happen when we flip the Ivy flag to true
|
||||
test_ivy_aot:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- *yarn_install
|
||||
- *setup_bazel_remote_execution
|
||||
|
||||
# We need to explicitly specify the --symlink_prefix option because otherwise we would
|
||||
@ -156,11 +215,8 @@ jobs:
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
- image: *browsers_docker_image
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Build aio
|
||||
- run: yarn --cwd aio build --progress=false
|
||||
# Lint the code
|
||||
@ -184,11 +240,8 @@ jobs:
|
||||
# Needed because before deploying the deploy-production script runs the PWA score tests.
|
||||
- image: *browsers_docker_image
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Deploy angular.io to production (if necessary)
|
||||
- run: setPublicVar CI_STABLE_BRANCH "$(npm info @angular/core dist-tags.latest | sed -r 's/^\s*([0-9]+\.[0-9]+)\.[0-9]+.*$/\1.x/')"
|
||||
- run: yarn --cwd aio deploy-production
|
||||
@ -199,13 +252,8 @@ jobs:
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
- image: *browsers_docker_image
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- attach_workspace:
|
||||
at: dist
|
||||
- *define_env_vars
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Build aio (with local Angular packages)
|
||||
- run: yarn --cwd aio build-local --progress=false
|
||||
# Run PWA-score tests
|
||||
@ -219,26 +267,22 @@ jobs:
|
||||
test_aio_local_ivy:
|
||||
<<: *job_defaults
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- attach_workspace:
|
||||
at: dist
|
||||
- *define_env_vars
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Rename the Ivy packages dist folder to "dist/packages-dist" as the AIO
|
||||
# package installer picks up the locally built packages from that location.
|
||||
# *Note*: We could also adjust the packages installer, but given we won't have
|
||||
# two different folders of Angular distributions in the future, it's likely not
|
||||
# worth the efforts to change the AIO packages installer.
|
||||
- run: mv dist/packages-dist-ivy-aot dist/packages-dist
|
||||
# Build aio with Ivy (using local Angular packages)
|
||||
- run: yarn --cwd aio build-with-ivy --progress=false
|
||||
|
||||
test_aio_tools:
|
||||
<<: *job_defaults
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- attach_workspace:
|
||||
at: dist
|
||||
- *define_env_vars
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Install
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
- run: yarn --cwd aio extract-cli-command-docs
|
||||
@ -251,23 +295,43 @@ jobs:
|
||||
docker:
|
||||
# Needed because the example e2e tests depend on Chrome.
|
||||
- image: *browsers_docker_image
|
||||
parallelism: 3
|
||||
parallelism: 4
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- attach_workspace:
|
||||
at: dist
|
||||
- *define_env_vars
|
||||
# Install root
|
||||
- *yarn_install
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Install aio
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
# Run examples tests. 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 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL}
|
||||
- run: yarn --cwd aio example-e2e --setup --local --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL}
|
||||
|
||||
test_docs_examples_ivy:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because the example e2e tests depend on Chrome.
|
||||
- image: *browsers_docker_image
|
||||
resource_class: xlarge
|
||||
# We increase the parallelism here to five while the "test_docs_examples" job runs with
|
||||
# a parallelism of four. This is necessary because this job also need to run NGCC which
|
||||
# takes up more time and we don't want these jobs to impact the overall CI turnaround.
|
||||
parallelism: 5
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Install aio
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
# Rename the Ivy packages dist folder to "dist/packages-dist" as the AIO
|
||||
# package installer picks up the locally built packages from that location.
|
||||
# *Note*: We could also adjust the packages installer, but given we won't have
|
||||
# two different folders of Angular distributions in the future, we should keep
|
||||
# the packages installer unchanged.
|
||||
- run: mv dist/packages-dist-ivy-aot dist/packages-dist
|
||||
# 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}
|
||||
|
||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||
aio_preview:
|
||||
@ -275,12 +339,8 @@ jobs:
|
||||
environment:
|
||||
AIO_SNAPSHOT_ARTIFACT_PATH: &aio_preview_artifact_path 'aio/tmp/snapshot.tgz'
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *yarn_install
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- run: ./aio/scripts/build-artifacts.sh $AIO_SNAPSHOT_ARTIFACT_PATH $CI_PULL_REQUEST $CI_COMMIT
|
||||
- store_artifacts:
|
||||
path: *aio_preview_artifact_path
|
||||
@ -296,12 +356,9 @@ jobs:
|
||||
# Needed because the test-preview script runs e2e tests and the PWA score test with Chrome.
|
||||
- image: *browsers_docker_image
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- run: yarn install --cwd aio --frozen-lockfile --non-interactive
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
- run:
|
||||
name: Wait for preview and run tests
|
||||
command: node aio/scripts/test-preview.js $CI_PULL_REQUEST $CI_COMMIT $CI_AIO_MIN_PWA_SCORE
|
||||
@ -319,47 +376,44 @@ jobs:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- *yarn_install
|
||||
- *setup_bazel_remote_execution
|
||||
|
||||
- run: scripts/build-packages-dist.sh
|
||||
|
||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
||||
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
|
||||
- persist_to_workspace:
|
||||
root: dist
|
||||
root: ~/
|
||||
paths:
|
||||
- packages-dist
|
||||
- ng/dist/packages-dist
|
||||
|
||||
# Save dependencies and bazel repository cache to use on subsequent runs.
|
||||
- save_cache:
|
||||
key: *cache_key
|
||||
paths:
|
||||
- "node_modules"
|
||||
- "aio/node_modules"
|
||||
- "~/bazel_repository_cache"
|
||||
|
||||
# Build the ivy npm packages.
|
||||
build-ivy-npm-packages:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- *yarn_install
|
||||
- *setup_bazel_remote_execution
|
||||
|
||||
- run: scripts/build-ivy-npm-packages.sh
|
||||
|
||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
||||
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
|
||||
- persist_to_workspace:
|
||||
root: dist
|
||||
root: ~/
|
||||
paths:
|
||||
- packages-dist-ivy-aot
|
||||
- ng/dist/packages-dist-ivy-aot
|
||||
|
||||
# We run the integration tests outside of Bazel for now.
|
||||
# They are a separate workflow job so that they can be easily re-run.
|
||||
@ -378,13 +432,8 @@ jobs:
|
||||
# on a 4G worker so we use a larger machine here too.
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- attach_workspace:
|
||||
at: dist
|
||||
- *define_env_vars
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Runs the integration tests in parallel across multiple CircleCI container instances. The
|
||||
# amount of container nodes for this job is controlled by the "parallelism" option.
|
||||
- run: ./integration/run_tests.sh ${CIRCLE_NODE_INDEX} ${CIRCLE_NODE_TOTAL}
|
||||
@ -394,21 +443,20 @@ jobs:
|
||||
publish_snapshot:
|
||||
<<: *job_defaults
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- *define_env_vars
|
||||
# See below - ideally this job should not trigger for non-upstream builds.
|
||||
# But since it does, we have to check this condition.
|
||||
- run:
|
||||
name: Skip this job for Pull Requests and Fork builds
|
||||
# Note, `|| true` on the end makes this step always exit 0
|
||||
command: '[[
|
||||
"$CI_PULL_REQUEST" != "false"
|
||||
|| "$CI_REPO_OWNER" != "angular"
|
||||
|| "$CI_REPO_NAME" != "angular"
|
||||
]] && circleci step halt || true'
|
||||
- attach_workspace:
|
||||
at: dist
|
||||
# Note: Using `CIRCLE_*` env variables (instead of those defined in `env.sh` so that this
|
||||
# step can be run before `init_environment`.
|
||||
command: >
|
||||
if [[ -n "${CIRCLE_PR_NUMBER}" ]] ||
|
||||
[[ "$CIRCLE_PROJECT_USERNAME" != "angular" ]] ||
|
||||
[[ "$CIRCLE_PROJECT_REPONAME" != "angular" ]]; then
|
||||
circleci step halt
|
||||
fi
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# CircleCI has a config setting to force SSH for all github connections
|
||||
# This is not compatible with our mechanism of using a Personal Access Token
|
||||
# Clear the global setting
|
||||
@ -425,35 +473,18 @@ jobs:
|
||||
# which does not load the browser through the Bazel webtesting rules.
|
||||
- image: *browsers_docker_image
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- run:
|
||||
name: Run tests against the deployed apps
|
||||
command: ./aio/scripts/test-production.sh $CI_AIO_MIN_PWA_SCORE
|
||||
- run:
|
||||
name: Notify caretaker about failure
|
||||
command: 'curl --request POST --header "Content-Type: application/json" --data "{\"text\":\":x: \`$CIRCLE_JOB\` job failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}" $CI_SECRET_SLACK_CARETAKER_WEBHOOK_URL'
|
||||
# `$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: 'curl --request POST --header "Content-Type: application/json" --data "{\"text\":\":x: \`$CIRCLE_JOB\` job failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}" $SLACK_CARETAKER_WEBHOOK_URL'
|
||||
when: on_fail
|
||||
|
||||
legacy-unit-tests-local:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
- image: *browsers_docker_image
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *yarn_install
|
||||
- run: yarn tsc -p packages
|
||||
- run: yarn tsc -p packages/examples
|
||||
- run: yarn tsc -p modules
|
||||
- run: yarn karma start ./karma-js.conf.js --single-run --browsers=ChromeNoSandbox
|
||||
|
||||
legacy-unit-tests-saucelabs:
|
||||
<<: *job_defaults
|
||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
||||
@ -461,12 +492,8 @@ jobs:
|
||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *yarn_install
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- run:
|
||||
name: Preparing environment for running tests on Saucelabs.
|
||||
command: |
|
||||
@ -477,7 +504,6 @@ jobs:
|
||||
command: ./scripts/saucelabs/start-tunnel.sh
|
||||
background: true
|
||||
- run: yarn tsc -p packages
|
||||
- run: yarn tsc -p packages/examples
|
||||
- run: yarn tsc -p modules
|
||||
# Waits for the Saucelabs tunnel to be ready. This ensures that we don't run tests
|
||||
# too early without Saucelabs not being ready.
|
||||
@ -485,43 +511,11 @@ jobs:
|
||||
- run: yarn karma start ./karma-js.conf.js --single-run --browsers=${KARMA_JS_BROWSERS}
|
||||
- run: ./scripts/saucelabs/stop-tunnel.sh
|
||||
|
||||
legacy-e2e-tests:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
- image: *browsers_docker_image
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *setup_circleci_bazel_config
|
||||
- *yarn_install
|
||||
- *setup_bazel_remote_execution
|
||||
- attach_workspace:
|
||||
at: dist
|
||||
# Build the e2e tests using the existing Bazel "packages-dist" output that has been
|
||||
# attached to this job. This avoids multiple rebuilds across various CI jobs.
|
||||
- run: ./scripts/build-e2e-tests.sh --use-existing-packages-dist
|
||||
- run:
|
||||
name: Starting servers for e2e tests
|
||||
command: yarn gulp serve serve-examples
|
||||
background: true
|
||||
- run: NODE_PATH=$NODE_PATH:./dist/all yarn protractor ./protractor-e2e.conf.js --bundles=true
|
||||
- run: NODE_PATH=$NODE_PATH:./dist/all yarn protractor ./protractor-examples-e2e.conf.js --bundles=true
|
||||
- run: NODE_PATH=$NODE_PATH:./dist/all yarn protractor ./protractor-perf.conf.js --bundles=true --dryrun
|
||||
|
||||
legacy-misc-tests:
|
||||
<<: *job_defaults
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *yarn_install
|
||||
- attach_workspace:
|
||||
at: dist
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- run: yarn gulp check-cycle
|
||||
# TODO: disabled because the Bazel packages-dist does not seem to have map files for
|
||||
# the ESM5/ES2015 output. See: https://github.com/angular/angular/issues/27966
|
||||
@ -531,32 +525,49 @@ workflows:
|
||||
version: 2
|
||||
default_workflow:
|
||||
jobs:
|
||||
- lint
|
||||
- test
|
||||
- test_ivy_aot
|
||||
- build-npm-packages
|
||||
- test_aio
|
||||
- legacy-unit-tests-local
|
||||
- legacy-unit-tests-saucelabs
|
||||
- setup
|
||||
- lint:
|
||||
requires:
|
||||
- setup
|
||||
- test:
|
||||
requires:
|
||||
- setup
|
||||
- test_ivy_aot:
|
||||
requires:
|
||||
- setup
|
||||
- build-npm-packages:
|
||||
requires:
|
||||
- setup
|
||||
- test_aio:
|
||||
requires:
|
||||
- setup
|
||||
- legacy-unit-tests-saucelabs:
|
||||
requires:
|
||||
- setup
|
||||
- deploy_aio:
|
||||
requires:
|
||||
- test_aio
|
||||
- legacy-e2e-tests:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- legacy-misc-tests:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- test_aio_local:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
# - test_aio_local_ivy:
|
||||
# requires:
|
||||
# - build-ivy-npm-packages
|
||||
- test_aio_tools:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- test_docs_examples:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
# - test_docs_examples_ivy:
|
||||
# requires:
|
||||
# - build-ivy-npm-packages
|
||||
- aio_preview:
|
||||
requires:
|
||||
- setup
|
||||
# Only run on PR builds. (There can be no previews for non-PR builds.)
|
||||
filters:
|
||||
branches:
|
||||
@ -581,20 +592,19 @@ workflows:
|
||||
- test_aio_local
|
||||
# - test_aio_local_ivy
|
||||
- test_docs_examples
|
||||
# Get the artifacts to publish from the build-npm-packages job
|
||||
# - test_docs_examples_ivy
|
||||
# Get the artifacts to publish from the build-packages-dist job
|
||||
# since the publishing script expects the legacy outputs layout.
|
||||
- build-npm-packages
|
||||
- legacy-e2e-tests
|
||||
- legacy-misc-tests
|
||||
- legacy-unit-tests-local
|
||||
- legacy-unit-tests-saucelabs
|
||||
|
||||
- legacy-misc-tests
|
||||
|
||||
aio_monitoring:
|
||||
jobs:
|
||||
- aio_monitoring
|
||||
triggers:
|
||||
- schedule:
|
||||
# Runs AIO monitoring job at 00:00AM every day.
|
||||
cron: "0 0 * * *"
|
||||
filters:
|
||||
branches:
|
||||
|
@ -1,8 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Variables
|
||||
readonly envHelpersPath="`dirname $0`/env-helpers.inc.sh";
|
||||
readonly getCommitRangePath="`dirname $0`/get-commit-range.js";
|
||||
readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..")
|
||||
readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh";
|
||||
readonly getCommitRangePath="$projectDir/.circleci/get-commit-range.js";
|
||||
|
||||
# Load helpers and make them available everywhere (through `$BASH_ENV`).
|
||||
source $envHelpersPath;
|
||||
@ -14,7 +15,7 @@ echo "source $envHelpersPath;" >> $BASH_ENV;
|
||||
####################################################################################################
|
||||
# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info.
|
||||
####################################################################################################
|
||||
setPublicVar PROJECT_ROOT "$(pwd)";
|
||||
setPublicVar PROJECT_ROOT "$projectDir";
|
||||
setPublicVar CI_AIO_MIN_PWA_SCORE "95";
|
||||
# This is the branch being built; e.g. `pull/12345` for PR builds.
|
||||
setPublicVar CI_BRANCH "$CIRCLE_BRANCH";
|
||||
@ -38,8 +39,6 @@ setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME";
|
||||
####################################################################################################
|
||||
setSecretVar CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN "$AIO_DEPLOY_TOKEN";
|
||||
setSecretVar CI_SECRET_PAYLOAD_FIREBASE_TOKEN "$ANGULAR_PAYLOAD_TOKEN";
|
||||
# Defined in https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
|
||||
setSecretVar CI_SECRET_SLACK_CARETAKER_WEBHOOK_URL "$SLACK_CARETAKER_WEBHOOK_URL";
|
||||
|
||||
|
||||
####################################################################################################
|
||||
@ -55,6 +54,7 @@ else
|
||||
setPublicVar SAUCE_USERNAME "angular-ci";
|
||||
setSecretVar SAUCE_ACCESS_KEY "9b988f434ff8-fbca-8aa4-4ae3-35442987";
|
||||
fi
|
||||
setPublicVar SAUCE_LOG_FILE /tmp/angular/sauce-connect.log
|
||||
setPublicVar SAUCE_READY_FILE /tmp/angular/sauce-connect-ready-file.lock
|
||||
setPublicVar SAUCE_PID_FILE /tmp/angular/sauce-connect-pid-file.lock
|
||||
setPublicVar SAUCE_TUNNEL_IDENTIFIER "angular-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}"
|
||||
|
105
.github/CODEOWNERS
vendored
@ -39,6 +39,7 @@
|
||||
# (just to make this file easier to understand)
|
||||
# ================================================
|
||||
|
||||
# alan-agius4 - Alan Agius
|
||||
# alexeagle - Alex Eagle
|
||||
# alxhub - Alex Rickabaugh
|
||||
# AndrewKushnir - Andrew Kushnir
|
||||
@ -293,6 +294,17 @@
|
||||
# - IgorMinar
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/tools-docs-libraries
|
||||
# ===========================================================
|
||||
#
|
||||
# - alan-agius4
|
||||
# - alexeagle
|
||||
# - hansl
|
||||
# - IgorMinar
|
||||
# - mgechev
|
||||
|
||||
|
||||
# ===========================================================
|
||||
# @angular/fw-docs-marketing
|
||||
# ===========================================================
|
||||
@ -313,6 +325,8 @@
|
||||
# ===========================================================
|
||||
#
|
||||
# - alexeagle
|
||||
# - filipesilva
|
||||
# - gkalpak
|
||||
# - IgorMinar
|
||||
|
||||
|
||||
@ -347,10 +361,19 @@
|
||||
|
||||
/packages/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/packages/platform-browser/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/animations.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/complex-animation-sequences.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/reusable-animations.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/route-animations.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/transition-and-triggers.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
@ -381,12 +404,13 @@
|
||||
|
||||
|
||||
# ================================================
|
||||
# @angular/compiler-cli/ngtools
|
||||
# Framework/cli integration
|
||||
#
|
||||
# a rule to control API changes between @angular/compiler-cli and @angular/cli
|
||||
# ================================================
|
||||
|
||||
/packages/compiler-cli/src/ngtools/** @angular/tools-cli @angular/framework-global-approvers
|
||||
/aio/content/guide/ivy.md @angular/tools-cli @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
|
||||
|
||||
@ -406,6 +430,14 @@
|
||||
/packages/platform-webworker/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/packages/platform-webworker-dynamic/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/architecture-components.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/architecture-modules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/architecture-next-steps.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/architecture-services.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/architecture.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/architecture/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/architecture/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/attribute-directives.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/attribute-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/attribute-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
@ -413,6 +445,8 @@
|
||||
/aio/content/guide/bootstrapping.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/bootstrapping/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/cheatsheet.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/component-interaction.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/component-interaction/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/component-interaction/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
@ -428,7 +462,13 @@
|
||||
/aio/content/examples/dependency-injection-in-action/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/dependency-injection-in-action/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/dependency-injection-pattern.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/dependency-injection-navtree.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/dependency-injection-providers.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/displaying-data.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/displaying-data/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/displaying-data/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/dynamic-component-loader.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/dynamic-component-loader/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
@ -455,12 +495,9 @@
|
||||
/aio/content/images/guide/lifecycle-hooks/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/examples/ngcontainer/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/ngcontainer/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/ngmodules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/ngmodules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/ngmodule/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/ngmodule/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/ngmodule-api.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
@ -472,6 +509,8 @@
|
||||
/aio/content/guide/module-types.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/template-syntax.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/event-binding/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/interpolation/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/template-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/template-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
@ -494,6 +533,10 @@
|
||||
/aio/content/examples/structural-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/structural-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
/aio/content/guide/user-input.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/user-input/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/user-input/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
@ -519,6 +562,7 @@
|
||||
/aio/content/guide/elements.md @angular/fw-elements @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
# @angular/forms
|
||||
# ================================================
|
||||
@ -649,6 +693,7 @@ testing/** @angular/fw-test
|
||||
/packages/platform-browser/src/security/** @angular/fw-security
|
||||
/aio/content/guide/security.md @angular/fw-security @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/security/** @angular/fw-security @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/security/** @angular/fw-security @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
|
||||
|
||||
@ -679,6 +724,14 @@ testing/** @angular/fw-test
|
||||
/aio/tests/** @angular/docs-infra @angular/framework-global-approvers
|
||||
/aio/tools/** @angular/docs-infra @angular/framework-global-approvers
|
||||
|
||||
# Hidden docs
|
||||
/aio/content/guide/change-log.md @angular/docs-infra @angular/framework-global-approvers
|
||||
/aio/content/guide/docs-style-guide.md @angular/docs-infra @angular/framework-global-approvers
|
||||
/aio/content/examples/docs-style-guide/** @angular/docs-infra @angular/framework-global-approvers
|
||||
/aio/content/images/guide/docs-style-guide/** @angular/docs-infra @angular/framework-global-approvers
|
||||
/aio/content/guide/visual-studio-2015.md @angular/docs-infra @angular/framework-global-approvers
|
||||
/aio/content/examples/visual-studio-2015/** @angular/docs-infra @angular/framework-global-approvers
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
@ -686,7 +739,17 @@ testing/** @angular/fw-test
|
||||
# ================================================
|
||||
|
||||
/aio/content/guide/quickstart.md @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/cli-quickstart/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/cli-quickstart/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/tutorial/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/toh/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/toh-pt0/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/toh-pt1/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/toh-pt2/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/toh-pt3/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/toh-pt4/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/toh-pt5/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/toh-pt6/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
|
||||
|
||||
@ -694,17 +757,15 @@ testing/** @angular/fw-test
|
||||
# Docs: observables
|
||||
# ================================================
|
||||
|
||||
/aio/content/examples/observables/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/observables/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/observables.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/observables/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/comparing-observables.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/observables-in-angular/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/observables-in-angular/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/observables-in-angular.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/practical-observable-usage/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/observables-in-angular/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/practical-observable-usage.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/rx-library/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/practical-observable-usage/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/rx-library.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/rx-library/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
|
||||
|
||||
@ -715,12 +776,26 @@ testing/** @angular/fw-test
|
||||
/aio/content/guide/npm-packages.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/browser-support.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/typescript-configuration.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/setup-systemjs-anatomy.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/quickstart/** @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/setup.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/examples/setup/** @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/build.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/guide/build/** @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/deployment.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/file-structure.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/releases.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/updating.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/workspace-config.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
# Docs: libraries
|
||||
# ================================================
|
||||
|
||||
/aio/content/guide/creating-libraries.md @angular/tools-docs-libraries @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/libraries.md @angular/tools-docs-libraries @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/using-libraries.md @angular/tools-docs-libraries @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
|
||||
|
||||
@ -729,6 +804,7 @@ testing/** @angular/fw-test
|
||||
# ================================================
|
||||
|
||||
/aio/content/marketing/** @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/bios/** @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/images/marketing/** @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/navigation.json @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/license.md @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
@ -744,6 +820,7 @@ testing/** @angular/fw-test
|
||||
/.circleci/** @angular/fw-dev-infra
|
||||
/.github/** @angular/fw-dev-infra
|
||||
/docs/BAZEL.md @angular/fw-dev-infra
|
||||
/packages/* @angular/fw-dev-infra
|
||||
/scripts/** @angular/fw-dev-infra
|
||||
/third_party/** @angular/fw-dev-infra
|
||||
/tools/** @angular/fw-dev-infra
|
||||
@ -756,6 +833,10 @@ testing/** @angular/fw-test
|
||||
# ================================================
|
||||
|
||||
/tools/public_api_guard/** @angular/fw-public-api
|
||||
/aio/content/guide/glossary.md @angular/fw-public-api
|
||||
/aio/content/guide/styleguide.md @angular/fw-public-api
|
||||
/aio/content/examples/styleguide/** @angular/fw-public-api
|
||||
/aio/content/images/guide/styleguide/** @angular/fw-public-api
|
||||
|
||||
|
||||
|
||||
|
4
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
@ -37,7 +37,9 @@ Please create and share minimal reproduction of the issue starting with this tem
|
||||
<!-- ✍️--> https://stackblitz.com/...
|
||||
|
||||
<!--
|
||||
If StackBlitz is not suitable for reproduction of your issue, please create a minimal GitHub repository with the reproduction of the issue. Share the link to the repo below along with step-by-step instructions to reproduce the problem, as well as expected and actual behavior.
|
||||
If StackBlitz is not suitable for reproduction of your issue, please create a minimal GitHub repository with the reproduction of the issue.
|
||||
A good way to make a minimal reproduction is to create a new app via `ng new repro-app` and add the minimum possible code to show the problem.
|
||||
Share the link to the repo below along with step-by-step instructions to reproduce the problem, as well as expected and actual behavior.
|
||||
|
||||
Issues that don't have enough info and can't be reproduced will be closed.
|
||||
|
||||
|
7
.github/angular-robot.yml
vendored
@ -56,9 +56,9 @@ merge:
|
||||
- "**/.gitkeep"
|
||||
- "**/yarn.lock"
|
||||
- "**/package.json"
|
||||
- "**/third_party/**"
|
||||
- "**/tsconfig-build.json"
|
||||
- "**/tsconfig.json"
|
||||
- "**/rollup.config.js"
|
||||
- "**/BUILD.bazel"
|
||||
- "**/*.md"
|
||||
- "packages/**/integrationtest/**"
|
||||
@ -103,6 +103,11 @@ merge:
|
||||
requiredStatuses:
|
||||
- "ci/circleci: build"
|
||||
- "ci/circleci: lint"
|
||||
- "ci/circleci: publish_snapshot"
|
||||
- "ci/angular: size"
|
||||
- "cla/google"
|
||||
- "google3"
|
||||
|
||||
|
||||
# the comment that will be added when the merge label is added despite failing checks, leave empty or set to false to disable
|
||||
# {{MERGE_LABEL}} will be replaced by the value of the mergeLabel option
|
||||
|
11
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"gkalpak.aio-docs-utils",
|
||||
"ms-vscode.vscode-typescript-tslint-plugin",
|
||||
"xaver.clang-format",
|
||||
],
|
||||
}
|
10
.vscode/settings.json
vendored
@ -1,4 +1,13 @@
|
||||
{
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": true,
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.formatOnSave": true,
|
||||
},
|
||||
// Please install https://marketplace.visualstudio.com/items?itemName=xaver.clang-format to take advantage of `clang-format` in VSCode.
|
||||
// (See https://clang.llvm.org/docs/ClangFormat.html for more info `clang-format`.)
|
||||
"clang-format.executable": "${workspaceRoot}/node_modules/.bin/clang-format",
|
||||
"files.watcherExclude": {
|
||||
"**/.git/objects/**": true,
|
||||
"**/.git/subtree-cache/**": true,
|
||||
@ -12,4 +21,5 @@
|
||||
"**/bazel-out": true,
|
||||
"**/dist": true,
|
||||
},
|
||||
"git.ignoreLimitWarning": true,
|
||||
}
|
46
BUILD.bazel
@ -1,7 +1,5 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "node_modules_filegroup")
|
||||
|
||||
exports_files([
|
||||
"tsconfig.json",
|
||||
"LICENSE",
|
||||
@ -12,10 +10,10 @@ filegroup(
|
||||
name = "web_test_bootstrap_scripts",
|
||||
# do not sort
|
||||
srcs = [
|
||||
"@ngdeps//node_modules/reflect-metadata:Reflect.js",
|
||||
"@ngdeps//node_modules/zone.js:dist/zone.js",
|
||||
"@ngdeps//node_modules/zone.js:dist/zone-testing.js",
|
||||
"@ngdeps//node_modules/zone.js:dist/task-tracking.js",
|
||||
"@npm//node_modules/reflect-metadata:Reflect.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",
|
||||
],
|
||||
)
|
||||
@ -25,32 +23,14 @@ 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)
|
||||
"@ngdeps//node_modules/angular:angular.js",
|
||||
"@ngdeps//node_modules/angular:angular.min.js",
|
||||
"@ngdeps//node_modules/angular-1.5:angular.js",
|
||||
"@ngdeps//node_modules/angular-1.5:angular.min.js",
|
||||
"@ngdeps//node_modules/angular-1.6:angular.js",
|
||||
"@ngdeps//node_modules/angular-1.6:angular.min.js",
|
||||
"@ngdeps//node_modules/angular-mocks:angular-mocks.js",
|
||||
"@ngdeps//node_modules/angular-mocks-1.5:angular-mocks.js",
|
||||
"@ngdeps//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",
|
||||
],
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
|
||||
|
||||
# A nodejs_binary for @angular/bazel/ngc-wrapped to use by default in
|
||||
# ng_module that depends on @npm//@angular/bazel instead of the
|
||||
# output of the //packages/bazel/src/ngc-wrapped ts_library rule. This
|
||||
# default is for downstream users that depend on the @angular/bazel npm
|
||||
# package. The generated @npm//@angular/bazel/ngc-wrapped target
|
||||
# does not work because it does not have the node `--expose-gc` flag
|
||||
# set which is required to support the call to `global.gc()`.
|
||||
nodejs_binary(
|
||||
name = "@angular/bazel/ngc-wrapped",
|
||||
configuration_env_vars = ["compile"],
|
||||
data = ["@npm//@angular/bazel"],
|
||||
entry_point = "@angular/bazel/src/ngc-wrapped/index.js",
|
||||
install_source_map_support = False,
|
||||
templated_args = ["--node_options=--expose-gc"],
|
||||
)
|
||||
|
52
CHANGELOG.md
@ -1,3 +1,55 @@
|
||||
<a name="7.2.9"></a>
|
||||
## [7.2.9](https://github.com/angular/angular/compare/7.2.8...7.2.9) (2019-03-12)
|
||||
|
||||
This release contains various API docs improvements.
|
||||
|
||||
<a name="7.2.8"></a>
|
||||
## [7.2.8](https://github.com/angular/angular/compare/7.2.7...7.2.8) (2019-03-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** ensure `position` and `display` styles are handled outside of keyframes/web-animations ([#28911](https://github.com/angular/angular/issues/28911)) ([86981b3](https://github.com/angular/angular/commit/86981b3)), closes [#24923](https://github.com/angular/angular/issues/24923) [#25635](https://github.com/angular/angular/issues/25635)
|
||||
* **router:** removed obsolete TODO comment ([#29085](https://github.com/angular/angular/issues/29085)) ([2a25ac2](https://github.com/angular/angular/commit/2a25ac2))
|
||||
* **service-worker:** detect new version even if files are identical to an old one ([#26006](https://github.com/angular/angular/issues/26006)) ([5669333](https://github.com/angular/angular/commit/5669333)), closes [#24338](https://github.com/angular/angular/issues/24338)
|
||||
* **service-worker:** ignore passive mixed content requests ([#25994](https://github.com/angular/angular/issues/25994)) ([b598e88](https://github.com/angular/angular/commit/b598e88)), closes [/github.com/angular/angular/issues/23012#issuecomment-376430187](https://github.com//github.com/angular/angular/issues/23012/issues/issuecomment-376430187) [#23012](https://github.com/angular/angular/issues/23012)
|
||||
|
||||
|
||||
|
||||
<a name="7.2.7"></a>
|
||||
## [7.2.7](https://github.com/angular/angular/compare/7.2.6...7.2.7) (2019-02-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** pin browser repositories using [@npm](https://github.com/npm)_bazel_karma//:browser_repositories.bzl in bazel schematics ([#28896](https://github.com/angular/angular/issues/28896)) ([b686449](https://github.com/angular/angular/commit/b686449))
|
||||
* **core:** traverse and sanitize content of unsafe elements ([#28804](https://github.com/angular/angular/issues/28804)) ([fdcf877](https://github.com/angular/angular/commit/fdcf877)), closes [#25879](https://github.com/angular/angular/issues/25879) [#25879](https://github.com/angular/angular/issues/25879) [#26007](https://github.com/angular/angular/issues/26007) [#28427](https://github.com/angular/angular/issues/28427)
|
||||
* **language-service:** Fix completions for input/output with alias ([#28904](https://github.com/angular/angular/issues/28904)) ([d0018e6](https://github.com/angular/angular/commit/d0018e6)), closes [#27959](https://github.com/angular/angular/issues/27959)
|
||||
|
||||
|
||||
|
||||
<a name="7.2.6"></a>
|
||||
## [7.2.6](https://github.com/angular/angular/compare/7.2.5...7.2.6) (2019-02-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** incorrect bundled metadata for static class member call expressions ([#28762](https://github.com/angular/angular/issues/28762)) ([ab69c31](https://github.com/angular/angular/commit/ab69c31)), closes [/github.com/angular/angular/blob/master/packages/core/src/change_detection/differs/keyvalue_differs.ts#L121](https://github.com//github.com/angular/angular/blob/master/packages/core/src/change_detection/differs/keyvalue_differs.ts/issues/L121) [#28741](https://github.com/angular/angular/issues/28741)
|
||||
|
||||
|
||||
|
||||
<a name="7.2.5"></a>
|
||||
## [7.2.5](https://github.com/angular/angular/compare/7.2.4...7.2.5) (2019-02-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** diagnostics should respect "newLine" compiler option ([#28550](https://github.com/angular/angular/issues/28550)) ([ce750e6](https://github.com/angular/angular/commit/ce750e6))
|
||||
* **router:** redirect to root url when returned as UrlTree from guard ([#28271](https://github.com/angular/angular/issues/28271)) ([1e58a21](https://github.com/angular/angular/commit/1e58a21)), closes [#27845](https://github.com/angular/angular/issues/27845)
|
||||
* **router:** set href when routerLink is used on an 'area' element ([#28441](https://github.com/angular/angular/issues/28441)) ([d491a20](https://github.com/angular/angular/commit/d491a20)), closes [#28401](https://github.com/angular/angular/issues/28401)
|
||||
|
||||
|
||||
|
||||
<a name="7.2.4"></a>
|
||||
## [7.2.4](https://github.com/angular/angular/compare/7.2.3...7.2.4) (2019-02-06)
|
||||
|
||||
|
125
WORKSPACE
@ -1,17 +1,6 @@
|
||||
workspace(name = "angular")
|
||||
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
load(
|
||||
"//packages/bazel:package.bzl",
|
||||
"rules_angular_dependencies",
|
||||
"rules_angular_dev_dependencies",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_go",
|
||||
sha256 = "b7a62250a3a73277ade0ce306d22f122365b513f5402222403e507f2f997d421",
|
||||
url = "https://github.com/bazelbuild/rules_go/releases/download/0.16.3/rules_go-0.16.3.tar.gz",
|
||||
)
|
||||
|
||||
# Uncomment for local bazel rules development
|
||||
#local_repository(
|
||||
@ -19,59 +8,43 @@ http_archive(
|
||||
# path = "../rules_nodejs",
|
||||
#)
|
||||
#local_repository(
|
||||
# name = "build_bazel_rules_typescript",
|
||||
# name = "npm_bazel_typescript",
|
||||
# path = "../rules_typescript",
|
||||
#)
|
||||
|
||||
# Angular Bazel users will call this function
|
||||
rules_angular_dependencies()
|
||||
|
||||
# Install transitive deps of rules_nodejs
|
||||
load("@build_bazel_rules_nodejs//:package.bzl", "rules_nodejs_dependencies")
|
||||
|
||||
rules_nodejs_dependencies()
|
||||
|
||||
# These are the dependencies only for us
|
||||
rules_angular_dev_dependencies()
|
||||
|
||||
# Install transitive deps of rules_typescript
|
||||
load("@build_bazel_rules_typescript//:package.bzl", "rules_typescript_dependencies")
|
||||
|
||||
rules_typescript_dependencies()
|
||||
|
||||
#
|
||||
# Point Bazel to WORKSPACEs that live in subdirectories
|
||||
#
|
||||
# Fetch rules_nodejs so we can install our npm dependencies
|
||||
http_archive(
|
||||
name = "rxjs",
|
||||
sha256 = "72b0b4e517f43358f554c125e40e39f67688cd2738a8998b4a266981ed32f403",
|
||||
strip_prefix = "package/src",
|
||||
url = "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.3.tgz",
|
||||
name = "build_bazel_rules_nodejs",
|
||||
sha256 = "5c86b055c57e15bf32d9009a15bcd6d8e190c41b1ff2fb18037b75e0012e4e7c",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.26.0/rules_nodejs-0.26.0.tar.gz"],
|
||||
)
|
||||
|
||||
# Point to the integration test workspace just so that Bazel doesn't descend into it
|
||||
# when expanding the //... pattern
|
||||
local_repository(
|
||||
name = "bazel_integration_test",
|
||||
path = "integration/bazel",
|
||||
)
|
||||
|
||||
#
|
||||
# Load and install our dependencies downloaded above.
|
||||
#
|
||||
# Check the bazel version and download npm dependencies
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories", "yarn_install")
|
||||
load("@build_bazel_rules_nodejs//:package.bzl", "check_rules_nodejs_version")
|
||||
|
||||
# Bazel version must be at least v0.21.0 because:
|
||||
# - 0.21.0 Using --incompatible_strict_action_env flag fixes cache when running `yarn bazel`
|
||||
# (see https://github.com/angular/angular/issues/27514#issuecomment-451438271)
|
||||
check_bazel_version("0.21.0", """
|
||||
check_bazel_version(
|
||||
message = """
|
||||
You no longer need to install Bazel on your machine.
|
||||
Angular has a dependency on the @bazel/bazel package which supplies it.
|
||||
Try running `yarn bazel` instead.
|
||||
(If you did run that, check that you've got a fresh `yarn install`)
|
||||
|
||||
""")
|
||||
""",
|
||||
minimum_bazel_version = "0.21.0",
|
||||
)
|
||||
|
||||
# The NodeJS rules version must be at least v0.15.3 because:
|
||||
# - 0.15.2 Re-introduced the prod_only attribute on yarn_install
|
||||
# - 0.15.3 Includes a fix for the `jasmine_node_test` rule ignoring target tags
|
||||
# - 0.16.8 Supports npm installed bazel workspaces
|
||||
# - 0.26.0 Fix for data files in yarn_install and npm_install
|
||||
check_rules_nodejs_version("0.26.0")
|
||||
|
||||
# Setup the Node.js toolchain
|
||||
node_repositories(
|
||||
node_version = "10.9.0",
|
||||
package_json = ["//:package.json"],
|
||||
@ -79,41 +52,63 @@ node_repositories(
|
||||
yarn_version = "1.12.1",
|
||||
)
|
||||
|
||||
local_repository(
|
||||
yarn_install(
|
||||
name = "npm",
|
||||
path = "tools/npm_workspace",
|
||||
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,
|
||||
yarn_lock = "//:yarn.lock",
|
||||
)
|
||||
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_register_toolchains", "go_rules_dependencies")
|
||||
yarn_install(
|
||||
name = "ts-api-guardian_deps",
|
||||
package_json = "@angular//tools/ts-api-guardian:package.json",
|
||||
yarn_lock = "@angular//tools/ts-api-guardian:yarn.lock",
|
||||
)
|
||||
|
||||
go_rules_dependencies()
|
||||
# Install all bazel dependencies of the @npm npm packages
|
||||
load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies")
|
||||
|
||||
go_register_toolchains()
|
||||
install_bazel_dependencies()
|
||||
|
||||
load("@io_bazel_rules_webtesting//web:repositories.bzl", "browser_repositories", "web_test_repositories")
|
||||
# Load angular dependencies
|
||||
load("//packages/bazel:package.bzl", "rules_angular_dev_dependencies")
|
||||
|
||||
rules_angular_dev_dependencies()
|
||||
|
||||
# Load karma dependencies
|
||||
load("@npm_bazel_karma//:package.bzl", "rules_karma_dependencies")
|
||||
|
||||
rules_karma_dependencies()
|
||||
|
||||
# Setup the rules_webtesting toolchain
|
||||
load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories")
|
||||
|
||||
web_test_repositories()
|
||||
|
||||
browser_repositories(
|
||||
chromium = True,
|
||||
firefox = True,
|
||||
)
|
||||
# Temporary work-around for https://github.com/angular/angular/issues/28681
|
||||
# TODO(gregmagolan): go back to @io_bazel_rules_webtesting browser_repositories
|
||||
load("@npm_bazel_karma//:browser_repositories.bzl", "browser_repositories")
|
||||
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
|
||||
browser_repositories()
|
||||
|
||||
# Setup the rules_typescript tooolchain
|
||||
load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace")
|
||||
|
||||
ts_setup_workspace()
|
||||
|
||||
load("@angular//:index.bzl", "ng_setup_workspace")
|
||||
|
||||
ng_setup_workspace()
|
||||
|
||||
##################################
|
||||
# Skylark documentation generation
|
||||
|
||||
# Setup the rules_sass toolchain
|
||||
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
|
||||
|
||||
sass_repositories()
|
||||
|
||||
# Setup the skydoc toolchain
|
||||
load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
|
||||
|
||||
skydoc_repositories()
|
||||
|
2
aio/.gitignore
vendored
@ -26,11 +26,13 @@
|
||||
!.vscode/extensions.json
|
||||
|
||||
# misc
|
||||
/.firebase/
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
debug.log
|
||||
firebase-debug.log
|
||||
npm-debug.log
|
||||
testem.log
|
||||
/typings
|
||||
|
6
aio/content/examples/.gitignore
vendored
@ -57,7 +57,9 @@ dist/
|
||||
|
||||
# aot
|
||||
**/*.ngsummary.json
|
||||
upgrade-module/tsconfig-aot.json
|
||||
!rollup-config.js
|
||||
upgrade-module/rollup-config.js
|
||||
aot-compiler/**/*.d.ts
|
||||
aot-compiler/**/*.factory.d.ts
|
||||
upgrade-phonecat-2-hybrid/aot/**/*
|
||||
@ -84,5 +86,9 @@ upgrade-phonecat-2-hybrid/aot/**/*
|
||||
*stackblitz.no-link.html
|
||||
|
||||
# ngUpgrade testing
|
||||
upgrade-phonecat-1-typescript/tsconfig-aot.json
|
||||
upgrade-phonecat-1-typescript/rollup-config.js
|
||||
upgrade-phonecat-3-final/tsconfig-aot.json
|
||||
upgrade-phonecat-3-final/rollup-config.js
|
||||
!upgrade-phonecat-*/**/karma.conf.js
|
||||
!upgrade-phonecat-*/**/karma-test-shim.js
|
||||
|
@ -20,7 +20,7 @@ export class AppComponent {
|
||||
movies: IMovie[] = [];
|
||||
showImage = true;
|
||||
title = 'AngularJS to Angular Quick Ref Cookbook';
|
||||
toggleImage(event: UIEvent) {
|
||||
toggleImage(event?: UIEvent) {
|
||||
this.showImage = !this.showImage;
|
||||
this.eventType = (event && event.type) || 'not provided';
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
import { Injectable, Pipe, PipeTransform } from '@angular/core';
|
||||
import { DatePipe } from '@angular/common';
|
||||
|
||||
@Injectable()
|
||||
// #docregion date-pipe
|
||||
@Pipe({name: 'date', pure: true})
|
||||
export class StringSafeDatePipe extends DatePipe implements PipeTransform {
|
||||
transform(value: any, format: string): string {
|
||||
value = typeof value === 'string' ?
|
||||
Date.parse(value) : value;
|
||||
return super.transform(value, format);
|
||||
}
|
||||
}
|
||||
// #enddocregion date-pipe
|
@ -19,6 +19,7 @@ import { HeroListAutoCalcPageComponent } from './hero-list-auto-page.component';
|
||||
import { HeroListAutoComponent } from './hero-list-auto.component';
|
||||
import { HomeComponent } from './home.component';
|
||||
import { AboutComponent } from './about.component';
|
||||
import { InsertRemoveComponent } from './insert-remove.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@ -56,6 +57,7 @@ import { AboutComponent } from './about.component';
|
||||
HeroListAutoCalcPageComponent,
|
||||
HeroListAutoComponent,
|
||||
HomeComponent,
|
||||
InsertRemoveComponent,
|
||||
AboutComponent
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
|
@ -2,7 +2,7 @@
|
||||
<h1>My First Attribute Directive</h1>
|
||||
<!-- #docregion applied -->
|
||||
<p appHighlight>Highlight me!</p>
|
||||
<!-- #enddocregion applied, -->
|
||||
<!-- #enddocregion applied -->
|
||||
|
||||
<!-- #docregion color-1 -->
|
||||
<p appHighlight highlightColor="yellow">Highlighted in yellow</p>
|
||||
|
@ -0,0 +1,3 @@
|
||||
<!-- #docregion unsupported -->
|
||||
<p app:Highlight>This is invalid</p>
|
||||
<!-- #enddocregion unsupported -->
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"e2e": [
|
||||
{
|
||||
"cmd": "yarn",
|
||||
"args": [
|
||||
"e2e",
|
||||
"--no-webdriver-update",
|
||||
"--port={PORT}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -19,20 +19,20 @@ const DifferentParent = Parent;
|
||||
// #enddocregion provide-the-parent
|
||||
// The `parentType` defaults to `Parent` when omitting the second parameter.
|
||||
// #docregion provide-the-parent
|
||||
const provideParent =
|
||||
export function provideParent
|
||||
// #enddocregion provide-parent, provide-the-parent
|
||||
// #docregion provide-parent
|
||||
(component: any, parentType?: any) => {
|
||||
(component: any, parentType?: any) {
|
||||
return { provide: parentType || Parent, useExisting: forwardRef(() => component) };
|
||||
};
|
||||
}
|
||||
// #enddocregion provide-parent
|
||||
|
||||
// Simpler syntax version that always provides the component in the name of `Parent`.
|
||||
const provideTheParent =
|
||||
export function provideTheParent
|
||||
// #docregion provide-the-parent
|
||||
(component: any) => {
|
||||
(component: any) {
|
||||
return { provide: Parent, useExisting: forwardRef(() => component) };
|
||||
};
|
||||
}
|
||||
// #enddocregion provide-the-parent
|
||||
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"e2e": [
|
||||
{
|
||||
"cmd": "yarn",
|
||||
"args": [
|
||||
"e2e",
|
||||
"--no-webdriver-update",
|
||||
"--port={PORT}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -28,10 +28,7 @@ let checkLogForMessage = (message: string) => {
|
||||
expect(page.logList.getText()).toContain(message);
|
||||
};
|
||||
|
||||
// TODO(i): temorarily disable these tests because angular-in-memory-web-api is not compatible with rxjs v6 yet
|
||||
// and we don't have the backwards compatibility package yet.
|
||||
// Reenable after rxjs v6 compatibility package is out or angular-in-memory-web-api is compatible with rxjs v6
|
||||
xdescribe('Http Tests', function() {
|
||||
describe('Http Tests', function() {
|
||||
beforeEach(() => {
|
||||
browser.get('');
|
||||
});
|
||||
|
@ -1,3 +1,13 @@
|
||||
{
|
||||
"projectType": "i18n"
|
||||
"projectType": "i18n",
|
||||
"e2e": [
|
||||
{
|
||||
"cmd": "yarn",
|
||||
"args": [
|
||||
"e2e",
|
||||
"--no-webdriver-update",
|
||||
"--port={PORT}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -50,9 +50,10 @@
|
||||
|
||||
<div (keyup)="0">
|
||||
<h4>Template context: template reference variables (#customerInput):</h4>
|
||||
<label>Type something:
|
||||
<!-- #docregion template-reference-variable -->
|
||||
<input #customerInput>{{customerInput.value}}</label>
|
||||
<label>Type something:
|
||||
<input #customerInput>{{customerInput.value}}
|
||||
</label>
|
||||
<!-- #enddocregion template-reference-variable -->
|
||||
</div>
|
||||
|
||||
|
@ -13,8 +13,8 @@ import { AppComponent } from './app.component';
|
||||
// #enddocregion
|
||||
*/
|
||||
// #docregion
|
||||
import { HighlightDirective } from './highlight.directive';
|
||||
import { TitleComponent } from './title.component';
|
||||
import { HighlightDirective } from './highlight.directive.1';
|
||||
import { TitleComponent } from './title.component.1';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
/* Routing Module */
|
||||
|
@ -1,10 +0,0 @@
|
||||
// #docregion
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({ name: 'awesome' })
|
||||
/** Precede the input string with the word "Awesome " */
|
||||
export class AwesomePipe implements PipeTransform {
|
||||
transform(phrase: string) {
|
||||
return phrase ? 'Awesome ' + phrase : '';
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
// #docplaster
|
||||
// Same directive name and selector as
|
||||
// HighlightDirective in parent AppModule
|
||||
// It selects for both input boxes and 'highlight' attr
|
||||
// and it highlights in blue instead of gold
|
||||
|
||||
// #docregion
|
||||
import { Directive, ElementRef } from '@angular/core';
|
||||
|
||||
// Highlight the host element or any InputElement in blue
|
||||
@Directive({ selector: '[highlight], input' })
|
||||
export class ContactHighlightDirective {
|
||||
constructor(el: ElementRef) {
|
||||
el.nativeElement.style.backgroundColor = 'powderblue';
|
||||
// #enddocregion
|
||||
console.log(`* Contact highlight called for ${el.nativeElement.tagName}`);
|
||||
// #docregion
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
@ -1,14 +0,0 @@
|
||||
// #docregion
|
||||
import { Directive, ElementRef } from '@angular/core';
|
||||
|
||||
// Same directive name and selector as
|
||||
// HighlightDirective in parent AppRootModule
|
||||
// It selects for both input boxes and 'highlight' attr
|
||||
// and it highlights in beige instead of yellow
|
||||
@Directive({ selector: '[highlight]' })
|
||||
export class HighlightDirective {
|
||||
constructor(el: ElementRef) {
|
||||
el.nativeElement.style.backgroundColor = 'beige';
|
||||
console.log(`* Hero highlight called for ${el.nativeElement.tagName}`);
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ import { UserService } from './user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-title',
|
||||
templateUrl: './title.component.html'
|
||||
templateUrl: './title.component.1.html'
|
||||
})
|
||||
export class TitleComponent {
|
||||
title = 'Angular Modules';
|
@ -1,30 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
itemImageUrl = '../assets/lamp.png';
|
||||
isUnchanged = true;
|
||||
classes = 'special';
|
||||
// #docregion parent-data-type
|
||||
parentItem = 'bananas';
|
||||
// #enddocregion parent-data-type
|
||||
|
||||
// #docregion pass-object
|
||||
currentItem = [{
|
||||
id: 21,
|
||||
name: 'peaches'
|
||||
}];
|
||||
// #enddocregion pass-object
|
||||
|
||||
interpolationTitle = 'Interpolation';
|
||||
propertyTitle = 'Property binding';
|
||||
|
||||
// #docregion malicious-content
|
||||
evilTitle = 'Template <script>alert("evil never sleeps")</script>Syntax';
|
||||
// #enddocregion malicious-content
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"server": {
|
||||
"baseDir": "src",
|
||||
"routes": {
|
||||
"/node_modules": "node_modules"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `<h1>Hello {{name}}</h1>`
|
||||
})
|
||||
export class AppComponent { name = 'Angular'; }
|
@ -1,11 +0,0 @@
|
||||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ AppComponent ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
@ -1,31 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular Quickstart</title>
|
||||
<base href="/">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {color:#369;font-family: Arial,Helvetica,sans-serif;}
|
||||
</style>
|
||||
|
||||
<!-- Polyfills -->
|
||||
<!-- #docregion polyfills -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
<!-- #enddocregion polyfills -->
|
||||
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('main.js').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- #docregion my-app-->
|
||||
<my-app>Loading AppComponent content here ...</my-app>
|
||||
<!-- #enddocregion my-app-->
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,5 +0,0 @@
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": [ "es2015", "dom" ],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"description": "QuickStart",
|
||||
"files": [
|
||||
"src/app/app.component.ts",
|
||||
"src/app/app.module.ts",
|
||||
"src/main.ts",
|
||||
"src/index.html"
|
||||
],
|
||||
"file": "src/app/app.component.ts",
|
||||
"tags": ["quickstart"]
|
||||
}
|
@ -4,7 +4,7 @@ import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-crisis-list',
|
||||
templateUrl: './crisis-list.component.html',
|
||||
styleUrls: ['./crisis-list.component.css']
|
||||
templateUrl: './crisis-list.component.1.html',
|
||||
styleUrls: ['./crisis-list.component.1.css']
|
||||
})
|
||||
export class CrisisListComponent { }
|
@ -4,7 +4,7 @@ import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-list',
|
||||
templateUrl: './hero-list.component.html',
|
||||
styleUrls: ['./hero-list.component.css']
|
||||
templateUrl: './hero-list.component.1.html',
|
||||
styleUrls: ['./hero-list.component.1.css']
|
||||
})
|
||||
export class HeroListComponent { }
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"projectType": "service-worker",
|
||||
"e2e": [
|
||||
{"cmd": "yarn", "args": ["e2e", "--no-webdriver-update"]},
|
||||
{"cmd": "yarn", "args": ["e2e", "--no-webdriver-update", "--port={PORT}"]},
|
||||
{"cmd": "yarn", "args": ["build", "--prod"]},
|
||||
{"cmd": "node", "args": ["--eval", "assert(fs.existsSync('./dist/ngsw.json'), 'ngsw.json is missing')"]},
|
||||
{"cmd": "node", "args": ["--eval", "assert(fs.existsSync('./dist/ngsw-worker.js'), 'ngsw-worker.js is missing')"]},
|
||||
|
@ -1,17 +0,0 @@
|
||||
'use strict'; // necessary for es6 output in node
|
||||
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
describe('QuickStart E2E Tests', function () {
|
||||
|
||||
let expectedMsg = 'Hello Angular';
|
||||
|
||||
beforeEach(function () {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
it(`should display: ${expectedMsg}`, function () {
|
||||
expect(element(by.css('h1')).getText()).toEqual(expectedMsg);
|
||||
});
|
||||
|
||||
});
|
@ -152,7 +152,7 @@
|
||||
<div [ngSwitch]="hero?.emotion">
|
||||
<app-happy-hero *ngSwitchCase="'happy'" [hero]="hero"></app-happy-hero>
|
||||
<app-sad-hero *ngSwitchCase="'sad'" [hero]="hero"></app-sad-hero>
|
||||
<app-confused-hero *ngSwitchCase="'app-confused'" [hero]="hero"></app-confused-hero>
|
||||
<app-confused-hero *ngSwitchCase="'confused'" [hero]="hero"></app-confused-hero>
|
||||
<app-unknown-hero *ngSwitchDefault [hero]="hero"></app-unknown-hero>
|
||||
</div>
|
||||
<!-- #enddocregion built-in, ngswitch -->
|
||||
|
@ -1,3 +1,2 @@
|
||||
// #docregion
|
||||
export * from './spinner.component';
|
||||
export * from './spinner.service';
|
||||
|
@ -1,16 +0,0 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
|
||||
import { SpinnerService } from './spinner.service';
|
||||
|
||||
@Component({
|
||||
selector: 'toh-spinner',
|
||||
template: '<div>spinner</div>'
|
||||
})
|
||||
|
||||
export class SpinnerComponent implements OnDestroy, OnInit {
|
||||
constructor(private spinnerService: SpinnerService) { }
|
||||
|
||||
ngOnInit() { }
|
||||
|
||||
ngOnDestroy() { }
|
||||
}
|
@ -1,3 +1,2 @@
|
||||
// #docregion
|
||||
export * from './toast.component';
|
||||
export * from './toast.service';
|
||||
|
@ -1,13 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { ToastService } from './toast.service';
|
||||
|
||||
@Component({
|
||||
selector: 'toh-toast',
|
||||
template: '<div>toast</div>'
|
||||
})
|
||||
export class ToastComponent implements OnInit {
|
||||
constructor(toastService: ToastService) { }
|
||||
|
||||
ngOnInit() { }
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { ToastService } from '../../core';
|
||||
|
||||
@Component({
|
||||
selector: 'toh-toast',
|
||||
template: '<div>toast</div>'
|
||||
})
|
||||
export class ToastComponent implements OnInit {
|
||||
constructor(toastService: ToastService) { }
|
||||
|
||||
ngOnInit() { }
|
||||
}
|
@ -32,8 +32,6 @@
|
||||
height: 56px;
|
||||
padding: 0 16px 0 72px;
|
||||
padding-left: 8px;
|
||||
background-color: #673AB7;
|
||||
background: #0033FF;
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
/*#docregion*/
|
||||
.spinner {
|
||||
position: absolute;
|
||||
left: 7em;
|
||||
top: 20em;
|
||||
position: absolute;
|
||||
|
@ -32,8 +32,6 @@
|
||||
height: 56px;
|
||||
padding: 0 16px 0 72px;
|
||||
padding-left: 8px;
|
||||
background-color: #673AB7;
|
||||
background: #0033FF;
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
|
@ -4,4 +4,7 @@ import { Component } from '@angular/core';
|
||||
selector: 'sg-app',
|
||||
templateUrl: './app.component.html'
|
||||
})
|
||||
export class AppComponent { }
|
||||
export class AppComponent {
|
||||
|
||||
doSomething() {}
|
||||
}
|
||||
|
@ -4,4 +4,7 @@ import { Component } from '@angular/core';
|
||||
selector: 'sg-app',
|
||||
templateUrl: './app.component.html'
|
||||
})
|
||||
export class AppComponent { }
|
||||
export class AppComponent {
|
||||
|
||||
onSavedTheDay(event$: any) { }
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
|
||||
currentHero: Hero;
|
||||
|
||||
deleteHero(hero: Hero) {
|
||||
deleteHero(hero?: Hero) {
|
||||
this.alert(`Delete ${hero ? hero.name : 'the hero'}.`);
|
||||
}
|
||||
|
||||
@ -105,18 +105,18 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
|
||||
get nullHero(): Hero { return null; }
|
||||
|
||||
onClickMe(event: KeyboardEvent) {
|
||||
onClickMe(event?: KeyboardEvent) {
|
||||
let evtMsg = event ? ' Event target class is ' + (<HTMLElement>event.target).className : '';
|
||||
this.alert('Click me.' + evtMsg);
|
||||
}
|
||||
|
||||
onSave(event: KeyboardEvent) {
|
||||
onSave(event?: KeyboardEvent) {
|
||||
let evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).textContent : '';
|
||||
this.alert('Saved.' + evtMsg);
|
||||
if (event) { event.stopPropagation(); }
|
||||
}
|
||||
|
||||
onSubmit() {/* referenced but not used */}
|
||||
onSubmit(data: any) {/* referenced but not used */}
|
||||
|
||||
product = {
|
||||
name: 'frimfram',
|
||||
|
@ -4,6 +4,8 @@ import { interval, of } from 'rxjs';
|
||||
import { delay, take } from 'rxjs/operators';
|
||||
|
||||
describe('Angular async helper', () => {
|
||||
|
||||
describe('async', () => {
|
||||
let actuallyDone = false;
|
||||
|
||||
beforeEach(() => { actuallyDone = false; });
|
||||
@ -45,6 +47,20 @@ describe('Angular async helper', () => {
|
||||
source.subscribe(val => actuallyDone = true, err => fail(err), done);
|
||||
});
|
||||
|
||||
it('should run async test with successful delayed Observable', async(() => {
|
||||
const source = of (true).pipe(delay(10));
|
||||
source.subscribe(val => actuallyDone = true, err => fail(err));
|
||||
}));
|
||||
|
||||
it('should run async test with successful delayed Observable', fakeAsync(() => {
|
||||
const source = of (true).pipe(delay(10));
|
||||
source.subscribe(val => actuallyDone = true, err => fail(err));
|
||||
|
||||
tick(10);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('fakeAsync', () => {
|
||||
// #docregion fake-async-test-tick
|
||||
it('should run timeout callback with delay after call tick with millis', fakeAsync(() => {
|
||||
let called = false;
|
||||
@ -83,6 +99,7 @@ describe('Angular async helper', () => {
|
||||
expect(dateDiff).toBe(2000);
|
||||
}));
|
||||
// #enddocregion fake-async-test-rxjs
|
||||
});
|
||||
|
||||
// #docregion fake-async-test-clock
|
||||
describe('use jasmine.clock()', () => {
|
||||
@ -124,16 +141,4 @@ describe('Angular async helper', () => {
|
||||
});
|
||||
// #enddocregion async-test-promise-then
|
||||
|
||||
it('should run async test with successful delayed Observable', async(() => {
|
||||
const source = of (true).pipe(delay(10));
|
||||
source.subscribe(val => actuallyDone = true, err => fail(err));
|
||||
}));
|
||||
|
||||
it('should run async test with successful delayed Observable', fakeAsync(() => {
|
||||
const source = of (true).pipe(delay(10));
|
||||
source.subscribe(val => actuallyDone = true, err => fail(err));
|
||||
|
||||
tick(10);
|
||||
}));
|
||||
|
||||
});
|
||||
|
@ -11,7 +11,7 @@
|
||||
<!-- #docregion name-input -->
|
||||
<div>
|
||||
<label>name:
|
||||
<input [(ngModel)]="hero.name" placeholder="name">
|
||||
<input [(ngModel)]="hero.name" placeholder="name"/>
|
||||
</label>
|
||||
</div>
|
||||
<!-- #enddocregion name-input -->
|
||||
|
@ -5,6 +5,6 @@
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
<input [(ngModel)]="hero.name" placeholder="name">
|
||||
<input [(ngModel)]="hero.name" placeholder="name"/>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<div><span>id: </span>{{selectedHero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
<input [(ngModel)]="selectedHero.name" placeholder="name">
|
||||
<input [(ngModel)]="selectedHero.name" placeholder="name"/>
|
||||
</label>
|
||||
</div>
|
||||
<!-- #enddocregion selectedHero-details -->
|
||||
|
@ -27,7 +27,7 @@ Here's a simple root NgModule definition.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The `export` property of `AppComponent` is included here for illustration; it isn't actually necessary in this example. A root NgModule has no reason to *export* anything because other modules don't need to *import* the root NgModule.
|
||||
`AppComponent` is included in the `exports` list here for illustration; it isn't actually necessary in this example. A root NgModule has no reason to *export* anything because other modules don't need to *import* the root NgModule.
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -41,6 +41,10 @@ when the user hovers over that element. You can apply it like this:
|
||||
|
||||
{@a write-directive}
|
||||
|
||||
Please note that directives _do not_ support namespaces.
|
||||
|
||||
<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
|
||||
|
||||
Create the directive class file in a terminal window with the CLI command [`ng generate directive`](cli/generate).
|
||||
|
@ -42,7 +42,7 @@ This builder, among other things, ensures that the library is always built with
|
||||
|
||||
To make library code reusable you must define a public API for it. This "user layer" defines what is available to consumers of your library. A user of your library should be able to access public functionality (such as NgModules, service providers and general utility functions) through a single import path.
|
||||
|
||||
The public API for your library is maintained in the `index.ts` file of your library folder.
|
||||
The public API for your library is maintained in the `public-api.ts` file in your library folder.
|
||||
Anything exported from this file is made public when your library is imported into an application.
|
||||
Use an NgModule to expose services and components.
|
||||
|
||||
|
@ -151,7 +151,7 @@ through the injector tree until it reaches the root injector.
|
||||
|
||||
* The first injector configured with a provider supplies the dependency (a service instance or value) to the constructor.
|
||||
|
||||
* If no provider is found in the root injector, the DI framework returns null to the constructor.
|
||||
* If no provider is found in the root injector, the DI framework throws an error.
|
||||
|
||||
There are a number of options for modifying the default search behavior, using _parameter decorators_
|
||||
on the service-valued parameters of a class constructor.
|
||||
@ -262,6 +262,8 @@ Using a custom provider allows you to provide a concrete implementation for impl
|
||||
|
||||
The `factory` function returns the `localStorage` property that is attached to the browser window object. The `Inject` decorator is a constructor parameter used to specify a custom provider of a dependency. This custom provider can now be overridden during testing with a mock API of `localStorage` instead of interactive with real browser APIs.
|
||||
|
||||
{@a skip}
|
||||
|
||||
### Modify the provider search with `@Self` and `@SkipSelf`
|
||||
|
||||
Providers can also be scoped by injector through constructor parameter decorators. The following example overrides the `BROWSER_STORAGE` token in the `Component` class `providers` with the `sessionStorage` browser API. The same `BrowserStorageService` is injected twice in the constructor, decorated with `@Self` and `@SkipSelf` to define which injector handles the provider dependency.
|
||||
@ -784,4 +786,3 @@ If you want to show only one of them, use the directive to make sure __??of what
|
||||
`<hero-overview heroCache></hero-overview>`
|
||||
|
||||
--->
|
||||
|
||||
|
@ -182,8 +182,7 @@ Child modules and component injectors are independent of each other, and create
|
||||
|
||||
Thanks to [injector inheritance](guide/hierarchical-dependency-injection),
|
||||
you can still inject application-wide services into these components.
|
||||
A component's injector is a child of its parent component's injector,
|
||||
and a descendent of its parent's parent's injector, and so on all the way back to the application's _root_ injector. Angular can inject a service provided by any injector in that lineage.
|
||||
A component's injector is a child of its parent component's injector, and inherits from all ancestor injectors all the way back to the application's _root_ injector. Angular can inject a service provided by any injector in that lineage.
|
||||
|
||||
For example, Angular can inject `HeroListComponent` with both the `HeroService` provided in `HeroComponent` and the `UserService` provided in `AppModule`.
|
||||
|
||||
|
@ -149,7 +149,7 @@ 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.ts" linenums="false" region="class">
|
||||
<code-example path="displaying-data/src/app/app-ctor.component.1.ts" linenums="false" region="class">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
@ -42,7 +42,7 @@ 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()` `provides` 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).
|
||||
|
||||
|
@ -476,7 +476,7 @@ That's easy to implement with RxJS operators, as shown in this excerpt.
|
||||
<code-example
|
||||
path="http/src/app/package-search/package-search.component.ts"
|
||||
region="debounce"
|
||||
header="app/package-search/package-search.component.ts (excerpt))">
|
||||
header="app/package-search/package-search.component.ts (excerpt)">
|
||||
</code-example>
|
||||
|
||||
The `searchText$` is the sequence of search-box values coming from the user.
|
||||
@ -865,6 +865,9 @@ with the `reportProgress` option set true to enable tracking of progress events.
|
||||
|
||||
Every progress event triggers change detection, so only turn them on if you truly intend to report progress in the UI.
|
||||
|
||||
When using [`HttpClient#request()`](api/common/http/HttpClient#request) with an HTTP method, configure with
|
||||
[`observe: 'events'`](api/common/http/HttpClient#request) to see all events, including the progress of transfers.
|
||||
|
||||
</div>
|
||||
|
||||
Next, pass this request object to the `HttpClient.request()` method, which
|
||||
|
@ -896,11 +896,11 @@ Replace the contents of each component with the sample HTML below.
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane header="src/app/crisis-list/crisis-list.component.html" path="router/src/app/crisis-list/crisis-list.component.html">
|
||||
<code-pane header="src/app/crisis-list/crisis-list.component.html" path="router/src/app/crisis-list/crisis-list.component.1.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane header="src/app/hero-list/hero-list.component.html" path="router/src/app/hero-list/hero-list.component.html" region="template">
|
||||
<code-pane header="src/app/hero-list/hero-list.component.html" path="router/src/app/hero-list/hero-list.component.1.html" region="template">
|
||||
|
||||
</code-pane>
|
||||
|
||||
@ -972,7 +972,7 @@ 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.html" linenums="false" header="src/app/hero-list/hero-list.component.html (excerpt)">
|
||||
<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>
|
||||
|
||||
@ -1239,11 +1239,11 @@ Here are the files discussed in this milestone.
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane header="hero-list/hero-list.component.html" path="router/src/app/hero-list/hero-list.component.html">
|
||||
<code-pane header="hero-list/hero-list.component.html" path="router/src/app/hero-list/hero-list.component.1.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane header="crisis-list/crisis-list.component.html" path="router/src/app/crisis-list/crisis-list.component.html">
|
||||
<code-pane header="crisis-list/crisis-list.component.html" path="router/src/app/crisis-list/crisis-list.component.1.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
@ -2242,10 +2242,10 @@ This file does the following:
|
||||
|
||||
You could also create more transitions for other routes. This trigger is sufficient for the current milestone.
|
||||
|
||||
Back in the `AppComponent`, import the `RouterOutlet` token from the `@angular/router` package and the `slideInDownAnimation` from
|
||||
Back in the `AppComponent`, import the `RouterOutlet` token from the `@angular/router` package and the `slideInAnimation` from
|
||||
`'./animations.ts`.
|
||||
|
||||
Add an `animations` array to the `@Component` metadata's that contains the `slideInDownAnimation`.
|
||||
Add an `animations` array to the `@Component` metadata's that contains the `slideInAnimation`.
|
||||
|
||||
<code-example path="router/src/app/app.component.2.ts" linenums="false" header="src/app/app.component.ts (animations)" region="animation-imports">
|
||||
|
||||
|
@ -50,6 +50,9 @@ script will never be registered with the browser.
|
||||
You can avoid that by waiting for the app to stabilize first, before starting to poll for updates
|
||||
(as shown in the example above).
|
||||
|
||||
Note that this is true for any kind of polling done by your application.
|
||||
Check the {@link ApplicationRef#isStable isStable} documentation for more information.
|
||||
|
||||
</div>
|
||||
|
||||
### Forcing update activation
|
||||
|
@ -301,7 +301,7 @@ an administrator ever needs to deactivate the service worker quickly.
|
||||
### Fail-safe
|
||||
|
||||
To deactivate the service worker, remove or rename the
|
||||
`ngsw-config.json` file. When the service worker's request
|
||||
`ngsw.json` file. When the service worker's request
|
||||
for `ngsw.json` returns a `404`, then the service worker
|
||||
removes all of its caches and de-registers itself,
|
||||
essentially self-destructing.
|
||||
|
@ -23,7 +23,7 @@ Just run the [`ng test`](cli/test) CLI command:
|
||||
</code-example>
|
||||
|
||||
The `ng test` command builds the app in _watch mode_,
|
||||
and launches the [karma test runner](https://karma-runner.github.io).
|
||||
and launches the [Karma test runner](https://karma-runner.github.io).
|
||||
|
||||
The console output looks a bit like this:
|
||||
|
||||
@ -55,15 +55,15 @@ The tests run again, the browser refreshes, and the new test results appear.
|
||||
|
||||
#### Configuration
|
||||
|
||||
The CLI takes care of Jasmine and karma configuration for you.
|
||||
The CLI takes care of Jasmine and Karma configuration for you.
|
||||
|
||||
You can fine-tune many options by editing the `karma.conf.js` and
|
||||
the `test.ts` files in the `src/` folder.
|
||||
|
||||
The `karma.conf.js` file is a partial karma configuration file.
|
||||
The `karma.conf.js` file is a partial Karma configuration file.
|
||||
The CLI constructs the full runtime configuration in memory, based on application structure specified in the `angular.json` file, supplemented by `karma.conf.js`.
|
||||
|
||||
Search the web for more details about Jasmine and karma configuration.
|
||||
Search the web for more details about Jasmine and Karma configuration.
|
||||
|
||||
#### Other test frameworks
|
||||
|
||||
@ -428,7 +428,7 @@ to extract the setup variables that it needs.
|
||||
Many developers feel this approach is cleaner and more explicit than the
|
||||
traditional `beforeEach()` style.
|
||||
|
||||
Although this testing guide follows the tradition style and
|
||||
Although this testing guide follows the traditional style and
|
||||
the default [CLI schematics](https://github.com/angular/angular-cli)
|
||||
generate test files with `beforeEach()` and `TestBed`,
|
||||
feel free to adopt _this alternative approach_ in your own projects.
|
||||
@ -2650,10 +2650,10 @@ It takes two arguments: the component type to override (`HeroDetailComponent`) a
|
||||
The [override metadata object](#metadata-override-object) is a generic defined as follows:
|
||||
|
||||
<code-example format="." language="javascript">
|
||||
type MetadataOverride<T> = {
|
||||
add?: Partial<T>;
|
||||
remove?: Partial<T>;
|
||||
set?: Partial<T>;
|
||||
type MetadataOverride<T> = {
|
||||
add?: Partial<T>;
|
||||
remove?: Partial<T>;
|
||||
set?: Partial<T>;
|
||||
};
|
||||
</code-example>
|
||||
|
||||
@ -2816,7 +2816,7 @@ Consider adding component tests such as this one:
|
||||
|
||||
Debug specs in the browser in the same way that you debug an application.
|
||||
|
||||
1. Reveal the karma browser window (hidden earlier).
|
||||
1. Reveal the Karma browser window (hidden earlier).
|
||||
1. Click the **DEBUG** button; it opens a new browser tab and re-runs the tests.
|
||||
1. Open the browser's “Developer Tools” (`Ctrl-Shift-I` on windows; `Command-Option-I` in OSX).
|
||||
1. Pick the "sources" section.
|
||||
@ -3008,10 +3008,10 @@ appropriate to the method, that is, the parameter of an `@NgModule`,
|
||||
`@Component`, `@Directive`, or `@Pipe`.
|
||||
|
||||
<code-example format="." language="javascript">
|
||||
type MetadataOverride<T> = {
|
||||
add?: Partial<T>;
|
||||
remove?: Partial<T>;
|
||||
set?: Partial<T>;
|
||||
type MetadataOverride<T> = {
|
||||
add?: Partial<T>;
|
||||
remove?: Partial<T>;
|
||||
set?: Partial<T>;
|
||||
};
|
||||
</code-example>
|
||||
|
||||
|
@ -34,7 +34,21 @@ For details about `tsconfig.json`, see the official
|
||||
|
||||
The [Setup](guide/setup) guide uses the following `tsconfig.json`:
|
||||
|
||||
<code-example path="quickstart/src/tsconfig.1.json" header="tsconfig.json" linenums="false"></code-example>
|
||||
<code-example lang="json" header="tsconfig.json" linenums="false">
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": [ "es2015", "dom" ],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
}
|
||||
}
|
||||
</code-example>
|
||||
|
||||
This file contains options and flags that are essential for Angular applications.
|
||||
|
||||
|
@ -1,17 +1,17 @@
|
||||
# Angular Universal: server-side rendering
|
||||
# Server-side Rendering (SSR): An intro to Angular Universal
|
||||
|
||||
This guide describes **Angular Universal**, a technology that runs your Angular application on the server.
|
||||
This guide describes **Angular Universal**, a technology that renders Angular applications on the server.
|
||||
|
||||
A normal Angular application executes in the _browser_, rendering pages in the DOM in response to user actions.
|
||||
Angular Universal generates _static_ application pages on the _server_
|
||||
through a process called _server-side rendering_ (SSR).
|
||||
When Universal is integrated with your app, it can generate and serve those pages in response to requests from browsers.
|
||||
It can also pre-generate pages as HTML files that you serve later.
|
||||
Angular Universal executes on the _server_, generating _static_ application pages that later get bootstrapped on
|
||||
the client. This means that the application generally renders more quickly, giving users a chance to view the application
|
||||
layout before it becomes fully interactive.
|
||||
|
||||
You can easily prepare an app for server-side rendering using the [Angular CLI](guide/glossary#cli). The CLI schematic `@nguniversal/express-engine` performs the required steps, as described below.
|
||||
For a more detailed look at different techniques and concepts surrounding SSR, please check out this
|
||||
[article](https://developers.google.com/web/updates/2019/02/rendering-on-the-web).
|
||||
|
||||
This guide describes a Universal sample application that launches quickly as a server-rendered page.
|
||||
Meanwhile, the browser downloads the full client version and switches to it automatically after the code loads.
|
||||
You can easily prepare an app for server-side rendering using the [Angular CLI](guide/glossary#cli).
|
||||
The CLI schematic `@nguniversal/express-engine` performs the required steps, as described below.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -20,471 +20,16 @@ Meanwhile, the browser downloads the full client version and switches to it auto
|
||||
|
||||
</div>
|
||||
|
||||
{@a why-do-it}
|
||||
|
||||
## Why use server-side rendering?
|
||||
|
||||
There are three main reasons to create a Universal version of your app.
|
||||
|
||||
1. Facilitate web crawlers (SEO)
|
||||
1. Improve performance on mobile and low-powered devices
|
||||
1. Show the first page quickly
|
||||
|
||||
{@a seo}
|
||||
{@a web-crawlers}
|
||||
### Facilitate web crawlers
|
||||
|
||||
Google, Bing, Facebook, Twitter, and other social media sites rely on web crawlers to index your application content and make that content searchable on the web.
|
||||
These web crawlers may be unable to navigate and index your highly interactive Angular application as a human user could do.
|
||||
|
||||
Angular Universal can generate a static version of your app that is easily searchable, linkable, and navigable without JavaScript.
|
||||
Universal also makes a site preview available since each URL returns a fully rendered page.
|
||||
|
||||
Enabling web crawlers is often referred to as
|
||||
[search engine optimization (SEO)](https://static.googleusercontent.com/media/www.google.com/en//webmasters/docs/search-engine-optimization-starter-guide.pdf).
|
||||
|
||||
{@a no-javascript}
|
||||
|
||||
### Improve performance on mobile and low-powered devices
|
||||
|
||||
Some devices don't support JavaScript or execute JavaScript so poorly that the user experience is unacceptable.
|
||||
For these cases, you may require a server-rendered, no-JavaScript version of the app.
|
||||
This version, however limited, may be the only practical alternative for
|
||||
people who otherwise couldn't use the app at all.
|
||||
|
||||
{@a startup-performance}
|
||||
|
||||
### Show the first page quickly
|
||||
|
||||
Displaying the first page quickly can be critical for user engagement.
|
||||
[53 percent of mobile site visits are abandoned](https://www.thinkwithgoogle.com/marketing-resources/data-measurement/mobile-page-speed-new-industry-benchmarks/) if pages take longer than 3 seconds to load.
|
||||
Your app may have to launch faster to engage these users before they decide to do something else.
|
||||
|
||||
With Angular Universal, you can generate landing pages for the app that look like the complete app.
|
||||
The pages are pure HTML, and can display even if JavaScript is disabled.
|
||||
The pages don't handle browser events, but they _do_ support navigation through the site using [`routerLink`](guide/router#router-link).
|
||||
|
||||
In practice, you'll serve a static version of the landing page to hold the user's attention.
|
||||
At the same time, you'll load the full Angular app behind it.
|
||||
The user perceives near-instant performance from the landing page
|
||||
and gets the full interactive experience after the full app loads.
|
||||
|
||||
{@a how-does-it-work}
|
||||
|
||||
## Universal web servers
|
||||
|
||||
A Universal web server responds to application page requests with static HTML rendered by the [Universal template engine](#universal-engine).
|
||||
The server receives and responds to HTTP requests from clients (usually browsers), and serves static assets such as scripts, CSS, and images.
|
||||
It may respond to data requests, either directly or as a proxy to a separate data server.
|
||||
|
||||
The sample web server for this guide is based on the popular [Express](https://expressjs.com/) framework.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
**Note:** _Any_ web server technology can serve a Universal app as long as it can call Universal's `renderModuleFactory()` function.
|
||||
The principles and decision points discussed here apply to any web server technology.
|
||||
|
||||
</div>
|
||||
|
||||
To make a Universal app, install the `platform-server` package, which provides server implementations
|
||||
of the DOM, `XMLHttpRequest`, and other low-level features that don't rely on a browser.
|
||||
Compile the client application with the `platform-server` module (instead of the `platform-browser` module)
|
||||
and run the resulting Universal app on a web server.
|
||||
|
||||
The server ([Node Express](https://expressjs.com/) in this guide's example)
|
||||
passes client requests for application pages to Universal's `renderModuleFactory()` function.
|
||||
|
||||
The `renderModuleFactory()` function takes as inputs a *template* HTML page (usually `index.html`),
|
||||
an Angular *module* containing components,
|
||||
and a *route* that determines which components to display.
|
||||
The route comes from the client's request to the server.
|
||||
|
||||
Each request results in the appropriate view for the requested route.
|
||||
The `renderModuleFactory()` function renders the view within the `<app>` tag of the template,
|
||||
creating a finished HTML page for the client.
|
||||
|
||||
Finally, the server returns the rendered page to the client.
|
||||
|
||||
{@a summary}
|
||||
## Preparing for server-side rendering
|
||||
|
||||
Before your app can be rendered on a server, you must make changes in the app itself, and also set up the server.
|
||||
|
||||
1. Install dependencies.
|
||||
1. Prepare your app by modifying both the app code and its configuration.
|
||||
1. Add a build target, and build a Universal bundle using the CLI with the `@nguniversal/express-engine` schematic.
|
||||
1. Set up a server to run Universal bundles.
|
||||
1. Pack and run the app on the server.
|
||||
|
||||
The following sections go into each of these main steps in more detail.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
**Note:** The [Universal tutorial](#the-example) below walks you through the steps using the Tour of Heroes sample app, and goes into more detail about what you can do and why you might want to do it.
|
||||
|
||||
To see a working version of an app with server-side rendering, clone the [Angular Universal starter](https://github.com/angular/universal-starter).
|
||||
|
||||
</div>
|
||||
|
||||
<div class="callout is-critical">
|
||||
|
||||
<header>Security for server requests</header>
|
||||
|
||||
HTTP requests issued from a browser app aren't the same as those issued by the Universal app on the server.
|
||||
Universal HTTP requests have different security requirements
|
||||
|
||||
When a browser makes an HTTP request, the server can make assumptions about cookies, XSRF headers, and so on.
|
||||
For example, the browser automatically sends authentication cookies for the current user.
|
||||
Angular Universal can't forward these credentials to a separate data server.
|
||||
If your server handles HTTP requests, you'll have to add your own security plumbing.
|
||||
|
||||
</div>
|
||||
|
||||
## Step 1: Install dependencies
|
||||
|
||||
Install `@angular/platform-server` into your project. Use the same version as the other `@angular` packages in your project. You also need `ts-loader`, `webpack-cli` for your webpack build and `@nguniversal/module-map-ngfactory-loader` to handle lazy-loading in the context of a server-render.
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
$ npm install --save @angular/platform-server @nguniversal/module-map-ngfactory-loader ts-loader webpack-cli
|
||||
</code-example>
|
||||
|
||||
## Step 2: Prepare your app
|
||||
|
||||
To prepare your app for Universal rendering, take the following steps:
|
||||
|
||||
* Add Universal support to your app.
|
||||
|
||||
* Create a server root module.
|
||||
|
||||
* Create a main file to export the server root module.
|
||||
|
||||
* Configure the server root module.
|
||||
|
||||
### 2a. Add Universal support to your app
|
||||
|
||||
Make your `AppModule` compatible with Universal by adding `.withServerTransition()` and an application ID to your `BrowserModule` import in `src/app/app.module.ts`.
|
||||
|
||||
<code-example format="." language="typescript" linenums="false">
|
||||
@NgModule({
|
||||
bootstrap: [AppComponent],
|
||||
imports: [
|
||||
// Add .withServerTransition() to support Universal rendering.
|
||||
// The application ID can be any identifier which is unique on
|
||||
// the page.
|
||||
BrowserModule.withServerTransition({appId: 'my-app'}),
|
||||
...
|
||||
],
|
||||
|
||||
})
|
||||
export class AppModule {}
|
||||
</code-example>
|
||||
|
||||
### 2b. Create a server root module
|
||||
|
||||
Create a module named `AppServerModule` to act as the root module when running on the server. This example places it alongside `app.module.ts` in a file named `app.server.module.ts`. The new module imports everything from the root `AppModule`, and adds `ServerModule`. It also adds `ModuleMapLoaderModule` to help make lazy-loaded routes possible during server-side renders with the Angular CLI.
|
||||
|
||||
Here's an example in `src/app/app.server.module.ts`.
|
||||
|
||||
<code-example format="." language="typescript" linenums="false">
|
||||
import {NgModule} from '@angular/core';
|
||||
import {ServerModule} from '@angular/platform-server';
|
||||
import {ModuleMapLoaderModule} from '@nguniversal/module-map-ngfactory-loader';
|
||||
|
||||
import {AppModule} from './app.module';
|
||||
import {AppComponent} from './app.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
// The AppServerModule should import your AppModule followed
|
||||
// by the ServerModule from @angular/platform-server.
|
||||
AppModule,
|
||||
ServerModule,
|
||||
ModuleMapLoaderModule // <-- *Important* to have lazy-loaded routes work
|
||||
],
|
||||
// Since the bootstrapped component is not inherited from your
|
||||
// imported AppModule, it needs to be repeated here.
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppServerModule {}
|
||||
</code-example>
|
||||
|
||||
### 2c. Create a main file to export AppServerModule
|
||||
|
||||
Create a main file for your Universal bundle in the app `src/` folder to export your `AppServerModule` instance. This example calls the file `main.server.ts`.
|
||||
|
||||
<code-example format="." language="typescript" linenums="false">
|
||||
export { AppServerModule } from './app/app.server.module';
|
||||
</code-example>
|
||||
|
||||
### 2d. Create a configuration file for AppServerModule
|
||||
|
||||
Copy `tsconfig.app.json` to `tsconfig.server.json` and modify it as follows:
|
||||
|
||||
* In `"compilerOptions"`, set the `"module"` target to `"commonjs"`.
|
||||
* Add a section for `"angularCompilerOptions"` and set `"entryModule"` to point to your `AppServerModule` instance. Use the format `importPath#symbolName`. In this example, the entry module is `app/app.server.module#AppServerModule`.
|
||||
|
||||
<code-example format="." language="none" linenums="false">
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"baseUrl": "./",
|
||||
// Set the module format to "commonjs":
|
||||
"module": "commonjs",
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"test.ts",
|
||||
"**/*.spec.ts"
|
||||
],
|
||||
// Add "angularCompilerOptions" with the AppServerModule you wrote
|
||||
// set as the "entryModule".
|
||||
"angularCompilerOptions": {
|
||||
"entryModule": "app/app.server.module#AppServerModule"
|
||||
}
|
||||
}
|
||||
</code-example>
|
||||
|
||||
## Step 3: Create a new build target and build the bundle
|
||||
|
||||
Open the Angular configuration file, `angular.json`, for your project, and add a new target in the `"architect"` section for the server build. The following example names the new target `"server"`.
|
||||
|
||||
<code-example format="." language="none" linenums="false">
|
||||
"architect": {
|
||||
"build": { ... }
|
||||
"server": {
|
||||
"builder": "@angular-devkit/build-angular:server",
|
||||
"options": {
|
||||
"outputPath": "dist/my-project-server",
|
||||
"main": "src/main.server.ts",
|
||||
"tsConfig": "src/tsconfig.server.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
</code-example>
|
||||
|
||||
To build a server bundle for your application, use the `ng run` command, with the format `projectName#serverTarget`. In our example, there are now two targets configured, `"build"` and `"server"`.
|
||||
|
||||
<code-example format="." language="none" linenums="false">
|
||||
# This builds your project using the server target, and places the output
|
||||
# in dist/my-project-server/
|
||||
$ ng run my-project:server
|
||||
|
||||
Date: 2017-07-24T22:42:09.739Z
|
||||
Hash: 9cac7d8e9434007fd8da
|
||||
Time: 4933ms
|
||||
chunk {0} main.js (main) 9.49 kB [entry] [rendered]
|
||||
chunk {1} styles.css (styles) 0 bytes [entry] [rendered]
|
||||
</code-example>
|
||||
|
||||
## Step 4: Set up a server to run Universal bundles
|
||||
|
||||
To run a Universal bundle, you need to send it to a server.
|
||||
|
||||
The following example passes `AppServerModule` (compiled with AoT) to the `PlatformServer` method `renderModuleFactory()`, which serializes the app and returns the result to the browser.
|
||||
|
||||
<code-example format="." language="typescript" linenums="false">
|
||||
app.engine('html', (_, options, callback) => {
|
||||
renderModuleFactory(AppServerModuleNgFactory, {
|
||||
// Our index.html
|
||||
document: template,
|
||||
url: options.req.url,
|
||||
// configure DI to make lazy-loading work differently
|
||||
// (we need to instantly render the view)
|
||||
extraProviders: [
|
||||
provideModuleMap(LAZY_MODULE_MAP)
|
||||
]
|
||||
}).then(html => {
|
||||
callback(null, html);
|
||||
});
|
||||
});
|
||||
</code-example>
|
||||
|
||||
This technique gives you complete flexibility. For convenience, you can also use the `@nguniversal/express-engine` tool that has some built-in features.
|
||||
|
||||
<code-example format="." language="typescript" linenums="false">
|
||||
import { ngExpressEngine } from '@nguniversal/express-engine';
|
||||
|
||||
app.engine('html', ngExpressEngine({
|
||||
bootstrap: AppServerModuleNgFactory,
|
||||
providers: [
|
||||
provideModuleMap(LAZY_MODULE_MAP)
|
||||
]
|
||||
}));
|
||||
</code-example>
|
||||
|
||||
The following simple example implements a bare-bones Node Express server to fire everything up.
|
||||
(Note that this is for demonstration only. In a real production environment, you need to set up additional authentication and security.)
|
||||
|
||||
At the root level of your project, next to `package.json`, create a file named `server.ts` and add the following content.
|
||||
|
||||
<code-example format="." language="typescript" linenums="false">
|
||||
// These are important and needed before anything else
|
||||
import 'zone.js/dist/zone-node';
|
||||
import 'reflect-metadata';
|
||||
|
||||
import { renderModuleFactory } from '@angular/platform-server';
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
|
||||
|
||||
import * as express from 'express';
|
||||
import { join } from 'path';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
// Faster server renders w/ Prod mode (dev mode never needed)
|
||||
enableProdMode();
|
||||
|
||||
// Express server
|
||||
const app = express();
|
||||
|
||||
const PORT = process.env.PORT || 4000;
|
||||
const DIST_FOLDER = join(process.cwd(), 'dist');
|
||||
|
||||
// Our index.html we'll use as our template
|
||||
const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).toString();
|
||||
|
||||
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./server/main');
|
||||
|
||||
|
||||
app.engine('html', (_, options, callback) => {
|
||||
renderModuleFactory(AppServerModuleNgFactory, {
|
||||
// Our index.html
|
||||
document: template,
|
||||
url: options.req.url,
|
||||
// DI so that we can get lazy-loading to work differently (since we need it to just instantly render it)
|
||||
extraProviders: [
|
||||
provideModuleMap(LAZY_MODULE_MAP)
|
||||
]
|
||||
}).then(html => {
|
||||
callback(null, html);
|
||||
});
|
||||
});
|
||||
|
||||
app.set('view engine', 'html');
|
||||
app.set('views', join(DIST_FOLDER, 'browser'));
|
||||
|
||||
// Server static files from /browser
|
||||
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));
|
||||
|
||||
// All regular routes use the Universal engine
|
||||
app.get('*', (req, res) => {
|
||||
res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req });
|
||||
});
|
||||
|
||||
// Start up the Node server
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Node server listening on http://localhost:${PORT}`);
|
||||
});
|
||||
</code-example>
|
||||
|
||||
## Step 5: Pack and run the app on the server
|
||||
|
||||
Set up a webpack configuration to handle the Node Express `server.ts` file and serve your application.
|
||||
|
||||
In your app root directory, create a webpack configuration file (`webpack.server.config.js`) that compiles the `server.ts` file and its dependencies into `dist/server.js`.
|
||||
|
||||
<code-example format="." language="typescript" linenums="false">
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
entry: { server: './server.ts' },
|
||||
resolve: { extensions: ['.js', '.ts'] },
|
||||
target: 'node',
|
||||
// this makes sure we include node_modules and other 3rd party libraries
|
||||
externals: [/(node_modules|main\..*\.js)/],
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.ts$/, loader: 'ts-loader' }
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
// Temporary Fix for issue: https://github.com/angular/angular/issues/11580
|
||||
// for "WARNING Critical dependency: the request of a dependency is an expression"
|
||||
new webpack.ContextReplacementPlugin(
|
||||
/(.+)?angular(\\|\/)core(.+)?/,
|
||||
path.join(__dirname, 'src'), // location of your src
|
||||
{} // a map of your routes
|
||||
),
|
||||
new webpack.ContextReplacementPlugin(
|
||||
/(.+)?express(\\|\/)(.+)?/,
|
||||
path.join(__dirname, 'src'),
|
||||
{}
|
||||
)
|
||||
]
|
||||
}
|
||||
</code-example>
|
||||
|
||||
The project's `dist/` folder now contains both browser and server folders.
|
||||
|
||||
<code-example format="." language="none" linenums="false">
|
||||
dist/
|
||||
browser/
|
||||
server/
|
||||
</code-example>
|
||||
|
||||
To run the app on the server, type the following in a command shell.
|
||||
|
||||
<code-example format="." language="bash" linenums="false">
|
||||
node dist/server.js
|
||||
</code-example>
|
||||
|
||||
### Creating scripts
|
||||
|
||||
Now let's create a few handy scripts to help us do all of this in the future.
|
||||
You can add these in the `"scripts"` section of the `package.json`.
|
||||
|
||||
<code-example format="." language="none" linenums="false">
|
||||
"scripts": {
|
||||
"build:ssr": "npm run build:client-and-server-bundles && npm run webpack:server",
|
||||
"serve:ssr": "node dist/server.js",
|
||||
"build:client-and-server-bundles": "ng build --prod && ng run my-project:server:production",
|
||||
"webpack:server": "webpack --config webpack.server.config.js --progress --colors",
|
||||
...
|
||||
}
|
||||
</code-example>
|
||||
|
||||
To run a production build of your app with Universal on your local system, use the following command.
|
||||
|
||||
<code-example format="." language="bash" linenums="false">
|
||||
npm run build:ssr && npm run serve:ssr
|
||||
</code-example>
|
||||
|
||||
### Working around the browser APIs
|
||||
|
||||
Because a Universal `platform-server` app doesn't execute in the browser, you may have to work around some of the browser APIs and capabilities that are missing on the server.
|
||||
|
||||
For example, your server-side page can't reference browser-only native objects such as `window`, `document`, `navigator`, or `location`.
|
||||
If you don't need these on the server-rendered page, you can side-step them with conditional logic.
|
||||
Alternatively, you can find an injectable Angular abstraction over the object you need such as `Location` or `Document`;
|
||||
it may substitute adequately for the specific API that you're calling.
|
||||
If Angular doesn't provide it, you can write your own abstraction that delegates to the browser API while in the browser and to a satisfactory alternative implementation while on the server.
|
||||
|
||||
Similarly, without mouse or keyboard events, a server-side app can't rely on a user clicking a button to show a component.
|
||||
The app must determine what to render based solely on the incoming client request.
|
||||
This is a good argument for making the app [routable](guide/router).
|
||||
|
||||
Because the user of a server-rendered page can't do much more than click links,
|
||||
you should swap in the real client app as quickly as possible for a proper interactive experience.
|
||||
|
||||
{@a the-example}
|
||||
|
||||
## Universal tutorial
|
||||
|
||||
The [Tour of Heroes tutorial](tutorial) is the foundation for this walkthrough.
|
||||
|
||||
The core application files are mostly untouched, with a few exceptions described below.
|
||||
You'll add more files to support building and serving with Universal.
|
||||
|
||||
In this example, the Angular CLI compiles and bundles the Universal version of the app with the
|
||||
[Ahead-of-Time (AoT) compiler](guide/aot-compiler).
|
||||
A Node Express web server turns client requests into the HTML pages rendered by Universal.
|
||||
A Node Express web server compiles HTML pages with Universal based on client requests.
|
||||
|
||||
To create server-side app module, `app.server.module.ts`, run the following CLI command.
|
||||
To create the server-side app module, `app.server.module.ts`, run the following CLI command.
|
||||
|
||||
<code-example format="." language="bash">
|
||||
|
||||
@ -512,34 +57,203 @@ webpack.server.config.js <i>* webpack server configuration</i>
|
||||
</code-example>
|
||||
|
||||
The files marked with `*` are new and not in the original tutorial sample.
|
||||
This guide covers them in the sections below.
|
||||
|
||||
### Universal in action
|
||||
|
||||
To start rendering your app with Universal on your local system, use the following command.
|
||||
|
||||
<code-example format="." language="bash" linenums="false">
|
||||
npm run build:ssr && npm run serve:ssr
|
||||
</code-example>
|
||||
|
||||
Open a browser and navigate to http://localhost:4000/.
|
||||
You should see the familiar Tour of Heroes dashboard page.
|
||||
|
||||
Navigation via `routerLinks` works correctly because they use the native anchor (`<a>`) tags.
|
||||
You can go from the Dashboard to the Heroes page and back.
|
||||
You can click a hero on the Dashboard page to display its Details page.
|
||||
|
||||
If you throttle your network speed so that the client-side scripts take longer to download (instructions below),
|
||||
you'll notice:
|
||||
* Clicking a hero on the Heroes page does nothing.
|
||||
* You can't add or delete a hero.
|
||||
* The search box on the Dashboard page is ignored.
|
||||
* The *Back* and *Save* buttons on the Details page don't work.
|
||||
|
||||
User events other than `routerLink` clicks aren't supported.
|
||||
You must wait for the full client app to bootstrap and run, or buffer the events using libraries like
|
||||
[preboot](https://github.com/angular/preboot), which allow you to replay these events once the client-side scripts load.
|
||||
|
||||
The transition from the server-rendered app to the client app happens quickly on a development machine, but you should
|
||||
always test your apps in real-world scenarios.
|
||||
|
||||
You can simulate a slower network to see the transition more clearly as follows:
|
||||
|
||||
1. Open the Chrome Dev Tools and go to the Network tab.
|
||||
1. Find the [Network Throttling](https://developers.google.com/web/tools/chrome-devtools/network-performance/reference#throttling)
|
||||
dropdown on the far right of the menu bar.
|
||||
1. Try one of the "3G" speeds.
|
||||
|
||||
The server-rendered app still launches quickly but the full client app may take seconds to load.
|
||||
|
||||
{@a why-do-it}
|
||||
## Why use server-side rendering?
|
||||
|
||||
There are three main reasons to create a Universal version of your app.
|
||||
|
||||
1. Facilitate web crawlers through [search engine optimization (SEO)](https://static.googleusercontent.com/media/www.google.com/en//webmasters/docs/search-engine-optimization-starter-guide.pdf)
|
||||
1. Improve performance on mobile and low-powered devices
|
||||
1. Show the first page quickly with a [first-contentful paint (FCP)](https://developers.google.com/web/tools/lighthouse/audits/first-contentful-paint)
|
||||
|
||||
{@a seo}
|
||||
{@a web-crawlers}
|
||||
### Facilitate web crawlers (SEO)
|
||||
|
||||
Google, Bing, Facebook, Twitter, and other social media sites rely on web crawlers to index your application content and
|
||||
make that content searchable on the web.
|
||||
These web crawlers may be unable to navigate and index your highly interactive Angular application as a human user could do.
|
||||
|
||||
Angular Universal can generate a static version of your app that is easily searchable, linkable, and navigable without JavaScript.
|
||||
Universal also makes a site preview available since each URL returns a fully rendered page.
|
||||
|
||||
{@a no-javascript}
|
||||
### Improve performance on mobile and low-powered devices
|
||||
|
||||
Some devices don't support JavaScript or execute JavaScript so poorly that the user experience is unacceptable.
|
||||
For these cases, you may require a server-rendered, no-JavaScript version of the app.
|
||||
This version, however limited, may be the only practical alternative for
|
||||
people who otherwise couldn't use the app at all.
|
||||
|
||||
{@a startup-performance}
|
||||
### Show the first page quickly
|
||||
|
||||
Displaying the first page quickly can be critical for user engagement.
|
||||
[53 percent of mobile site visits are abandoned](https://www.thinkwithgoogle.com/marketing-resources/data-measurement/mobile-page-speed-new-industry-benchmarks/)
|
||||
if pages take longer than 3 seconds to load.
|
||||
Your app may have to launch faster to engage these users before they decide to do something else.
|
||||
|
||||
With Angular Universal, you can generate landing pages for the app that look like the complete app.
|
||||
The pages are pure HTML, and can display even if JavaScript is disabled.
|
||||
The pages don't handle browser events, but they _do_ support navigation through the site using [`routerLink`](guide/router#router-link).
|
||||
|
||||
In practice, you'll serve a static version of the landing page to hold the user's attention.
|
||||
At the same time, you'll load the full Angular app behind it.
|
||||
The user perceives near-instant performance from the landing page
|
||||
and gets the full interactive experience after the full app loads.
|
||||
|
||||
{@a how-does-it-work}
|
||||
## Universal web servers
|
||||
|
||||
A Universal web server responds to application page requests with static HTML rendered by the [Universal template engine](#universal-engine).
|
||||
The server receives and responds to HTTP requests from clients (usually browsers), and serves static assets such as scripts, CSS, and images.
|
||||
It may respond to data requests, either directly or as a proxy to a separate data server.
|
||||
|
||||
The sample web server for this guide is based on the popular [Express](https://expressjs.com/) framework.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
**Note:** _Any_ web server technology can serve a Universal app as long as it can call Universal's `renderModuleFactory()` function.
|
||||
The principles and decision points discussed here apply to any web server technology.
|
||||
|
||||
</div>
|
||||
|
||||
Universal applications use the Angular `platform-server` package (as opposed to `platform-browser`), which provides
|
||||
server implementations of the DOM, `XMLHttpRequest`, and other low-level features that don't rely on a browser.
|
||||
|
||||
The server ([Node Express](https://expressjs.com/) in this guide's example)
|
||||
passes client requests for application pages to the NgUniversal `ngExpressEngine`. Under the hood, this
|
||||
calls Universal's `renderModuleFactory()` function, while providing caching and other helpful utilities.
|
||||
|
||||
The `renderModuleFactory()` function takes as inputs a *template* HTML page (usually `index.html`),
|
||||
an Angular *module* containing components,
|
||||
and a *route* that determines which components to display.
|
||||
The route comes from the client's request to the server.
|
||||
|
||||
Each request results in the appropriate view for the requested route.
|
||||
The `renderModuleFactory()` function renders the view within the `<app>` tag of the template,
|
||||
creating a finished HTML page for the client.
|
||||
|
||||
Finally, the server returns the rendered page to the client.
|
||||
|
||||
### Working around the browser APIs
|
||||
|
||||
Because a Universal app doesn't execute in the browser, some of the browser APIs and capabilities may be missing on the server.
|
||||
|
||||
For example, server-side applications can't reference browser-only global objects such as `window`, `document`, `navigator`, or `location`.
|
||||
|
||||
Angular provides some injectable abstractions over these objects, such as [`Location`](api/common/Location)
|
||||
or [`DOCUMENT`](api/common/DOCUMENT); it may substitute adequately for these APIs.
|
||||
If Angular doesn't provide it, it's possible to write new abstractions that delegate to the browser APIs while in the browser
|
||||
and to an alternative implementation while on the server (aka shimming).
|
||||
|
||||
Similarly, without mouse or keyboard events, a server-side app can't rely on a user clicking a button to show a component.
|
||||
The app must determine what to render based solely on the incoming client request.
|
||||
This is a good argument for making the app [routable](guide/router).
|
||||
|
||||
{@a http-urls}
|
||||
|
||||
### Using absolute URLs for server requests
|
||||
|
||||
The tutorial's `HeroService` and `HeroSearchService` delegate to the Angular `HttpClient` module to fetch application data.
|
||||
These services send requests to _relative_ URLs such as `api/heroes`.
|
||||
In a Universal app, HTTP URLs must be _absolute_ (for example, `https://my-server.com/api/heroes`) even when the Universal web server is capable of handling relative requests.
|
||||
This means you need to change your services to make requests with absolute URLs when running on the server and with relative URLs when running in the browser.
|
||||
In a Universal app, HTTP URLs must be _absolute_ (for example, `https://my-server.com/api/heroes`).
|
||||
This means you need to change your services to make requests with absolute URLs when running on the server and with relative
|
||||
URLs when running in the browser.
|
||||
|
||||
One solution is to provide the server's runtime origin under Angular's [`APP_BASE_HREF`](api/common/APP_BASE_HREF) token,
|
||||
inject it into the service, and prepend the origin to the request URL.
|
||||
One solution is to provide the full URL to your application on the server, and write an interceptor that can retrieve this
|
||||
value and prepend it to the request URL. If you're using the `ngExpressEngine`, as shown in the example in this guide, half
|
||||
the work is already done. We'll assume this is the case, but it's trivial to provide the same functionality.
|
||||
|
||||
Start by changing the `HeroService` constructor to take a second `origin` parameter that is optionally injected via the `APP_BASE_HREF` token.
|
||||
Start by creating an [HttpInterceptor](api/common/http/HttpInterceptor):
|
||||
|
||||
<code-example format="." language="typescript">
|
||||
|
||||
import {Injectable, Inject, Optional} from '@angular/core';
|
||||
import {HttpInterceptor, HttpHandler, HttpRequest, HttpHeaders} from '@angular/common/http';
|
||||
import {Request} from 'express';
|
||||
import {REQUEST} from '@nguniversal/express-engine/tokens';
|
||||
|
||||
@Injectable()
|
||||
export class UniversalInterceptor implements HttpInterceptor {
|
||||
|
||||
constructor(@Optional() @Inject(REQUEST) protected request: Request) {}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler) {
|
||||
let serverReq: HttpRequest<any> = req;
|
||||
if (this.request) {
|
||||
let newUrl = `${this.request.protocol}://${this.request.get('host')}`;
|
||||
if (!req.url.startsWith('/')) {
|
||||
newUrl += '/';
|
||||
}
|
||||
newUrl += req.url;
|
||||
serverReq = req.clone({url: newUrl});
|
||||
}
|
||||
return next.handle(serverReq);
|
||||
}
|
||||
}
|
||||
|
||||
<code-example path="universal/src/app/hero.service.ts" region="ctor" header="src/app/hero.service.ts (constructor with optional origin)">
|
||||
</code-example>
|
||||
|
||||
The constructor uses the `@Optional()` directive to prepend the origin to `heroesUrl` _if it exists_.
|
||||
You don't provide `APP_BASE_HREF` in the browser version, so `heroesUrl` remains relative.
|
||||
Next, provide the interceptor in the providers for the server `AppModule` (app.server.module.ts):
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<code-example format="." language="typescript">
|
||||
|
||||
**Note:** You can ignore `APP_BASE_HREF` in the browser if you've specified `<base href="/">` in the `index.html` file to satisfy the router's need for a base address (as the tutorial sample does).
|
||||
import {HTTP_INTERCEPTORS} from '@angular/common/http';
|
||||
import {UniversalInterceptor} from './universal-interceptor';
|
||||
|
||||
</div>
|
||||
@NgModule({
|
||||
...
|
||||
providers: [{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
useClass: UniversalInterceptor,
|
||||
multi: true
|
||||
}],
|
||||
})
|
||||
export class AppServerModule {}
|
||||
|
||||
</code-example>
|
||||
|
||||
Now, on every HTTP request made on the server, this interceptor will fire and replace the request URL with the absolute
|
||||
URL provided in the Express `Request` object.
|
||||
|
||||
{@a universal-engine}
|
||||
### Universal template engine
|
||||
@ -549,30 +263,35 @@ The important bit in the `server.ts` file is the `ngExpressEngine()` function.
|
||||
<code-example path="universal/server.ts" header="server.ts" region="ngExpressEngine">
|
||||
</code-example>
|
||||
|
||||
The `ngExpressEngine()` function is a wrapper around Universal's `renderModuleFactory()` function which turns a client's requests into server-rendered HTML pages.
|
||||
You'll call that function within a _template engine_ that's appropriate for your server stack.
|
||||
The `ngExpressEngine()` function is a wrapper around Universal's `renderModuleFactory()` function which turns a client's
|
||||
requests into server-rendered HTML pages.
|
||||
|
||||
* The first parameter is `AppServerModule`.
|
||||
It's the bridge between the Universal server-side renderer and your application.
|
||||
It's the bridge between the Universal server-side renderer and the Angular application.
|
||||
|
||||
* The second parameter, `extraProviders`, is optional. It lets you specify dependency providers that apply only when running on this server.
|
||||
* The second parameter, `extraProviders`, is optional. It lets you specify dependency providers that apply only when
|
||||
running on this server.
|
||||
You can do this when your app needs information that can only be determined by the currently running server instance.
|
||||
The required information in this case is the running server's *origin*, provided under the `APP_BASE_HREF` token, so that the app can [calculate absolute HTTP URLs](#http-urls).
|
||||
One example could be the running server's *origin*, which could be used to [calculate absolute HTTP URLs](#http-urls) if
|
||||
not using the `Request` token as shown above.
|
||||
|
||||
The `ngExpressEngine()` function returns a `Promise` callback that resolves to the rendered page.
|
||||
It's up to your engine to decide what to do with that page.
|
||||
It's up to the engine to decide what to do with that page.
|
||||
This engine's `Promise` callback returns the rendered page to the web server,
|
||||
which then forwards it to the client in the HTTP response.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
**Note:** These wrappers help hide the complexity of the `renderModuleFactory()` function. There are more wrappers for different backend technologies
|
||||
at the [Universal repository](https://github.com/angular/universal).
|
||||
**Note:** These wrappers help hide the complexity of the `renderModuleFactory()` function. There are more wrappers
|
||||
for different backend technologies at the [Universal repository](https://github.com/angular/universal).
|
||||
|
||||
</div>
|
||||
|
||||
### Filtering request URLs
|
||||
|
||||
NOTE: the basic behavior described below is handled automatically when using the NgUniversal Express schematic, this
|
||||
is helpful when trying to understand the underlying behavior or replicate it without using the schematic.
|
||||
|
||||
The web server must distinguish _app page requests_ from other kinds of requests.
|
||||
|
||||
It's not as simple as intercepting a request to the root address `/`.
|
||||
@ -586,11 +305,11 @@ All static asset requests have a file extension (such as `main.js` or `/node_mod
|
||||
|
||||
Because we use routing, we can easily recognize the three types of requests and handle them differently.
|
||||
|
||||
1. Data request - request URL that begins `/api`.
|
||||
2. App navigation - request URL with no file extension.
|
||||
3. Static asset - all other requests.
|
||||
1. **Data request**: request URL that begins `/api`.
|
||||
1. **App navigation**: request URL with no file extension.
|
||||
1. **Static asset**: all other requests.
|
||||
|
||||
A Node Express server is a pipeline of middleware that filters and processes URL requests one after the other.
|
||||
A Node Express server is a pipeline of middleware that filters and processes requests one after the other.
|
||||
You configure the Node Express server pipeline with calls to `app.get()` like this one for data requests.
|
||||
|
||||
<code-example path="universal/server.ts" header="server.ts (data URL)" region="data-request" linenums="false">
|
||||
@ -616,41 +335,11 @@ The following code filters for request URLs with no extensions and treats them a
|
||||
A single `app.use()` treats all other URLs as requests for static assets
|
||||
such as JavaScript, image, and style files.
|
||||
|
||||
To ensure that clients can only download the files that they are permitted to see, put all client-facing asset files in the `/dist` folder and only honor requests for files from the `/dist` folder.
|
||||
To ensure that clients can only download the files that they are permitted to see, put all client-facing asset files in
|
||||
the `/dist` folder and only honor requests for files from the `/dist` folder.
|
||||
|
||||
The following Node Express code routes all remaining requests to `/dist`, and returns a `404 - NOT FOUND` error if the file isn't found.
|
||||
The following Node Express code routes all remaining requests to `/dist`, and returns a `404 - NOT FOUND` error if the
|
||||
file isn't found.
|
||||
|
||||
<code-example path="universal/server.ts" header="server.ts (static files)" region="static" linenums="false">
|
||||
</code-example>
|
||||
|
||||
|
||||
### Universal in action
|
||||
|
||||
Open a browser to http://localhost:4000/.
|
||||
You should see the familiar Tour of Heroes dashboard page.
|
||||
|
||||
Navigation via `routerLinks` works correctly.
|
||||
You can go from the Dashboard to the Heroes page and back.
|
||||
You can click a hero on the Dashboard page to display its Details page.
|
||||
|
||||
Notice, however, that clicks, mouse-moves, and keyboard entries are inert.
|
||||
|
||||
* Clicking a hero on the Heroes page does nothing.
|
||||
* You can't add or delete a hero.
|
||||
* The search box on the Dashboard page is ignored.
|
||||
* The *Back* and *Save* buttons on the Details page don't work.
|
||||
|
||||
User events other than `routerLink` clicks aren't supported.
|
||||
You must wait for the full client app to arrive.
|
||||
It won't arrive until you compile the client app
|
||||
and move the output into the `dist/` folder.
|
||||
|
||||
The transition from the server-rendered app to the client app happens quickly on a development machine.
|
||||
You can simulate a slower network to see the transition more clearly and
|
||||
better appreciate the launch-speed advantage of a Universal app running on a low-powered, poorly connected device.
|
||||
|
||||
Open the Chrome Dev Tools and go to the Network tab.
|
||||
Find the [Network Throttling](https://developers.google.com/web/tools/chrome-devtools/network-performance/reference#throttling) dropdown on the far right of the menu bar.
|
||||
|
||||
Try one of the "3G" speeds.
|
||||
The server-rendered app still launches quickly but the full client app may take seconds to load.
|
||||
|
@ -17,9 +17,6 @@ For an example, see [Angular Material](https://material.angular.io/) docs.
|
||||
|
||||
Library packages often include typings in `.d.ts` files; see examples in `node_modules/@angular/material`. If your library's package does not include typings and your IDE complains, you may need to install the library's associated `@types/<lib_name>` package.
|
||||
|
||||
To configure a library that does not include typings in the same package, install the related `@types` package with npm.
|
||||
TypeScript looks for types in the `node_modules/@types` folder by default, so you don't have to add each type package individually.
|
||||
|
||||
For example, suppose you have a library named `d3`:
|
||||
|
||||
<code-example format="." language="bash">
|
||||
@ -27,15 +24,8 @@ npm install d3 --save
|
||||
npm install @types/d3 --save-dev
|
||||
</code-example>
|
||||
|
||||
Types defined in the library need to be added to the TypeScript configuration for the project that uses it.
|
||||
|
||||
* Add the library to the "types" array in `src/tsconfig.app.json`.
|
||||
|
||||
```
|
||||
"types":[
|
||||
"d3"
|
||||
]
|
||||
```
|
||||
Types defined in a `@types/` package for a library installed into the workspace are automatically added to the TypeScript configuration for the project that uses that library.
|
||||
TypeScript looks for types in the `node_modules/@types` folder by default, so you don't have to add each type package individually.
|
||||
|
||||
If a library doesn't have typings available at `@types/`, you can still use it by manually adding typings for it.
|
||||
To do this:
|
||||
|
BIN
aio/content/images/bios/keilla.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
aio/content/images/bios/leonardo.jpg
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
aio/content/images/bios/mira.jpg
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
aio/content/images/bios/samjulien.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
aio/content/images/bios/sharon.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
aio/content/images/bios/tomastrajan.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 1.7 KiB |