Compare commits
195 Commits
6.0.0-beta
...
5.2x
Author | SHA1 | Date | |
---|---|---|---|
5c89d6bffa | |||
3e6a86fb0a | |||
a7ebf5aadd | |||
b42921bbd2 | |||
722dec11b0 | |||
9e6268ba59 | |||
435f6eecd2 | |||
1c1cbba04b | |||
3b692a55a7 | |||
69a0578e00 | |||
b5ca275590 | |||
519f022b02 | |||
236a9320df | |||
28ac24444f | |||
99909bbf2c | |||
ee60bb5b36 | |||
f6120c09e7 | |||
e2bdef4cf6 | |||
8115edc82f | |||
a8b5465e24 | |||
9ce495b3d8 | |||
d40263447d | |||
31c5c1060a | |||
c9ebd60435 | |||
5a14e2238f | |||
3ceee99e22 | |||
28b23f954c | |||
ad17e5e791 | |||
c30d329faa | |||
991300b86c | |||
7c45db3a19 | |||
67cf11d071 | |||
49082d7ab2 | |||
6b627f67db | |||
5c320b4c2a | |||
ac2b04a5ab | |||
a63b764b54 | |||
2654357c72 | |||
4ec40c6ab2 | |||
80d424798e | |||
7fa2d4b503 | |||
f4845fae12 | |||
f693be3996 | |||
a73d5308e0 | |||
e1bf067090 | |||
884de18cba | |||
dfa2fb95d5 | |||
2639b4bffb | |||
978f97cc59 | |||
f1a063298e | |||
d241532488 | |||
f755db78dc | |||
5dd2b5135d | |||
7ac34e42a0 | |||
029dbf0e18 | |||
bba65e0f41 | |||
a069e08354 | |||
03d93c96a3 | |||
020338230f | |||
a1d86daa71 | |||
7078fbffb4 | |||
0aa9b46b79 | |||
831592c381 | |||
f628797d91 | |||
47f51c2ead | |||
ba9cd5bbc4 | |||
b54ad053f9 | |||
5b8eb9c5c7 | |||
0b683123d2 | |||
363498b6b4 | |||
a1bb56f739 | |||
5bb9fcad3e | |||
f4697f351e | |||
1d571b299d | |||
3a0b5a928c | |||
265ac8a106 | |||
fa7d8907d0 | |||
0220ce7002 | |||
3bd0b2ab28 | |||
a589ca0adb | |||
72f8abd7b3 | |||
20a900b648 | |||
6435ecd3c6 | |||
16d1700a8e | |||
b75cf3f70b | |||
4f19491fec | |||
8f36fd1374 | |||
2de0d4c1db | |||
5e4af7c550 | |||
8ec21fc325 | |||
eb48750705 | |||
be59c3a98c | |||
b333919722 | |||
235a235fab | |||
2d5e7d1b52 | |||
647b8595d0 | |||
0a1a397cd7 | |||
7f9b1b78f6 | |||
1e9484673d | |||
88bec238ac | |||
62e7b9da1e | |||
61341b2791 | |||
92a5876f51 | |||
a57df4ee20 | |||
92d7060cb0 | |||
7e9b120452 | |||
b081dfe705 | |||
4a4d749710 | |||
c878d55397 | |||
263a2eca88 | |||
44154e71fd | |||
0b2f7d13d0 | |||
420cc7afc6 | |||
5fc77c90cb | |||
c3484450b8 | |||
fbef94a8ee | |||
aa456edafc | |||
7007f51c35 | |||
bc1e22922a | |||
cf8d512e43 | |||
0b1f5d2127 | |||
dcf64a0d01 | |||
a9545aba4d | |||
d9ae70c699 | |||
a751649c8d | |||
3f5a3d6ea1 | |||
10a014d89e | |||
8feb8e5408 | |||
16dada28f5 | |||
67cf7128ae | |||
16e5b866d2 | |||
83d43ac850 | |||
cd25939be9 | |||
b58c3527e9 | |||
efc67ee5ef | |||
7a406a3896 | |||
98001a065d | |||
e442881ead | |||
b37cee36f9 | |||
e56de1025a | |||
64ae6d206e | |||
54a14312d1 | |||
7e95802cc1 | |||
e3e7044d06 | |||
eb3bfc25be | |||
94d769de71 | |||
66191e8a37 | |||
bec188506c | |||
4f869ff755 | |||
8f6047340e | |||
9744a1c966 | |||
0bcfae7cac | |||
140e7c00d1 | |||
941e88ff79 | |||
71ea931df5 | |||
545fdf10e2 | |||
7e928db204 | |||
cd4c0eab94 | |||
5b06069fd9 | |||
d0f3162e84 | |||
81537cb161 | |||
370ab66c4f | |||
2707012181 | |||
4d62be69c5 | |||
7e51e52f55 | |||
e81606c97a | |||
f791e9f081 | |||
3aa7e0228a | |||
9d3326caa7 | |||
1940b18124 | |||
0846784b98 | |||
0d10b9002e | |||
0c9ec37e26 | |||
9a0700f5bd | |||
ae7bc2238d | |||
5df626bbe1 | |||
5a624fa1be | |||
3a86940ca5 | |||
7b120b5f73 | |||
de25d1886e | |||
d77444b88a | |||
240aed29e0 | |||
bf29936af9 | |||
339ca83f9d | |||
447783e575 | |||
743d8bc845 | |||
f816666ede | |||
d3c2aa5f95 | |||
3cc1d76ee7 | |||
124283982b | |||
65cf1add97 | |||
8b14488827 | |||
f9fa157a09 | |||
eb8ddd2983 | |||
1aa2947f70 |
25
.circleci/bazel.rc
Normal file
25
.circleci/bazel.rc
Normal file
@ -0,0 +1,25 @@
|
||||
# These options are enabled when running on CI
|
||||
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
|
||||
# See remote cache documentation in /docs/BAZEL.md
|
||||
|
||||
# Don't be spammy in the logs
|
||||
build --noshow_progress
|
||||
|
||||
# Don't run manual tests
|
||||
test --test_tag_filters=-manual
|
||||
|
||||
# Enable experimental CircleCI bazel remote cache proxy
|
||||
# See remote cache documentation in /docs/BAZEL.md
|
||||
build --experimental_remote_spawn_cache --remote_rest_cache=http://localhost:7643
|
||||
|
||||
# Prevent unstable environment variables from tainting cache keys
|
||||
build --experimental_strict_action_env
|
||||
|
||||
# Workaround https://github.com/bazelbuild/bazel/issues/3645
|
||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||
# Limit Bazel to consuming resources that fit in CircleCI "medium" class which is the default:
|
||||
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
|
||||
build --local_resources=3072,2.0,1.0
|
||||
|
||||
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
|
||||
test --flaky_test_attempts=2
|
@ -15,6 +15,13 @@
|
||||
var_1: &docker_image angular/ngcontainer:0.1.0
|
||||
var_2: &cache_key angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.1.0
|
||||
|
||||
# See remote cache documentation in /docs/BAZEL.md
|
||||
var_3: &setup-bazel-remote-cache
|
||||
run:
|
||||
name: Start up bazel remote cache proxy
|
||||
command: ~/bazel-remote-proxy -backend circleci://
|
||||
background: true
|
||||
|
||||
# Settings common to each job
|
||||
anchor_1: &job_defaults
|
||||
working_directory: ~/ng
|
||||
@ -34,14 +41,16 @@ jobs:
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
# Check BUILD.bazel formatting before we have a node_modules directory
|
||||
# Then we don't need any exclude pattern to avoid checking those files
|
||||
- run: 'buildifier -mode=check $(find . -type f \( -name BUILD.bazel -or -name BUILD \)) ||
|
||||
(echo "BUILD files not formatted. Please run ''yarn buildifier''" ; exit 1)'
|
||||
# Run the skylark linter to check our Bazel rules
|
||||
- run: 'find . -type f -name "*.bzl" |
|
||||
xargs java -jar /usr/local/bin/Skylint_deploy.jar ||
|
||||
# See remote cache documentation in /docs/BAZEL.md
|
||||
- run: .circleci/setup_cache.sh
|
||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
- *setup-bazel-remote-cache
|
||||
|
||||
- run: 'yarn buildifier -mode=check ||
|
||||
(echo -e "\nBUILD files not formatted. Please run ''yarn buildifier''" ; exit 1)'
|
||||
- run: 'yarn skylint ||
|
||||
(echo -e "\n.bzl files have lint errors. Please run ''yarn skylint''"; exit 1)'
|
||||
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
|
||||
@ -54,6 +63,11 @@ jobs:
|
||||
steps:
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
# See remote cache documentation in /docs/BAZEL.md
|
||||
- run: .circleci/setup_cache.sh
|
||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
- *setup-bazel-remote-cache
|
||||
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
|
||||
@ -62,7 +76,16 @@ jobs:
|
||||
# Use bazel query so that we explicitly ask for all buildable targets to be built as well
|
||||
# This avoids waiting for a build command to finish before running the first test
|
||||
# See https://github.com/bazelbuild/bazel/issues/4257
|
||||
- run: bazel query --output=label '//modules/... union //packages/... union //tools/...' | xargs bazel test --config=ci
|
||||
- run: bazel query --output=label '//modules/... union //packages/... union //tools/...' | xargs bazel test
|
||||
|
||||
# CircleCI will allow us to go back and view/download these artifacts from past builds.
|
||||
# Also we can use a service like https://buildsize.org/ to automatically track binary size of these artifacts.
|
||||
- store_artifacts:
|
||||
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js
|
||||
destination: packages/core/test/bundling/hello_world/bundle.min.js
|
||||
- store_artifacts:
|
||||
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js.brotli
|
||||
destination: packages/core/test/bundling/hello_world/bundle.min.js.brotli
|
||||
|
||||
- save_cache:
|
||||
key: *cache_key
|
||||
|
11
.circleci/setup_cache.sh
Executable file
11
.circleci/setup_cache.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
# Install bazel remote cache proxy
|
||||
# This is temporary until the feature is no longer experimental on CircleCI.
|
||||
# See remote cache documentation in /docs/BAZEL.md
|
||||
|
||||
set -u -e
|
||||
|
||||
readonly DOWNLOAD_URL="https://5-116431813-gh.circle-artifacts.com/0/pkg/bazel-remote-proxy-$(uname -s)_$(uname -m)"
|
||||
|
||||
curl --fail -o ~/bazel-remote-proxy "$DOWNLOAD_URL"
|
||||
chmod +x ~/bazel-remote-proxy
|
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@ -25,7 +25,7 @@ ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION.
|
||||
## Minimal reproduction of the problem with instructions
|
||||
<!--
|
||||
For bug reports please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
|
||||
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
|
||||
https://stackblitz.com or similar (you can use this template as a starting point: https://stackblitz.com/fork/angular-gitter).
|
||||
-->
|
||||
|
||||
## What is the motivation / use case for changing the behavior?
|
||||
|
42
.github/angular-robot.yml
vendored
42
.github/angular-robot.yml
vendored
@ -13,6 +13,30 @@ merge:
|
||||
# text to show when some checks are failing
|
||||
failureText: "The following checks are failing:"
|
||||
|
||||
# the g3 status will be added to your pull requests if they include files that match the patterns
|
||||
g3Status:
|
||||
# set to true to disable
|
||||
disabled: false
|
||||
# the name of the status
|
||||
context: "google3"
|
||||
# text to show when the status is pending
|
||||
pendingDesc: "Googler: test this change in google3 http://go/angular-g3sync"
|
||||
# text to show when the status is success
|
||||
successDesc: "Does not affect google3"
|
||||
# list of patterns to check for the files changed by the PR
|
||||
# this list must be manually kept in sync with google3/third_party/javascript/angular2/copy.bara.sky
|
||||
include:
|
||||
- "BUILD.bazel"
|
||||
- "LICENSE"
|
||||
- "WORKSPACE"
|
||||
- "modules/**"
|
||||
- "packages/**"
|
||||
# list of patterns to ignore for the files changed by the PR
|
||||
exclude:
|
||||
- "packages/language-service/**"
|
||||
- "**/.gitignore"
|
||||
- "**/.gitkeep"
|
||||
|
||||
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
|
||||
mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.
|
||||
\nPlease help to unblock it by resolving these conflicts. Thanks!"
|
||||
@ -26,13 +50,15 @@ merge:
|
||||
noConflict: true
|
||||
# list of labels that a PR needs to have, checked with a regexp (e.g. "PR target:" will work for the label "PR target: master")
|
||||
requiredLabels:
|
||||
- "PR target:"
|
||||
- "PR target: *"
|
||||
- "cla: yes"
|
||||
|
||||
# list of labels that a PR shouldn't have, checked after the required labels with a regexp
|
||||
forbiddenLabels:
|
||||
- "PR target: TBD"
|
||||
- "PR action: cleanup"
|
||||
- "PR action: review"
|
||||
- "PR state: blocked"
|
||||
- "cla: no"
|
||||
|
||||
# list of PR statuses that need to be successful
|
||||
@ -60,9 +86,15 @@ triage:
|
||||
triagedLabels:
|
||||
-
|
||||
- "type: bug"
|
||||
- "severity"
|
||||
- "freq"
|
||||
- "comp:"
|
||||
- "severity*"
|
||||
- "freq*"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: feature"
|
||||
- "comp:"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: refactor"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: RFC / Discussion / question"
|
||||
- "comp: *"
|
||||
|
@ -44,6 +44,7 @@ groups:
|
||||
all:
|
||||
users: all
|
||||
required: 1
|
||||
rejection_value: -999
|
||||
# In this group, your self-approval does not count
|
||||
author_approval:
|
||||
auto: false
|
||||
|
@ -56,7 +56,6 @@ env:
|
||||
- CI_MODE=aio
|
||||
- CI_MODE=aio_e2e AIO_SHARD=0
|
||||
- CI_MODE=aio_e2e AIO_SHARD=1
|
||||
- CI_MODE=bazel
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
@ -32,6 +32,7 @@ filegroup(
|
||||
"reflect-metadata",
|
||||
"source-map-support",
|
||||
"minimist",
|
||||
"tslib",
|
||||
] for ext in [
|
||||
"*.js",
|
||||
"*.json",
|
||||
|
96
CHANGELOG.md
96
CHANGELOG.md
@ -1,3 +1,99 @@
|
||||
<a name="6.0.0-beta.4"></a>
|
||||
# [6.0.0-beta.4](https://github.com/angular/angular/compare/6.0.0-beta.3...6.0.0-beta.4) (2018-02-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** allow TS to read ambient typings ([#21876](https://github.com/angular/angular/issues/21876)) ([b081dfe](https://github.com/angular/angular/commit/b081dfe)), closes [#21872](https://github.com/angular/angular/issues/21872)
|
||||
* **bazel:** improve error message for missing assets ([#22096](https://github.com/angular/angular/issues/22096)) ([dcf64a0](https://github.com/angular/angular/commit/dcf64a0)), closes [#22095](https://github.com/angular/angular/issues/22095)
|
||||
* **common:** add locale currency values ([#21783](https://github.com/angular/angular/issues/21783)) ([420cc7a](https://github.com/angular/angular/commit/420cc7a)), closes [#20385](https://github.com/angular/angular/issues/20385)
|
||||
* **common:** round currencies based on decimal digits in `CurrencyPipe` ([#21783](https://github.com/angular/angular/issues/21783)) ([44154e7](https://github.com/angular/angular/commit/44154e7)), closes [#10189](https://github.com/angular/angular/issues/10189)
|
||||
* **common:** weaken AsyncPipe transform signature ([#22169](https://github.com/angular/angular/issues/22169)) ([be59c3a](https://github.com/angular/angular/commit/be59c3a))
|
||||
* **compiler:** make unary plus operator consistent to JavaScript ([#22154](https://github.com/angular/angular/issues/22154)) ([72f8abd](https://github.com/angular/angular/commit/72f8abd)), closes [#22089](https://github.com/angular/angular/issues/22089)
|
||||
* **core:** add stacktrace in log when error during cleanup component in TestBed ([#22162](https://github.com/angular/angular/issues/22162)) ([16d1700](https://github.com/angular/angular/commit/16d1700))
|
||||
* **core:** ensure initial value of QueryList length ([#21980](https://github.com/angular/angular/issues/21980)) ([#21982](https://github.com/angular/angular/issues/21982)) ([e56de10](https://github.com/angular/angular/commit/e56de10)), closes [#21980](https://github.com/angular/angular/issues/21980)
|
||||
* **core:** use appropriate inert document strategy for Firefox & Safari ([#17019](https://github.com/angular/angular/issues/17019)) ([a751649](https://github.com/angular/angular/commit/a751649))
|
||||
* **forms:** make Validators.email support optional controls ([#20869](https://github.com/angular/angular/issues/20869)) ([140e7c0](https://github.com/angular/angular/commit/140e7c0))
|
||||
* **forms:** prevent event emission on enable/disable when emitEvent is false ([#12366](https://github.com/angular/angular/issues/12366)) ([#21018](https://github.com/angular/angular/issues/21018)) ([0bcfae7](https://github.com/angular/angular/commit/0bcfae7))
|
||||
* **forms:** set state before emitting a value from ngModelChange ([#21514](https://github.com/angular/angular/issues/21514)) ([9744a1c](https://github.com/angular/angular/commit/9744a1c)), closes [#21513](https://github.com/angular/angular/issues/21513)
|
||||
* **language-service:** correct instructions to install the language service ([#22000](https://github.com/angular/angular/issues/22000)) ([b37cee3](https://github.com/angular/angular/commit/b37cee3))
|
||||
* **platform-browser:** add @Injectable where it was missing ([#22005](https://github.com/angular/angular/issues/22005)) ([0a1a397](https://github.com/angular/angular/commit/0a1a397))
|
||||
* **platform-browser:** support 0/false/null values in transfer_state ([#22179](https://github.com/angular/angular/issues/22179)) ([6435ecd](https://github.com/angular/angular/commit/6435ecd))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **bazel:** allow explicit specification of factories ([#22003](https://github.com/angular/angular/issues/22003)) ([e442881](https://github.com/angular/angular/commit/e442881))
|
||||
* **compiler:** mark @NgModules in provider lists for identification at runtime ([#22005](https://github.com/angular/angular/issues/22005)) ([2d5e7d1](https://github.com/angular/angular/commit/2d5e7d1))
|
||||
* **forms:** multiple validators for array method ([#20766](https://github.com/angular/angular/issues/20766)) ([941e88f](https://github.com/angular/angular/commit/941e88f)), closes [#20665](https://github.com/angular/angular/issues/20665)
|
||||
* change @Injectable() to support tree-shakeable tokens ([#22005](https://github.com/angular/angular/issues/22005)) ([235a235](https://github.com/angular/angular/commit/235a235))
|
||||
|
||||
|
||||
|
||||
<a name="5.2.5"></a>
|
||||
## [5.2.5](https://github.com/angular/angular/compare/5.2.4...5.2.5) (2018-02-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **aio:** update Firebase redirects and SW routes ([#21763](https://github.com/angular/angular/pull/21763)) ([#22104](https://github.com/angular/angular/pull/22104)) ([15ff7ba](https://github.com/angular/angular/commit/15ff7ba)), closes [#21377](https://github.com/angular/angular/issues/21377)
|
||||
* **bazel:** allow TS to read ambient typings ([#21876](https://github.com/angular/angular/issues/21876)) ([d57fd0b](https://github.com/angular/angular/commit/d57fd0b)), closes [#21872](https://github.com/angular/angular/issues/21872)
|
||||
* **bazel:** improve error message for missing assets ([#22096](https://github.com/angular/angular/issues/22096)) ([c5ec8d9](https://github.com/angular/angular/commit/c5ec8d9)), closes [#22095](https://github.com/angular/angular/issues/22095)
|
||||
* **common:** weaken AsyncPipe transform signature ([#22169](https://github.com/angular/angular/issues/22169)) ([c6bdc83](https://github.com/angular/angular/commit/c6bdc83))
|
||||
* **compiler:** make unary plus operator consistent to JavaScript ([#22154](https://github.com/angular/angular/issues/22154)) ([1b8ea10](https://github.com/angular/angular/commit/1b8ea10)), closes [#22089](https://github.com/angular/angular/issues/22089)
|
||||
* **core:** add stacktrace in log when error during cleanup component in TestBed ([#22162](https://github.com/angular/angular/issues/22162)) ([c4f841f](https://github.com/angular/angular/commit/c4f841f))
|
||||
* **core:** ensure initial value of QueryList length ([#21980](https://github.com/angular/angular/issues/21980)) ([#21982](https://github.com/angular/angular/issues/21982)) ([47b73fd](https://github.com/angular/angular/commit/47b73fd)), closes [#21980](https://github.com/angular/angular/issues/21980)
|
||||
* **core:** use appropriate inert document strategy for Firefox & Safari ([#17019](https://github.com/angular/angular/issues/17019)) ([47b71d9](https://github.com/angular/angular/commit/47b71d9))
|
||||
* **forms:** prevent event emission on enable/disable when emitEvent is false ([#12366](https://github.com/angular/angular/issues/12366)) ([#21018](https://github.com/angular/angular/issues/21018)) ([56b9591](https://github.com/angular/angular/commit/56b9591))
|
||||
* **language-service:** correct instructions to install the language service ([#22000](https://github.com/angular/angular/issues/22000)) ([0b23573](https://github.com/angular/angular/commit/0b23573))
|
||||
* **platform-browser:** support 0/false/null values in transfer_state ([#22179](https://github.com/angular/angular/issues/22179)) ([da6ab91](https://github.com/angular/angular/commit/da6ab91))
|
||||
|
||||
|
||||
|
||||
<a name="6.0.0-beta.3"></a>
|
||||
# [6.0.0-beta.3](https://github.com/angular/angular/compare/6.0.0-beta.2...6.0.0-beta.3) (2018-02-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** don't convert null to a string when flushing a mock request ([#21417](https://github.com/angular/angular/issues/21417)) ([8b14488](https://github.com/angular/angular/commit/8b14488)), closes [#20744](https://github.com/angular/angular/issues/20744)
|
||||
* **core:** fix [#20582](https://github.com/angular/angular/issues/20582), don't need to wrap zone in location change listener ([#20640](https://github.com/angular/angular/issues/20640)) ([f791e9f](https://github.com/angular/angular/commit/f791e9f))
|
||||
* **core:** fix proper propagation of subscriptions in EventEmitter ([#22016](https://github.com/angular/angular/issues/22016)) ([e81606c](https://github.com/angular/angular/commit/e81606c)), closes [#21999](https://github.com/angular/angular/issues/21999)
|
||||
* **core:** should check Zone existance when scheduleMicroTask ([#20656](https://github.com/angular/angular/issues/20656)) ([3a86940](https://github.com/angular/angular/commit/3a86940))
|
||||
* **forms:** publish missing types ([#19941](https://github.com/angular/angular/issues/19941)) ([2707012](https://github.com/angular/angular/commit/2707012))
|
||||
* **ivy:** generate correct interpolations ([#21946](https://github.com/angular/angular/issues/21946)) ([3cc1d76](https://github.com/angular/angular/commit/3cc1d76))
|
||||
* **ivy:** generate lifecycle pattern ([#21865](https://github.com/angular/angular/issues/21865)) ([f816666](https://github.com/angular/angular/commit/f816666))
|
||||
* **ivy:** improve `bindV` perf and memory usage ([#21881](https://github.com/angular/angular/issues/21881)) ([0846784](https://github.com/angular/angular/commit/0846784))
|
||||
* **ivy:** remove unnecessary parameter of NgOnChangesFeature ([#21879](https://github.com/angular/angular/issues/21879)) ([65cf1ad](https://github.com/angular/angular/commit/65cf1ad))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler-cli:** reflect static methods added to classes in metadata ([#21926](https://github.com/angular/angular/issues/21926)) ([eb8ddd2](https://github.com/angular/angular/commit/eb8ddd2))
|
||||
* **ivy:** add canonical example of a pipe. ([#21834](https://github.com/angular/angular/issues/21834)) ([743d8bc](https://github.com/angular/angular/commit/743d8bc))
|
||||
* **ivy:** add support for attributes on ng-content nodes ([#21935](https://github.com/angular/angular/issues/21935)) ([1aa2947](https://github.com/angular/angular/commit/1aa2947))
|
||||
* **ivy:** memoize array literals in render3 ([#21973](https://github.com/angular/angular/issues/21973)) ([4d62be6](https://github.com/angular/angular/commit/4d62be6))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **ivy:** improve Uglify configuration in hello world integration test ([#21985](https://github.com/angular/angular/issues/21985)) ([7e51e52](https://github.com/angular/angular/commit/7e51e52))
|
||||
|
||||
|
||||
|
||||
<a name="5.2.4"></a>
|
||||
## [5.2.4](https://github.com/angular/angular/compare/5.2.3...5.2.4) (2018-02-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** don't convert null to a string when flushing a mock request ([#21417](https://github.com/angular/angular/issues/21417)) ([c4fb696](https://github.com/angular/angular/commit/c4fb696)), closes [#20744](https://github.com/angular/angular/issues/20744)
|
||||
* **core:** fix [#20582](https://github.com/angular/angular/issues/20582), don't need to wrap zone in location change listener ([#22007](https://github.com/angular/angular/issues/22007)) ([ce51ea9](https://github.com/angular/angular/commit/ce51ea9))
|
||||
* **core:** fix proper propagation of subscriptions in EventEmitter ([#22016](https://github.com/angular/angular/issues/22016)) ([c6645e7](https://github.com/angular/angular/commit/c6645e7)), closes [#21999](https://github.com/angular/angular/issues/21999)
|
||||
* **core:** should check Zone existance when scheduleMicroTask ([#20656](https://github.com/angular/angular/issues/20656)) ([aa9ba7f](https://github.com/angular/angular/commit/aa9ba7f))
|
||||
|
||||
|
||||
|
||||
<a name="6.0.0-beta.2"></a>
|
||||
# [6.0.0-beta.2](https://github.com/angular/angular/compare/6.0.0-beta.1...6.0.0-beta.2) (2018-01-31)
|
||||
|
||||
|
@ -51,7 +51,7 @@ and help you to craft the change so that it is successfully accepted into the pr
|
||||
|
||||
Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
|
||||
|
||||
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs we will systematically ask you to provide a minimal reproduction scenario using http://plnkr.co. Having a live, reproducible scenario gives us wealth of important information without going back & forth to you with additional questions like:
|
||||
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs, we will systematically ask you to provide a minimal reproduction scenario using http://plnkr.co. Having a live, reproducible scenario gives us a wealth of important information without going back & forth to you with additional questions like:
|
||||
|
||||
- version of Angular used
|
||||
- 3rd-party libraries and their versions
|
||||
@ -61,7 +61,7 @@ A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm
|
||||
|
||||
We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal plunk. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
|
||||
|
||||
Unfortunately we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you we are going to close an issue that don't have enough info to be reproduced.
|
||||
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you we are going to close an issue that doesn't have enough info to be reproduced.
|
||||
|
||||
You can file new issues by filling out our [new issue form](https://github.com/angular/angular/issues/new).
|
||||
|
||||
@ -173,12 +173,12 @@ The **header** is mandatory and the **scope** of the header is optional.
|
||||
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
|
||||
to read on GitHub as well as in various git tools.
|
||||
|
||||
Footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
|
||||
The footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
|
||||
|
||||
Samples: (even more [samples](https://github.com/angular/angular/commits/master))
|
||||
|
||||
```
|
||||
docs(changelog): update change log to beta.5
|
||||
docs(changelog): update changelog to beta.5
|
||||
```
|
||||
```
|
||||
fix(release): need to depend on latest rxjs and zone.js
|
||||
@ -203,7 +203,7 @@ Must be one of the following:
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
|
||||
### Scope
|
||||
The scope should be the name of the npm package affected (as perceived by person reading changelog generated from commit messages.
|
||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages.
|
||||
|
||||
The following is the list of supported scopes:
|
||||
|
||||
@ -232,10 +232,10 @@ There are currently a few exceptions to the "use package name" rule:
|
||||
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`)
|
||||
|
||||
### Subject
|
||||
The subject contains succinct description of the change:
|
||||
The subject contains a succinct description of the change:
|
||||
|
||||
* use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
* don't capitalize first letter
|
||||
* don't capitalize the first letter
|
||||
* no dot (.) at the end
|
||||
|
||||
### Body
|
||||
@ -266,7 +266,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
||||
* https://help.github.com/articles/setting-your-commit-email-address-in-git/
|
||||
* https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean
|
||||
* https://help.github.com/articles/about-commit-email-addresses/
|
||||
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
|
||||
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
|
||||
|
||||
Note that if you have more than one Git identity, it is important to verify that you are logged in with the same ID with which you signed the CLA, before you commit changes. If not, your PR will fail the CLA check.
|
||||
|
||||
|
40
WORKSPACE
40
WORKSPACE
@ -1,11 +1,14 @@
|
||||
workspace(name = "angular")
|
||||
|
||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
# Using a pre-release snapshot to pick up a commit that makes all nodejs_binary
|
||||
# programs produce source-mapped stack traces.
|
||||
RULES_NODEJS_VERSION = "926349cea4cd360afcd5647ccdd09d2d2fb471aa"
|
||||
|
||||
git_repository(
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
remote = "https://github.com/bazelbuild/rules_nodejs.git",
|
||||
commit = "230d39a391226f51c03448f91eb61370e2e58c42",
|
||||
url = "https://github.com/bazelbuild/rules_nodejs/archive/%s.zip" % RULES_NODEJS_VERSION,
|
||||
strip_prefix = "rules_nodejs-%s" % RULES_NODEJS_VERSION,
|
||||
sha256 = "5ba3c8c209078c2e3f0c6aa4abd01a1a561f92a5bfda04e25604af5f4734d69d",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories")
|
||||
@ -13,10 +16,13 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_reposi
|
||||
check_bazel_version("0.9.0")
|
||||
node_repositories(package_json = ["//:package.json"])
|
||||
|
||||
git_repository(
|
||||
RULES_TYPESCRIPT_VERSION = "0.10.1"
|
||||
|
||||
http_archive(
|
||||
name = "build_bazel_rules_typescript",
|
||||
remote = "https://github.com/bazelbuild/rules_typescript.git",
|
||||
commit = "eb3244363e1cb265c84e723b347926f28c29aa35"
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/%s.zip" % RULES_TYPESCRIPT_VERSION,
|
||||
strip_prefix = "rules_typescript-%s" % RULES_TYPESCRIPT_VERSION,
|
||||
sha256 = "a2c81776a4a492ff9f878f9705639f5647bef345f7f3e1da09c9eeb8dec80485",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
|
||||
@ -56,3 +62,23 @@ http_archive(
|
||||
strip_prefix = "bazel-9755c72b48866ed034bd28aa033e9abd27431b1e",
|
||||
sha256 = "5b8443fc3481b5fcd9e7f348e1dd93c1397f78b223623c39eb56494c55f41962",
|
||||
)
|
||||
|
||||
# We have a source dependency on the Devkit repository, because it's built with
|
||||
# Bazel.
|
||||
# This allows us to edit sources and have the effect appear immediately without
|
||||
# re-packaging or "npm link"ing.
|
||||
# Even better, things like aspects will visit the entire graph including
|
||||
# ts_library rules in the devkit repository.
|
||||
http_archive(
|
||||
name = "angular_devkit",
|
||||
url = "https://github.com/angular/devkit/archive/v0.3.1.zip",
|
||||
strip_prefix = "devkit-0.3.1",
|
||||
sha256 = "31d4b597fe9336650acf13df053c1c84dcbe9c29c6a833bcac3819cd3fd8cad3",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "org_brotli",
|
||||
url = "https://github.com/google/brotli/archive/v1.0.2.zip",
|
||||
strip_prefix = "brotli-1.0.2",
|
||||
sha256 = "b43d5d6bc40f2fa6c785b738d86c6bbe022732fe25196ebbe43b9653a025920d",
|
||||
)
|
||||
|
@ -39,7 +39,7 @@
|
||||
],
|
||||
"e2e": {
|
||||
"protractor": {
|
||||
"config": "./protractor.conf.js"
|
||||
"config": "tests/e2e/protractor.conf.js"
|
||||
}
|
||||
},
|
||||
"lint": [
|
||||
@ -50,12 +50,12 @@
|
||||
"project": "src/tsconfig.spec.json"
|
||||
},
|
||||
{
|
||||
"project": "e2e/tsconfig.e2e.json"
|
||||
"project": "tests/e2e/tsconfig.e2e.json"
|
||||
}
|
||||
],
|
||||
"test": {
|
||||
"karma": {
|
||||
"config": "./karma.conf.js"
|
||||
"config": "src/karma.conf.js"
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
|
@ -105,8 +105,7 @@ The general setup is as follows:
|
||||
* Open a terminal, ensure the dependencies are installed; run an initial doc generation; then start the doc-viewer:
|
||||
|
||||
```bash
|
||||
yarn
|
||||
yarn docs
|
||||
yarn setup
|
||||
yarn start
|
||||
```
|
||||
|
||||
|
@ -15,6 +15,7 @@ describe('Form Validation Tests', function () {
|
||||
});
|
||||
|
||||
tests('Template-Driven Form');
|
||||
bobTests();
|
||||
});
|
||||
|
||||
describe('Reactive form', () => {
|
||||
|
@ -20,7 +20,7 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
|
||||
// #enddocregion directive-providers
|
||||
})
|
||||
export class ForbiddenValidatorDirective implements Validator {
|
||||
@Input() forbiddenName: string;
|
||||
@Input('appForbiddenName') forbiddenName: string;
|
||||
|
||||
validate(control: AbstractControl): {[key: string]: any} {
|
||||
return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control)
|
||||
|
@ -12,7 +12,7 @@
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<!-- #docregion name-input -->
|
||||
<input id="name" name="name" class="form-control"
|
||||
required minlength="4" forbiddenName="bob"
|
||||
required minlength="4" appForbiddenName="bob"
|
||||
[(ngModel)]="hero.name" #name="ngModel" >
|
||||
<!-- #enddocregion name-input -->
|
||||
|
||||
|
@ -21,7 +21,14 @@ import { MessagesComponent } from './messages/messages.component';
|
||||
FormsModule
|
||||
],
|
||||
// #docregion providers
|
||||
providers: [ HeroService, MessageService ],
|
||||
// #docregion providers-heroservice
|
||||
providers: [
|
||||
HeroService,
|
||||
// #enddocregion providers-heroservice
|
||||
MessageService
|
||||
// #docregion providers-heroservice
|
||||
],
|
||||
// #enddocregion providers-heroservice
|
||||
// #enddocregion providers
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
|
@ -3,6 +3,6 @@
|
||||
<h2>Messages</h2>
|
||||
<button class="clear"
|
||||
(click)="messageService.clear()">clear</button>
|
||||
<div *ngFor='let message of messageService.messages'> {{message}} </div>
|
||||
<div *ngFor="let message of messageService.messages"> {{message}} </div>
|
||||
|
||||
</div>
|
||||
|
@ -190,6 +190,29 @@ describe('Tutorial part 6', () => {
|
||||
const maxId = heroesBefore[heroesBefore.length - 1].id;
|
||||
expect(heroesAfter[numHeroes]).toEqual({id: maxId + 1, name: newHeroName});
|
||||
});
|
||||
|
||||
it('displays correctly styled buttons', async () => {
|
||||
element.all(by.buttonText('x')).then(buttons => {
|
||||
for (const button of buttons) {
|
||||
// Inherited styles from styles.css
|
||||
expect(button.getCssValue('font-family')).toBe('Arial');
|
||||
expect(button.getCssValue('border')).toContain('none');
|
||||
expect(button.getCssValue('padding')).toBe('5px 10px');
|
||||
expect(button.getCssValue('border-radius')).toBe('4px');
|
||||
// Styles defined in heroes.component.css
|
||||
expect(button.getCssValue('left')).toBe('194px');
|
||||
expect(button.getCssValue('top')).toBe('-32px');
|
||||
}
|
||||
});
|
||||
|
||||
const addButton = element(by.buttonText('add'));
|
||||
// Inherited styles from styles.css
|
||||
expect(addButton.getCssValue('font-family')).toBe('Arial');
|
||||
expect(addButton.getCssValue('border')).toContain('none');
|
||||
expect(addButton.getCssValue('padding')).toBe('5px 10px');
|
||||
expect(addButton.getCssValue('border-radius')).toBe('4px');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Progressive hero search', () => {
|
||||
|
@ -51,7 +51,7 @@
|
||||
}
|
||||
|
||||
/* #docregion additions */
|
||||
.button {
|
||||
button {
|
||||
background-color: #eee;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
|
@ -1254,7 +1254,7 @@ also encapsulate a style sheet within a specific component.
|
||||
|
||||
|
||||
### Styles configuration
|
||||
<code-example hideCopy path="ajs-quick-reference/.angular-cli.1.json" region="styles" linenums="false">
|
||||
<code-example hideCopy path="ajs-quick-reference/.angular-cli.1.json" region="styles" linenums="false"></code-example>
|
||||
|
||||
With the Angular CLI, you can configure your global styles in the `.angular-cli.json` file.
|
||||
You can rename the extension to `.scss` to use sass.
|
||||
|
@ -92,7 +92,7 @@ You can control your app compilation by providing template compiler options in t
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"fullTemplateTypeCheck": true,
|
||||
"preserveWhiteSpaces": false,
|
||||
"preserveWhitespaces": true,
|
||||
...
|
||||
}
|
||||
}
|
||||
@ -234,9 +234,7 @@ done manually.
|
||||
### *preserveWhitespaces*
|
||||
|
||||
This option tells the compiler whether to remove blank text nodes from compiled templates.
|
||||
This option is `true` by default.
|
||||
|
||||
*Note*: It is recommended to set this explicitly to `false` as it emits smaller template factory modules and might be set to `false` by default in the future.
|
||||
As of v6, this option is `false` by default, which results in smaller emitted template factory modules.
|
||||
|
||||
### *allowEmptyCodegenFiles*
|
||||
|
||||
|
175
aio/content/guide/api-page-class.md
Normal file
175
aio/content/guide/api-page-class.md
Normal file
@ -0,0 +1,175 @@
|
||||
<div class="breadcrumb">
|
||||
<a href="#">API<a> / <a href="#">@core<a>
|
||||
</div>
|
||||
<header class="api-header">
|
||||
<h1><label class="api-status-label experimental">experimental</label><label class="api-type-label class">class</label>Class Name</h1>
|
||||
</header>
|
||||
<div class="page-actions">
|
||||
<a href="#"><label class="raised page-label"><i class="material-icons">mode_edit</i>suggest edits</label></a>
|
||||
<a href="#"><label class="raised page-label"><i class="material-icons">code</i>view source</label></a>
|
||||
</div>
|
||||
<p>Class description goes here. This is a short and to the point one or two sentence description that easily introduces the reader to the class.</p>
|
||||
<div class="api-body">
|
||||
<section>
|
||||
<h2>Overview</h2>
|
||||
<code-example language="ts" hidecopy="true" ng-version="5.2.0"><aio-code class="simple-code" ng-reflect-ng-class="[object Object]" ng-reflect-code="
|
||||
class <a href="api/core/Compi" ng-reflect-hide-copy="true" ng-reflect-language="ts" ng-reflect-linenums="" ng-reflect-path="" ng-reflect-region="" ng-reflect-title=""><pre class="prettyprint lang-ts">
|
||||
<code class="animated fadeIn"><span class="kwd">class</span><span class="pln"> </span><a href="api/core/Compiler" class="code-anchor"><span class="typ">Compiler</span></a><span class="pln"> </span><span class="pun">{</span><span class="pln">
|
||||
</span><a class="code-anchor" href="api/core/Compiler#compileModuleSync"><span class="pln">compileModuleSync</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>(</span><span class="pln">moduleType</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Type</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>):</span><span class="pln"> </span><span class="typ">NgModuleFactory</span><span class="pun"><</span><span class="pln">T</span><span class="pun">></span></a><span class="pln">
|
||||
</span><a class="code-anchor" href="api/core/Compiler#compileModuleAsync"><span class="pln">compileModuleAsync</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>(</span><span class="pln">moduleType</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Type</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>):</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun"><</span><span class="typ">NgModuleFactory</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>></span></a><span class="pln">
|
||||
</span><a class="code-anchor" href="api/core/Compiler#compileModuleAndAllComponentsSync"><span class="pln">compileModuleAndAllComponentsSync</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>(</span><span class="pln">moduleType</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Type</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>):</span><span class="pln"> </span><span class="typ">ModuleWithComponentFactories</span><span class="pun"><</span><span class="pln">T</span><span class="pun">></span></a><span class="pln">
|
||||
</span><a class="code-anchor" href="api/core/Compiler#compileModuleAndAllComponentsAsync"><span class="pln">compileModuleAndAllComponentsAsync</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>(</span><span class="pln">moduleType</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Type</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>):</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun"><</span><span class="typ">ModuleWithComponentFactories</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>></span></a><span class="pln">
|
||||
</span><a class="code-anchor" href="api/core/Compiler#clearCache"><span class="pln">clearCache</span><span class="pun">():</span><span class="pln"> </span><span class="kwd">void</span></a><span class="pln">
|
||||
</span><a class="code-anchor" href="api/core/Compiler#clearCacheFor"><span class="pln">clearCacheFor</span><span class="pun">(</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Type</span><span class="pun"><</span><span class="pln">any</span><span class="pun">>)</span></a><span class="pln">
|
||||
</span><span class="pun">}</span></code>
|
||||
</pre></aio-code></code-example>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Description</h2>
|
||||
<p>The longer class description goes here which can include multiple paragraphs.</p>
|
||||
</p>Bacon ipsum dolor amet pork belly capicola sirloin venison alcatra ground round ham hock jowl turkey picanha bresaola pancetta brisket chicken fatback. Burgdoggen kevin salami jowl shoulder jerky leberkas meatball. Ham hock picanha burgdoggen pork belly rump bacon cupim. Bacon kielbasa sirloin shank strip steak ground round. Bresaola cow salami meatloaf pork chop leberkas flank turducken biltong meatball chuck pork tri-tip chicken. Ribeye corned beef shoulder, meatloaf strip steak jerky porchetta capicola alcatra ham.</p>
|
||||
<h3>Subclasses</h3>
|
||||
<ul>
|
||||
<li><a href="#">Subclass1</a></li>
|
||||
<li><a href="#">Subclass2</a></li>
|
||||
<li><a href="#">Subclass3</a></li>
|
||||
</ul>
|
||||
<h3>See Also</h3>
|
||||
<ul>
|
||||
<li><a href="#">Link1</a></li>
|
||||
<li><a href="#">Link2</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Constructor</h2>
|
||||
<code-example hidecopy="true" class="no-box api-heading" ng-version="5.2.0">
|
||||
<aio-code class="simple-code"><pre class="prettyprint lang-">
|
||||
<code class="animated fadeIn"><span class="kwd">constructor</span><span class="pun">(</span><span class="pln">element</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">,</span><span class="pln"> keyframes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
|
||||
</span><span class="pun">[</span><span class="pln">key</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">]:</span><span class="pln"> </span><span class="kwd">string</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
|
||||
</span><span class="pun">}[],</span><span class="pln"> duration</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> delay</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> easing</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">,</span><span class="pln"> previousPlayers</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">[])</span></code>
|
||||
</pre></aio-code></code-example>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Properties</h2>
|
||||
<table class="is-full-width list-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Property</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code><strong>Property1</strong></code>
|
||||
</td>
|
||||
<td><label class="property-type-label type">Type</label></td>
|
||||
<td>Description goes here</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code><strong>Property2</strong></code>
|
||||
</td>
|
||||
<td>Type</td>
|
||||
<td>Description goes here</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code><strong>Property3</strong></code>
|
||||
</td>
|
||||
<td>Type</td>
|
||||
<td>Description goes here</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section class="api-method">
|
||||
<h2>Methods</h2>
|
||||
<table class="is-full-width item-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Method1Name( )</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<p>Description goes here</p>
|
||||
<br>
|
||||
<p>Bacon ipsum dolor amet pork belly capicola sirloin venison alcatra ground round ham hock jowl turkey picanha bresaola pancetta brisket chicken fatback. Burgdoggen kevin salami jowl shoulder jerky leberkas meatball. Ham hock picanha burgdoggen pork belly rump bacon cupim. Bacon kielbasa sirloin shank strip steak ground round. Bresaola cow salami meatloaf pork chop leberkas flank turducken biltong meatball chuck pork tri-tip chicken. Ribeye corned beef shoulder, meatloaf strip steak jerky porchetta capicola alcatra ham.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="is-full-width api-method item-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Method2Name( )</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<p>Description goes here</p>
|
||||
<hr>
|
||||
<h5>Declaration</h5>
|
||||
<code-example language="ts" hidecopy="true" ng-version="5.2.0">
|
||||
<aio-code class="simple-code"><pre class="prettyprint lang-ts">
|
||||
<code class="animated fadeIn"><span class="kwd">class</span><span class="pln"> </span><a href="api/animations/AnimationBuilder" class="code-anchor"><span class="typ">AnimationBuilder</span></a><span class="pln"> </span><span class="pun">{</span><span class="pln"></span><a class="code-anchor" href="api/animations/AnimationBuilder#build"><span class="pln">build</span><span class="pun">(</span><span class="pln">animation</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AnimationMetadata</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="typ">AnimationMetadata</span><span class="pun">[]):</span><span class="pln"> </span><span class="typ">AnimationFactory</span></a><span class="pln"></span><span class="pun">}</span></code></pre>
|
||||
</aio-code>
|
||||
</code-example>
|
||||
<h6>Parameters</h6>
|
||||
<h6>Returns</h6>
|
||||
<p>Returns information and results goes here.</p>
|
||||
<h6>Errors</h6>
|
||||
<p>Error information goes here</p>
|
||||
<hr>
|
||||
<p>Further details provided as needed. Bacon ipsum dolor amet pork belly capicola sirloin venison alcatra ground round ham hock jowl turkey picanha bresaola pancetta brisket chicken fatback. Burgdoggen kevin salami jowl shoulder jerky leberkas meatball.</p><hr>
|
||||
<h6>Overloads</h6>
|
||||
<table class="is-full-width">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code-example hidecopy="true" class="no-box api-heading" ng-version="5.2.0">
|
||||
<aio-code class="simple-code"><pre class="prettyprint lang-">
|
||||
<code class="animated fadeIn"><span class="kwd">constructor</span><span class="pun">(</span><span class="pln">element</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">,</span><span class="pln"> keyframes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
|
||||
</span><span class="pun">[</span><span class="pln">key</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">]:</span><span class="pln"> </span><span class="kwd">string</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
|
||||
</span><span class="pun">}[],</span><span class="pln"> duration</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> delay</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> easing</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">,</span><span class="pln"> previousPlayers</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">[])</span></code>
|
||||
</pre></aio-code></code-example>
|
||||
</td>
|
||||
<td>Description goes here</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code-example hidecopy="true" class="no-box api-heading" ng-version="5.2.0">
|
||||
<aio-code class="simple-code"><pre class="prettyprint lang-">
|
||||
<code class="animated fadeIn"><span class="kwd">constructor</span><span class="pun">(</span><span class="pln">element</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">,</span><span class="pln"> keyframes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
|
||||
</span><span class="pun">[</span><span class="pln">key</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">]:</span><span class="pln"> </span><span class="kwd">string</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
|
||||
</span><span class="pun">}[],</span><span class="pln"> duration</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> delay</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> easing</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">,</span><span class="pln"> previousPlayers</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">[])</span></code>
|
||||
</pre></aio-code></code-example>
|
||||
</td>
|
||||
<td>Description goes here</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h5>Example: Descriptive Title of Method Example</h5>
|
||||
<p>Bacon ipsum dolor amet pork belly capicola sirloin venison alcatra ground round ham hock jowl turkey picanha bresaola pancetta brisket chicken fatback. Burgdoggen kevin salami jowl shoulder jerky leberkas meatball. Ham hock picanha burgdoggen pork belly rump bacon cupim. Bacon kielbasa sirloin shank strip steak ground round. Bresaola cow salami meatloaf pork chop leberkas flank turducken biltong meatball chuck pork tri-tip chicken. Ribeye corned beef shoulder, meatloaf strip steak jerky porchetta capicola alcatra ham.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Example: Descriptive Title of Combined Example Goes Here</h2>
|
||||
<p>Intro description text about what the example is and how it can be used.</p>
|
||||
<code-example hidecopy="true" class="no-box api-heading" ng-version="5.2.0">
|
||||
<aio-code class="simple-code"><pre class="prettyprint lang-">
|
||||
<code class="animated fadeIn"><span class="kwd">constructor</span><span class="pun">(</span><span class="pln">element</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">,</span><span class="pln"> keyframes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
|
||||
</span><span class="pun">[</span><span class="pln">key</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">]:</span><span class="pln"> </span><span class="kwd">string</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
|
||||
</span><span class="pun">}[],</span><span class="pln"> duration</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> delay</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> easing</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">,</span><span class="pln"> previousPlayers</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">[])</span></code>
|
||||
</pre></aio-code></code-example>
|
||||
<p>Further explanation provided as needed. Bacon ipsum dolor amet pork belly capicola sirloin venison alcatra ground round ham hock jowl turkey picanha bresaola pancetta brisket chicken fatback. Burgdoggen kevin salami jowl shoulder jerky leberkas meatball.</p>
|
||||
</section>
|
||||
</div>
|
@ -186,9 +186,9 @@ template for our `HeroListComponent`:
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.html" title="src/app/hero-list.component.html"></code-example>
|
||||
|
||||
Although this template uses typical HTML elements like `<h2>` and `<p>`, it also has some differences. Code like `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<hero-detail>` uses Angular's [template syntax](guide/template-syntax).
|
||||
Although this template uses typical HTML elements like `<h2>` and `<p>`, it also has some differences. Code like `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<app-hero-detail>` uses Angular's [template syntax](guide/template-syntax).
|
||||
|
||||
In the last line of the template, the `<hero-detail>` tag is a custom element that represents a new component, `HeroDetailComponent`.
|
||||
In the last line of the template, the `<app-hero-detail>` tag is a custom element that represents a new component, `HeroDetailComponent`.
|
||||
|
||||
The `HeroDetailComponent` is a *different* component than the `HeroListComponent` you've been reviewing.
|
||||
The `HeroDetailComponent` (code not shown) presents facts about a particular hero, the
|
||||
@ -197,7 +197,7 @@ The `HeroDetailComponent` is a **child** of the `HeroListComponent`.
|
||||
|
||||
<img src="generated/images/guide/architecture/component-tree.png" alt="Metadata" class="left">
|
||||
|
||||
Notice how `<hero-detail>` rests comfortably among native HTML elements. Custom components mix seamlessly with native HTML in the same layouts.
|
||||
Notice how `<app-hero-detail>` rests comfortably among native HTML elements. Custom components mix seamlessly with native HTML in the same layouts.
|
||||
|
||||
<hr class="clear"/>
|
||||
|
||||
@ -230,8 +230,8 @@ information Angular needs to create and present the component and its view.
|
||||
Here are a few of the most useful `@Component` configuration options:
|
||||
|
||||
* `selector`: CSS selector that tells Angular to create and insert an instance of this component
|
||||
where it finds a `<hero-list>` tag in *parent* HTML.
|
||||
For example, if an app's HTML contains `<hero-list></hero-list>`, then
|
||||
where it finds a `<app-hero-list>` tag in *parent* HTML.
|
||||
For example, if an app's HTML contains `<app-hero-list></app-hero-list>`, then
|
||||
Angular inserts an instance of the `HeroListComponent` view between those tags.
|
||||
|
||||
* `templateUrl`: module-relative address of this component's HTML template, shown [above](guide/architecture#templates).
|
||||
|
@ -2,188 +2,96 @@
|
||||
|
||||
Angular supports most recent browsers. This includes the following specific versions:
|
||||
|
||||
|
||||
<table>
|
||||
|
||||
<tr>
|
||||
|
||||
<th>
|
||||
<th>
|
||||
Browser
|
||||
</th>
|
||||
|
||||
<th>
|
||||
Supported versions
|
||||
</th>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
Chrome
|
||||
</th>
|
||||
</td>
|
||||
|
||||
<th>
|
||||
<td>
|
||||
latest
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
Firefox
|
||||
</th>
|
||||
</td>
|
||||
|
||||
<th>
|
||||
<td>
|
||||
latest
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
Edge
|
||||
</th>
|
||||
</td>
|
||||
|
||||
<th>
|
||||
<td>
|
||||
2 most recent major versions
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
IE
|
||||
</th>
|
||||
|
||||
<th>
|
||||
Safari
|
||||
</th>
|
||||
|
||||
<th>
|
||||
iOS
|
||||
</th>
|
||||
|
||||
<th>
|
||||
Android
|
||||
</th>
|
||||
|
||||
<th>
|
||||
</td>
|
||||
<td>
|
||||
11<br>10<br>9
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr>
|
||||
<td>
|
||||
IE Mobile
|
||||
</th>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
latest
|
||||
</td>
|
||||
|
||||
<td>
|
||||
latest
|
||||
</td>
|
||||
|
||||
<td>
|
||||
14
|
||||
</td>
|
||||
|
||||
<td>
|
||||
11
|
||||
</td>
|
||||
|
||||
<td>
|
||||
10
|
||||
</td>
|
||||
|
||||
<td>
|
||||
10
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Nougat (7.0)<br>Marshmallow (6.0)
|
||||
</td>
|
||||
|
||||
<td>
|
||||
11
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Safari
|
||||
</td>
|
||||
|
||||
<td>
|
||||
2 most recent major versions
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
iOS
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
2 most recent major versions
|
||||
</td>
|
||||
|
||||
<td>
|
||||
13
|
||||
</td>
|
||||
|
||||
<td>
|
||||
10
|
||||
</td>
|
||||
|
||||
<td>
|
||||
9
|
||||
</td>
|
||||
|
||||
<td>
|
||||
9
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Lollipop<br>(5.0, 5.1)
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
Android
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
Nougat (7.0)<br>Marshmallow (6.0)<br>Lollipop (5.0, 5.1)<br>KitKat (4.4)
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
9
|
||||
</td>
|
||||
|
||||
<td>
|
||||
8
|
||||
</td>
|
||||
|
||||
<td>
|
||||
8
|
||||
</td>
|
||||
|
||||
<td>
|
||||
KitKat<br>(4.4)
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
7
|
||||
</td>
|
||||
|
||||
<td>
|
||||
7
|
||||
</td>
|
||||
|
||||
<td>
|
||||
Jelly Bean<br>(4.1, 4.2, 4.3)
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
@ -219,21 +127,16 @@ the CLI created with your project.
|
||||
|
||||
This file incorporates the mandatory and many of the optional polyfills as JavaScript `import` statements.
|
||||
|
||||
The npm packages for the _mandatory_ polyfills (such as `zone.js`) were installed automatically for you when you created your project and
|
||||
their corresponding `import` statements are ready to go.
|
||||
You probably won't touch these.
|
||||
The npm packages for the _mandatory_ polyfills (such as `zone.js`) were installed automatically for you when you created your project and their corresponding `import` statements are ready to go. You probably won't touch these.
|
||||
|
||||
But if you need an optional polyfill, you'll have to install its npm package with `npm` or `yarn`.
|
||||
For example, [if you need the web animations polyfill](http://caniuse.com/#feat=web-animation),
|
||||
you could install it with either of the following commands:
|
||||
But if you need an optional polyfill, you'll have to install its npm package.
|
||||
For example, [if you need the web animations polyfill](http://caniuse.com/#feat=web-animation), you could install it with `npm`, using the following command (or the `yarn` equivalent):
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
npm install --save web-animations-js
|
||||
yarn add web-animations-js
|
||||
</code-example>
|
||||
|
||||
Then open the `polyfills.ts` file and un-comment the corresponding `import` statement
|
||||
as in the following example:
|
||||
Then open the `polyfills.ts` file and un-comment the corresponding `import` statement as in the following example:
|
||||
|
||||
<code-example title="src/polyfills.ts">
|
||||
/**
|
||||
@ -243,7 +146,7 @@ as in the following example:
|
||||
import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
</code-example>
|
||||
|
||||
If you can't find the polyfill you want in `polyfills.ts`,
|
||||
If you can't find the polyfill you want in `polyfills.ts`,
|
||||
add it yourself, following the same pattern:
|
||||
|
||||
1. install the npm package
|
||||
@ -283,6 +186,7 @@ These are the polyfills required to run an Angular application on each supported
|
||||
<td>
|
||||
|
||||
[ES7/reflect](guide/browser-support#core-es7-reflect) (JIT only)
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@ -295,8 +199,8 @@ These are the polyfills required to run an Angular application on each supported
|
||||
|
||||
<td>
|
||||
|
||||
|
||||
[ES6](guide/browser-support#core-es6)
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@ -309,7 +213,6 @@ These are the polyfills required to run an Angular application on each supported
|
||||
|
||||
<td>
|
||||
|
||||
|
||||
[ES6<br>classList](guide/browser-support#classlist)
|
||||
|
||||
</td>
|
||||
@ -323,8 +226,7 @@ These are the polyfills required to run an Angular application on each supported
|
||||
|
||||
Some features of Angular may require additional polyfills.
|
||||
|
||||
For example, the animations library relies on the standard web animation API, which is only available in Chrome and Firefox today.
|
||||
You'll need a polyfill to use animations in other browsers.
|
||||
For example, the animations library relies on the standard web animation API, which is only available in Chrome and Firefox today. You'll need a polyfill to use animations in other browsers.
|
||||
|
||||
Here are the features which may require additional polyfills:
|
||||
|
||||
@ -351,19 +253,20 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
<td>
|
||||
|
||||
[JIT compilation](guide/aot-compiler).
|
||||
[JIT compilation](guide/aot-compiler).
|
||||
|
||||
Required to reflect for metadata.
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
[ES7/reflect](guide/browser-support#core-es7-reflect)
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
All current browsers.
|
||||
Enabled by default.
|
||||
Can remove If you always use AOT and only use Angular decorators.
|
||||
All current browsers. Enabled by default.
|
||||
Can remove if you always use AOT and only use Angular decorators.
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@ -373,12 +276,13 @@ Here are the features which may require additional polyfills:
|
||||
<td>
|
||||
|
||||
[Animations](guide/animations)
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
|
||||
[Web Animations](guide/browser-support#web-animations)
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -391,13 +295,23 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
<td>
|
||||
|
||||
If you use the following deprecated i18n pipes: [date](api/common/DeprecatedDatePipe), [currency](api/common/DeprecatedCurrencyPipe), [decimal](api/common/DeprecatedDecimalPipe) and [percent](api/common/DeprecatedPercentPipe)
|
||||
If you use the following deprecated i18n pipes:
|
||||
|
||||
|
||||
[date](api/common/DeprecatedDatePipe),
|
||||
|
||||
[currency](api/common/DeprecatedCurrencyPipe),
|
||||
|
||||
[decimal](api/common/DeprecatedDecimalPipe),
|
||||
|
||||
[percent](api/common/DeprecatedPercentPipe)
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
|
||||
[Intl API](guide/browser-support#intl)
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -410,13 +324,15 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
<td>
|
||||
|
||||
[NgClass](api/common/NgClass) on SVG elements
|
||||
[NgClass](api/common/NgClass)
|
||||
|
||||
on SVG elements
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
|
||||
[classList](guide/browser-support#classlist)
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -429,14 +345,19 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
<td>
|
||||
|
||||
[Http](guide/http) when sending and receiving binary data
|
||||
[Http](guide/http)
|
||||
|
||||
when sending and receiving binary data
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
[Typed Array](guide/browser-support#typedarray)<br>
|
||||
|
||||
[Blob](guide/browser-support#blob)<br>
|
||||
|
||||
[FormData](guide/browser-support#formdata)
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -476,6 +397,7 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
<td>
|
||||
|
||||
<a id='core-es7-reflect' href="https://github.com/zloirock/core-js/blob/master/es7/reflect.js">ES7/reflect</a>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -491,7 +413,9 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
<a id='core-es6' href="https://github.com/zloirock/core-js">ES6</a>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -507,7 +431,9 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
<a id='classlist' href="https://github.com/eligrey/classList.js">classList</a>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -523,7 +449,9 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
<a id='intl' href="https://github.com/andyearnshaw/Intl.js">Intl</a>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -539,7 +467,9 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
<a id='web-animations' href="https://github.com/web-animations/web-animations-js">Web Animations</a>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -555,7 +485,9 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
<a id='typedarray' href="https://github.com/inexorabletash/polyfill/blob/master/typedarray.js">Typed Array</a>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -571,7 +503,9 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
<a id='blob' href="https://github.com/eligrey/Blob.js">Blob</a>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -587,7 +521,9 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
<a id='formdata' href="https://github.com/francois2metz/html5-formdata">FormData</a>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
@ -603,14 +539,13 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
\* Figures are for minified and gzipped code,
|
||||
computed with the <a href="http://closure-compiler.appspot.com/home">closure compiler</a>.
|
||||
|
||||
{@a non-cli}
|
||||
## Polyfills for non-CLI users
|
||||
|
||||
If you aren't using the CLI, you should add your polyfill scripts directly to the host web page (`index.html`), perhaps like this.
|
||||
If you are not using the CLI, you should add your polyfill scripts directly to the host web page (`index.html`), perhaps like this.
|
||||
|
||||
<code-example title="src/index.html">
|
||||
<!-- pre-zone polyfills -->
|
||||
@ -625,8 +560,13 @@ If you aren't using the CLI, you should add your polyfill scripts directly to th
|
||||
// __Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||
// __Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
// __zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||
</script>
|
||||
|
||||
/*
|
||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||
*/
|
||||
// __Zone_enable_cross_context_check = true;
|
||||
</script>
|
||||
<!-- zone.js required by Angular -->
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
|
||||
|
@ -120,11 +120,13 @@ The documentation for the version prior to v.2.2.0 has been removed.
|
||||
|
||||
## ES6 described in "TypeScript to JavaScript" (2016-11-14)
|
||||
|
||||
The updated TypeScript to JavaScript guide (removed August 2017, PR #18694)
|
||||
explains how to write apps in ES6/7
|
||||
The updated TypeScript to JavaScript guide explains how to write apps in ES6/7
|
||||
by translating the common idioms in the TypeScript documentation examples
|
||||
(and elsewhere on the web) to ES6/7 and ES5.
|
||||
|
||||
This was [removed in August 2017](https://github.com/angular/angular/pull/18694) but can still be
|
||||
viewed in the [v2 documentation](https://v2.angular.io/docs/ts/latest/cookbook/ts-to-js.html).
|
||||
|
||||
## Sync with Angular v.2.1.1 (2016-10-21)
|
||||
|
||||
Docs and code samples updated and tested with Angular v.2.1.1.
|
||||
|
@ -140,6 +140,11 @@ is available to <code>declarations</code> of this module.</p>
|
||||
<td><p>Binds the presence of CSS classes on the element to the truthiness of the associated map values. The right-hand expression should return {class-name: true/false} map.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><div <b>[ngStyle]</b>="{'property': 'value'}"></code><br><code><div <b>[ngStyle]</b>="dynamicStyles()"></code></td>
|
||||
<td><p>Allows you to assign styles to an HTML element using CSS. You can use CSS directly, as in the first example, or you can call a method from the component.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
<table class="is-full-width is-fixed-layout">
|
||||
|
@ -101,7 +101,7 @@ Begin the title with the markdown `#` character. Alternatively, you can write th
|
||||
|
||||
**Only one title (`<h1>`) per document!**
|
||||
|
||||
Title text should be in "Title Case", which means that you use capital letters to start the first words and all _principal_ words. Use lower case letters for _secondary words such as "in", "of", and "the".
|
||||
Title text should be in "Title Case", which means that you use capital letters to start the first words and all _principal_ words. Use lower case letters for _secondary_ words such as "in", "of", and "the".
|
||||
|
||||
```html
|
||||
# The Meat of the Matter
|
||||
|
@ -97,7 +97,7 @@ In fact, many libraries declare and export components you'll never use.
|
||||
For example, a material design library will export all components because it doesn’t know which ones you will use. However, it is unlikely that you will use them all.
|
||||
For the ones you don't reference, the tree shaker drops these components from the final code package.
|
||||
|
||||
If a component isn't an _entry component_ or isn't found in a template,
|
||||
If a component isn't an _entry component_ and isn't found in a template,
|
||||
the tree shaker will throw it away. So, it's best to add only the components that are truly entry components to help keep your app
|
||||
as trim as possible.
|
||||
|
||||
|
@ -98,7 +98,7 @@ When the CLI generated the `CustomerDashboardComponent` for the feature module,
|
||||
</code-example>
|
||||
|
||||
|
||||
To see this HTML in the `AppComponent`, you first have to export the `CustomerDashboardComponent` in the `CustomerDashboardModule`. In `customer-dashboard.module.ts`, just beneath the declarations array, add an exports array containing `CustomerDashboardModule`:
|
||||
To see this HTML in the `AppComponent`, you first have to export the `CustomerDashboardComponent` in the `CustomerDashboardModule`. In `customer-dashboard.module.ts`, just beneath the `declarations` array, add an `exports` array containing `CustomerDashboardModule`:
|
||||
|
||||
<code-example path="feature-modules/src/app/customer-dashboard/customer-dashboard.module.ts" region="component-exports" title="src/app/customer-dashboard/customer-dashboard.module.ts" linenums="false">
|
||||
</code-example>
|
||||
|
@ -171,7 +171,7 @@ comes together:
|
||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive" title="shared/forbidden-name.directive.ts (directive)">
|
||||
</code-example>
|
||||
|
||||
Once the `ForbiddenValidatorDirective` is ready, you can simply add its selector, `forbiddenName`, to any input element to activate it. For example:
|
||||
Once the `ForbiddenValidatorDirective` is ready, you can simply add its selector, `appForbiddenName`, to any input element to activate it. For example:
|
||||
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-input" title="template/hero-form-template.component.html (forbidden-name-input)" linenums="false">
|
||||
|
||||
|
@ -512,10 +512,6 @@ You rarely access Angular feature modules directly. You usually import them from
|
||||
|
||||
## NgModule
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
Helps you organize an application into cohesive blocks of functionality.
|
||||
An NgModule identifies the components, directives, and pipes that the application uses along with the list of external NgModules that the application needs, such as `FormsModule`.
|
||||
|
||||
@ -526,7 +522,7 @@ For details and examples, see [NgModules](guide/ngmodules) and the
|
||||
related files in that section.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{@a O}
|
||||
|
||||
@ -631,13 +627,10 @@ For more information, see the [Routing & Navigation](guide/router) page.
|
||||
|
||||
## Router module
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
A separate [NgModule](guide/glossary#ngmodule) that provides the necessary service providers and directives for navigating through application views.
|
||||
|
||||
For more information, see the [Routing & Navigation](guide/router) page.
|
||||
|
||||
</div>
|
||||
|
||||
## Routing component
|
||||
|
||||
|
@ -358,7 +358,7 @@ subscribes without a callback.
|
||||
The bare `.subscribe()` _seems_ pointless.
|
||||
|
||||
In fact, it is essential.
|
||||
Merely calling `HeroService.addHero()` **does not initiate the DELETE request.**
|
||||
Merely calling `HeroService.deleteHero()` **does not initiate the DELETE request.**
|
||||
|
||||
<code-example
|
||||
path="http/src/app/heroes/heroes.component.ts"
|
||||
|
@ -57,7 +57,7 @@ You can also use the VS Quick Open (⌘+P) to search for the extension. When you
|
||||
enter the following command:
|
||||
|
||||
```sh
|
||||
ext install ng-template
|
||||
ext install Angular.ng-template
|
||||
```
|
||||
|
||||
Then click the install button to install the Angular Language Service.
|
||||
|
@ -1,7 +1,5 @@
|
||||
# Lifecycle Hooks
|
||||
|
||||
<img src="generated/images/guide/lifecycle-hooks/hooks-in-sequence.png" alt="Us" class="left">
|
||||
|
||||
A component has a lifecycle managed by Angular.
|
||||
|
||||
Angular creates it, renders it, creates and renders its children,
|
||||
@ -10,7 +8,7 @@ checks it when its data-bound properties change, and destroys it before removing
|
||||
Angular offers **lifecycle hooks**
|
||||
that provide visibility into these key life moments and the ability to act when they occur.
|
||||
|
||||
A directive has the same set of lifecycle hooks, minus the hooks that are specific to component content and views.
|
||||
A directive has the same set of lifecycle hooks.
|
||||
|
||||
{@a hooks-overview}
|
||||
|
||||
@ -27,7 +25,7 @@ that Angular calls shortly after creating the component:
|
||||
|
||||
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" title="peek-a-boo.component.ts (excerpt)" linenums="false"></code-example>
|
||||
|
||||
No directive or component will implement all of the lifecycle hooks and some of the hooks only make sense for components.
|
||||
No directive or component will implement all of the lifecycle hooks.
|
||||
Angular only calls a directive/component hook method *if it is defined*.
|
||||
|
||||
{@a hooks-purpose-timing}
|
||||
@ -88,12 +86,10 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Respond after Angular projects external content into the component's view.
|
||||
Respond after Angular projects external content into the component's view / the view that a directive is in.
|
||||
|
||||
Called _once_ after the first `ngDoCheck()`.
|
||||
|
||||
_A component-only hook_.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style='vertical-align:top'>
|
||||
@ -102,12 +98,10 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Respond after Angular checks the content projected into the component.
|
||||
Respond after Angular checks the content projected into the directive/component.
|
||||
|
||||
Called after the `ngAfterContentInit()` and every subsequent `ngDoCheck()`.
|
||||
|
||||
_A component-only hook_.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style='vertical-align:top'>
|
||||
@ -116,12 +110,10 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Respond after Angular initializes the component's views and child views.
|
||||
Respond after Angular initializes the component's views and child views / the view that a directive is in.
|
||||
|
||||
Called _once_ after the first `ngAfterContentChecked()`.
|
||||
|
||||
_A component-only hook_.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style='vertical-align:top'>
|
||||
@ -130,12 +122,10 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Respond after Angular checks the component's views and child views.
|
||||
Respond after Angular checks the component's views and child views / the view that a directive is in.
|
||||
|
||||
Called after the `ngAfterViewInit` and every subsequent `ngAfterContentChecked()`.
|
||||
|
||||
_A component-only hook_.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr style='vertical-align:top'>
|
||||
|
@ -27,7 +27,7 @@ JavaScript modules help you namespace, preventing accidental global variables.
|
||||
## NgModules
|
||||
|
||||
<!-- KW-- perMisko: let's discuss. This does not answer the question why it is different. Also, last sentence is confusing.-->
|
||||
NgModules are classes decorated with `@NgModule`. The `@NgModule` decorator’s `imports` array tells Angular what other NgModules the current module needs. The modules in the imports array are different than JavaScript modules because they are NgModules rather than regular JavaScript modules. Classes with an `@NgModule` decorator are by convention kept in their own files, but what makes them an `NgModule` isn’t being in their own file, like JavaScript modules; it’s the presence of `@NgModule` and its metadata.
|
||||
NgModules are classes decorated with `@NgModule`. The `@NgModule` decorator’s `imports` array tells Angular what other NgModules the current module needs. The modules in the `imports` array are different than JavaScript modules because they are NgModules rather than regular JavaScript modules. Classes with an `@NgModule` decorator are by convention kept in their own files, but what makes them an `NgModule` isn’t being in their own file, like JavaScript modules; it’s the presence of `@NgModule` and its metadata.
|
||||
|
||||
The `AppModule` generated from the Angular CLI demonstrates both kinds of modules in action:
|
||||
|
||||
@ -53,7 +53,7 @@ export class AppModule { }
|
||||
```
|
||||
|
||||
|
||||
The NgModule classes differ from JavaScript module classes in the following key ways:
|
||||
The NgModule classes differ from JavaScript module in the following key ways:
|
||||
|
||||
* An NgModule bounds [declarable classes](guide/ngmodule-faq#q-declarable) only.
|
||||
Declarables are the only classes that matter to the [Angular compiler](guide/ngmodule-faq#q-angular-compiler).
|
||||
|
@ -45,7 +45,7 @@ NgModule metadata does the following:
|
||||
* Declares which components, directives, and pipes belong to the module.
|
||||
* Makes some of those components, directives, and pipes public so that other module's component templates can use them.
|
||||
* Imports other modules with the components, directives, and pipes that components in the current module need.
|
||||
* Provides services at the other application components can use.
|
||||
* Provides services that the other application components can use.
|
||||
|
||||
Every Angular app has at least one module, the root module.
|
||||
You [bootstrap](guide/bootstrapping) that module to launch the application.
|
||||
|
@ -496,7 +496,7 @@ Remember that impure pipes are called every few milliseconds.
|
||||
If you're not careful, this pipe will punish the server with requests.
|
||||
|
||||
In the following code, the pipe only calls the server when the request URL changes and it caches the server response.
|
||||
The code uses the [Angular http](guide/http) client to retrieve data</span>:
|
||||
The code uses the [Angular http](guide/http) client to retrieve data:
|
||||
|
||||
|
||||
<code-example path="pipes/src/app/fetch-json.pipe.ts" title="src/app/fetch-json.pipe.ts">
|
||||
|
@ -10,7 +10,7 @@ see the <live-example></live-example>.
|
||||
<hr>
|
||||
|
||||
## Create a service
|
||||
You can provide services to your app by using the providers array in an NgModule.
|
||||
You can provide services to your app by using the `providers` array in an NgModule.
|
||||
Consider the default app generated by the CLI. In order to add a user service to it,
|
||||
you can generate one by entering the following command in the terminal window:
|
||||
|
||||
@ -20,7 +20,7 @@ ng generate service User
|
||||
|
||||
This creates a service called `UserService`. You now need to make the service available in your
|
||||
app's injector. Update `app.module.ts` by importing it with your other import statements at the top
|
||||
of the file and adding it to the providers array:
|
||||
of the file and adding it to the `providers` array:
|
||||
|
||||
<code-example path="providers/src/app/app.module.ts" title="src/app/app.module.ts" linenums="false">
|
||||
</code-example>
|
||||
@ -28,7 +28,7 @@ of the file and adding it to the providers array:
|
||||
|
||||
## Provider scope
|
||||
|
||||
When you add a service provider to the providers array of the root module, it’s available throughout the app. Additionally, when you import a module that has providers, those providers are also available to all the classes in the app as long they have the lookup token. For example, if you import the `HttpClientModule` into your `AppModule`, its providers are then available to the entire app and you can make HTTP requests from anywhere in your app.
|
||||
When you add a service provider to the `providers` array of the root module, it’s available throughout the app. Additionally, when you import a module that has providers, those providers are also available to all the classes in the app as long they have the lookup token. For example, if you import the `HttpClientModule` into your `AppModule`, its providers are then available to the entire app and you can make HTTP requests from anywhere in your app.
|
||||
|
||||
|
||||
## Limiting provider scope by lazy loading modules
|
||||
|
@ -418,7 +418,7 @@ To see the form model, add the following line after the
|
||||
closing `form` tag in the `hero-detail.component.html`:
|
||||
|
||||
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-3.component.html" region="form-value-json" title="src/app/hero-detail/hero-detail.component.html" linenums="false">
|
||||
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-2.component.html" region="form-value-json" title="src/app/hero-detail/hero-detail.component.html" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
@ -350,3 +350,10 @@ It's the perfect place to reproduce a bug when you want to
|
||||
<a href="https://github.com/angular/angular/issues/new" title="File an Angular issue">file an issue with Angular itself</a>.
|
||||
|
||||
For real development, we strongly recommend [developing locally](guide/setup#develop-locally).
|
||||
|
||||
## Appendix: develop locally with IE
|
||||
|
||||
If you develop angular locally with `ng serve`, there will be `websocket` connection being setup automatically between browser and local dev server, so when your code change, browser can automatically refresh.
|
||||
|
||||
In windows, by default one application can only have 6 websocket connections, <a href="https://msdn.microsoft.com/library/ee330736%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396#websocket_maxconn" title="MSDN WebSocket settings">MSDN WebSocket Settings</a>.
|
||||
So if IE was refreshed manunally or automatically by `ng serve`, sometimes, the websocket will not close properly, when websocket connections exceed limitations, `SecurityError` will be thrown, this error will not affect the angular application, you can just restart IE to clear this error, or modify the windows registry to update the limitations.
|
||||
|
@ -135,3 +135,12 @@ QuickStart identifies two *typings*, or `d.ts`, files:
|
||||
you can view an example in the [webpack](guide/webpack) page.
|
||||
|
||||
QuickStart doesn't require these typings but many of the samples do.
|
||||
|
||||
|
||||
{@a target}
|
||||
|
||||
|
||||
### *target*
|
||||
|
||||
By default, the target is `es5`, you can configure the target to `es6` if you only want to deploy the application to
|
||||
es6 compatible browser. But if you configure the target to `es6` in some old browser such as `IE`, `Syntax Error` will be thrown.
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 25 KiB |
BIN
aio/content/images/marketing/home/ng-conf.png
Normal file
BIN
aio/content/images/marketing/home/ng-conf.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.5 KiB |
9
aio/content/marketing/announcements.json
Normal file
9
aio/content/marketing/announcements.json
Normal file
@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"startDate": "2018-02-14",
|
||||
"endDate": "2018-04-22",
|
||||
"message": "Join us for ng-conf<br/>Apr 18th-20th, 2018",
|
||||
"imageUrl": "generated/images/marketing/home/ng-conf.png",
|
||||
"linkUrl": "http://ng-conf.org/"
|
||||
}
|
||||
]
|
@ -20,7 +20,7 @@
|
||||
|
||||
<div class="feature">
|
||||
<div class="feature-title">Native</div>
|
||||
<p class="text-body">Build native mobile apps with strategies from Ionic Framework, NativeScript, and React Native.</p>
|
||||
<p class="text-body">Build native mobile apps with strategies from Cordova, Ionic, or NativeScript.</p>
|
||||
</div>
|
||||
|
||||
<div class="feature">
|
||||
|
@ -29,14 +29,7 @@
|
||||
|
||||
<div class="home-rows">
|
||||
|
||||
<!-- Announcement Bar -->
|
||||
<div class="homepage-container">
|
||||
<div class="announcement-bar">
|
||||
<img src="generated/images/marketing/home/ng-atl.png">
|
||||
<p>Join us in Atlanta for ngATL<br>Jan 30 - Feb 2, 2018</p>
|
||||
<a class="button" href="http://ng-atl.org/">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
<aio-announcement-bar></aio-announcement-bar>
|
||||
|
||||
<!-- Group 1 -->
|
||||
<div layout="row" layout-xs="column" class="home-row homepage-container">
|
||||
|
@ -241,6 +241,19 @@
|
||||
"rev": true,
|
||||
"title": "NinjaCodeGen - Angular CRUD Generator",
|
||||
"url": "https://ninjaCodeGen.com"
|
||||
},
|
||||
"angular-playground": {
|
||||
"desc": "UI development environment for building, testing, and documenting Angular applications.",
|
||||
"rev": true,
|
||||
"title": "Angular Playground",
|
||||
"url": "http://www.angularplayground.it/"
|
||||
},
|
||||
"nx": {
|
||||
"desc": "Nx (Nrwl Extensions for Angular) is an open source toolkit built on top of Angular CLI to help enterprise teams develop Angular at scale.",
|
||||
"rev": true,
|
||||
"title": "Nx",
|
||||
"logo": "https://nrwl.io/assets/nx-logo.png",
|
||||
"url": "https://nrwl.io/nx"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -438,6 +451,13 @@
|
||||
"rev": true,
|
||||
"title": "Essential Angular",
|
||||
"url": "https://gumroad.com/l/essential_angular"
|
||||
},
|
||||
"angular-buch": {
|
||||
"desc": "The first German book about Angular. It gives you a detailed practical overview of the key concepts of the platform. In each chapter a sample application is built upon with a new Angular topic. All sources are available on GitHub.",
|
||||
"logo": "https://angular-buch.com/assets/img/brand.svg",
|
||||
"rev": true,
|
||||
"title": "Angular-Buch (German)",
|
||||
"url": "https://angular-buch.com/"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -482,6 +502,13 @@
|
||||
"title": "CodeSchool: Accelerating Through Angular",
|
||||
"url": "https://www.codeschool.com/courses/accelerating-through-angular-2"
|
||||
},
|
||||
"angular-playbook": {
|
||||
"desc": "Learn advanced Angular best practices for enterprise teams, created by Nrwl.io.",
|
||||
"logo": "https://nrwl.io/assets/logo_footer_2x.png",
|
||||
"rev": true,
|
||||
"title": "Angular Enterprise Playbook",
|
||||
"url": "https://angularplaybook.com"
|
||||
},
|
||||
"a2b": {
|
||||
"desc": "Hundreds of Angular courses for all skill levels",
|
||||
"logo": "",
|
||||
@ -652,6 +679,13 @@
|
||||
"rev": true,
|
||||
"title": "We Are One Sàrl",
|
||||
"url": "https://weareone.ch/courses/angular/"
|
||||
},
|
||||
"angular-schule": {
|
||||
"desc": "Angular onsite training and public workshops in Germany from the authors of the German Angular book. We also regularly post articles and videos on our blog (in English and German language).",
|
||||
"logo": "https://angular.schule/assets/img/brand.svg",
|
||||
"rev": true,
|
||||
"title": "Angular.Schule (German)",
|
||||
"url": "https://angular.schule/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,6 @@
|
||||
},
|
||||
|
||||
{
|
||||
"url": "tutorial",
|
||||
"title": "Tutorial",
|
||||
"tooltip": "The Tour of Heroes tutorial takes you through the steps of creating an Angular application in TypeScript.",
|
||||
"children": [
|
||||
@ -122,7 +121,6 @@
|
||||
},
|
||||
|
||||
{
|
||||
"url": "guide/architecture",
|
||||
"title": "Fundamentals",
|
||||
"tooltip": "The fundamentals of Angular",
|
||||
"children": [
|
||||
@ -170,6 +168,11 @@
|
||||
"title": "Attribute Directives",
|
||||
"tooltip": "Attribute directives attach behavior to elements."
|
||||
},
|
||||
{
|
||||
"url": "guide/structural-directives",
|
||||
"title": "Structural Directives",
|
||||
"tooltip": "Structural directives manipulate the layout of the page."
|
||||
},
|
||||
{
|
||||
"url": "guide/pipes",
|
||||
"title": "Pipes",
|
||||
|
@ -29,7 +29,7 @@ and annotate the component class with `@Component`.
|
||||
|
||||
The CLI generated three metadata properties:
|
||||
|
||||
1. `selector`— the components CSS element selector
|
||||
1. `selector`— the component's CSS element selector
|
||||
1. `templateUrl`— the location of the component's template file.
|
||||
1. `styleUrls`— the location of the component's private CSS styles.
|
||||
|
||||
|
@ -96,7 +96,7 @@ Bind the `HeroesComponent.selectedHero` to the element's `hero` property like th
|
||||
`[hero]="selectedHero"` is an Angular [property binding](guide/template-syntax#property-binding).
|
||||
|
||||
It's a _one way_ data binding from
|
||||
the `selectedHero` property of the `HeroComponent` to the `hero` property of the target element, which maps to the `hero` property of the `HeroDetailComponent`.
|
||||
the `selectedHero` property of the `HeroesComponent` to the `hero` property of the target element, which maps to the `hero` property of the `HeroDetailComponent`.
|
||||
|
||||
Now when the user clicks a hero in the list, the `selectedHero` changes.
|
||||
When the `selectedHero` changes, the _property binding_ updates `hero`
|
||||
|
@ -97,7 +97,7 @@ Since you did not, you'll have to provide it yourself.
|
||||
|
||||
Open the `AppModule` class, import the `HeroService`, and add it to the `@NgModule.providers` array.
|
||||
|
||||
<code-example path="toh-pt4/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (providers)" region="providers">
|
||||
<code-example path="toh-pt4/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (providers)" region="providers-heroservice">
|
||||
</code-example>
|
||||
|
||||
The `providers` array tells Angular to create a single, shared instance of `HeroService`
|
||||
@ -105,6 +105,12 @@ and inject into any class that asks for it.
|
||||
|
||||
The `HeroService` is now ready to plug into the `HeroesComponent`.
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
This is a interim code sample that will allow you to provide and use the `HeroService`. At this point, the code will differ from the `HeroService` in the ["final code review"](#final-code-review).
|
||||
|
||||
</div>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Learn more about _providers_ in the [Providers](guide/providers) guide.
|
||||
@ -423,6 +429,10 @@ Here are the code files discussed on this page and your app should look like thi
|
||||
path="toh-pt4/src/app/messages/messages.component.css">
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="src/app/app.module.ts"
|
||||
path="toh-pt4/src/app/app.module.ts">
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="src/app/app.component.html"
|
||||
path="toh-pt4/src/app/app.component.html">
|
||||
</code-pane>
|
||||
|
@ -135,7 +135,7 @@ Convert that method to use `HttpClient`
|
||||
Refresh the browser. The hero data should successfully load from the
|
||||
mock server.
|
||||
|
||||
You've swapped `http.get` for `of` and the app keeps working without any other changes
|
||||
You've swapped `of` for `http.get` and the app keeps working without any other changes
|
||||
because both functions return an `Observable<Hero[]>`.
|
||||
|
||||
### Http methods return one value
|
||||
|
28
aio/e2e/search.e2e-spec.ts
Normal file
28
aio/e2e/search.e2e-spec.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { SitePage } from './app.po';
|
||||
|
||||
describe('site search', () => {
|
||||
let page;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new SitePage();
|
||||
page.navigateTo('');
|
||||
});
|
||||
|
||||
it('should find pages when searching by a partial word in the title', () => {
|
||||
page.enterSearch('ngCont');
|
||||
expect(page.getSearchResults()).toContain('NgControl');
|
||||
|
||||
page.enterSearch('accessor');
|
||||
expect(page.getSearchResults()).toContain('ControlValueAccessor');
|
||||
});
|
||||
|
||||
it('should find API docs whose instance member name matches the search query', () => {
|
||||
page.enterSearch('decode');
|
||||
expect(page.getSearchResults()).toContain('HttpParameterCodec');
|
||||
});
|
||||
|
||||
it('should find API docs whose static method name matches the search query', () => {
|
||||
page.enterSearch('compose');
|
||||
expect(page.getSearchResults()).toContain('Validators');
|
||||
});
|
||||
});
|
@ -12,50 +12,98 @@
|
||||
// make sure the routing RegExp in `ngsw-manifest.json` is updated accordingly.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// cli-quickstart.html, glossary.html, quickstart.html, server-communication.html, style-guide.html
|
||||
{"type": 301, "source": "/docs/ts/latest/cli-quickstart.html", "destination": "/guide/quickstart"},
|
||||
{"type": 301, "source": "/docs/ts/latest/glossary.html", "destination": "/guide/glossary"},
|
||||
{"type": 301, "source": "/docs/ts/latest/quickstart.html", "destination": "/guide/quickstart"},
|
||||
{"type": 301, "source": "/docs/ts/latest/guide/server-communication.html", "destination": "/guide/http"},
|
||||
{"type": 301, "source": "/docs/ts/latest/guide/style-guide.html", "destination": "/guide/styleguide"},
|
||||
// A random bad indexed page that used `api/api`
|
||||
{"type": 301, "source": "/api/api/:rest*", "destination": "/api/:rest*"},
|
||||
|
||||
// guide/cli-quickstart, styleguide
|
||||
// Guide renames
|
||||
{"type": 301, "source": "/docs/*/latest/cli-quickstart.html", "destination": "/guide/quickstart"},
|
||||
{"type": 301, "source": "/docs/*/latest/glossary.html", "destination": "/guide/glossary"},
|
||||
{"type": 301, "source": "/docs/*/latest/quickstart.html", "destination": "/guide/quickstart"},
|
||||
{"type": 301, "source": "/docs/*/latest/guide/server-communication.html", "destination": "/guide/http"},
|
||||
{"type": 301, "source": "/docs/*/latest/guide/style-guide.html", "destination": "/guide/styleguide"},
|
||||
{"type": 301, "source": "/guide/cli-quickstart", "destination": "/guide/quickstart"},
|
||||
{"type": 301, "source": "/styleguide", "destination": "/guide/styleguide"},
|
||||
|
||||
// cookbook/a1-a2-quick-reference.html, cookbook/component-communication.html, cookbook/dependency-injection.html
|
||||
{"type": 301, "source": "/docs/ts/latest/cookbook/a1-a2-quick-reference.html", "destination": "/guide/ajs-quick-reference"},
|
||||
{"type": 301, "source": "/docs/ts/latest/cookbook/component-communication.html", "destination": "/guide/component-interaction"},
|
||||
{"type": 301, "source": "/docs/ts/latest/cookbook/dependency-injection.html", "destination": "/guide/dependency-injection-in-action"},
|
||||
|
||||
// cookbook, cookbook/, cookbook/index.html
|
||||
{"type": 301, "source": "/docs/ts/latest/cookbook", "destination": "/docs"},
|
||||
{"type": 301, "source": "/docs/ts/latest/cookbook/", "destination": "/docs"},
|
||||
{"type": 301, "source": "/docs/ts/latest/cookbook/index.html", "destination": "/docs"},
|
||||
|
||||
// cookbook/*.html
|
||||
{"type": 301, "source": "/docs/ts/latest/cookbook/:cookbook.html", "destination": "/guide/:cookbook"},
|
||||
|
||||
// docs/ts/latest/api/<package>/index/*-<type>.html (+ special case for `NgFor` which has been renamed)
|
||||
{"type": 301, "source": "/docs/ts/latest/api/common/index/NgFor-directive.html", "destination": "/api/common/NgForOf"},
|
||||
{"type": 301, "source": "/docs/ts/latest/api/:package/index/:api-*.html", "destination": "/api/:package/:api"},
|
||||
|
||||
// docs/ts/latest
|
||||
{"type": 301, "source": "/docs/ts/latest", "destination": "/docs"},
|
||||
|
||||
// guide/*, tutorial/*, **/*
|
||||
{"type": 301, "source": "/docs/ts/latest/:any*", "destination": "/:any*"},
|
||||
|
||||
// aot-compiler.md and metadata.md combined into aot-compiler.md - issue #19510
|
||||
{"type": 301, "source": "/guide/metadata", "destination": "/guide/aot-compiler"},
|
||||
|
||||
// ngmodule.md renamed to ngmodules.md
|
||||
{"type": 301, "source": "/guide/ngmodule", "destination": "/guide/ngmodules"},
|
||||
|
||||
// service-worker-getstart.md, service-worker-comm.md, service-worker-configref.md
|
||||
{"type": 301, "source": "/guide/service-worker-getstart", "destination": "/guide/service-worker-getting-started"},
|
||||
{"type": 301, "source": "/guide/service-worker-comm", "destination": "/guide/service-worker-communications"},
|
||||
{"type": 301, "source": "/guide/service-worker-configref", "destination": "/guide/service-worker-config"}
|
||||
{"type": 301, "source": "/guide/service-worker-configref", "destination": "/guide/service-worker-config"},
|
||||
|
||||
// some top level guide pages on old site were moved below the guide folder
|
||||
{"type": 301, "source": "/styleguide", "destination": "/guide/styleguide"},
|
||||
{"type": 301, "source": "/docs/styleguide", "destination": "/guide/styleguide"},
|
||||
|
||||
// news is now blog
|
||||
{"type": 301, "source": "/news*", "destination": "https://blog.angular.io/"},
|
||||
|
||||
// cookbook guides were moved (and sometime renamed or removed)
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook", "destination": "/docs"},
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook/", "destination": "/docs"},
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook/index.html", "destination": "/docs"},
|
||||
{"type": 301, "source": "/**/cookbook/ts-to-js*", "destination": "https://github.com/angular/angular/blob/master/aio/content/guide/change-log.md#es6--described-in-typescript-to-javascript-2016-11-14"},
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook/a1-a2-quick-reference.html", "destination": "/guide/ajs-quick-reference"},
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook/component-communication.html", "destination": "/guide/component-interaction"},
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook/dependency-injection.html", "destination": "/guide/dependency-injection-in-action"},
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook/:cookbook.html", "destination": "/guide/:cookbook"},
|
||||
|
||||
// Forms related code was moved from the `common` to `forms` package (and NgFor was renamed to NgForOf)
|
||||
{"type": 301, "source": "/**/NgFor-*", "destination": "/api/common/NgForOf"},
|
||||
{"type": 301, "source": "/**/api/common/index/MaxLengthValidator-*", "destination": "/api/forms/MaxLengthValidator"},
|
||||
{"type": 301, "source": "/**/api/common/ControlGroup*", "destination": "/api/forms/FormGroup"},
|
||||
{"type": 301, "source": "/**/api/common/Control*", "destination": "/api/forms/FormControl"},
|
||||
{"type": 301, "source": "/**/api/common/SelectControlValueAccessor-*", "destination": "/api/forms/SelectControlValueAccessor"},
|
||||
{"type": 301, "source": "/**/api/common/NgModel", "destination": "/api/forms/NgModel"},
|
||||
|
||||
// Animations moves, renames and removals
|
||||
{"type": 301, "source": "/api/animate/:rest*", "destination": "/api/animations/:rest*"},
|
||||
// AnimationStateDeclarationMetadata was removed
|
||||
{"type": 301, "source": "/**/AnimationStateDeclarationMetadata*", "destination": "/api/animations"},
|
||||
// `AnimationDriver` was moved to the `animations/browser` package
|
||||
{"type": 301, "source": "/api/platform-browser/AnimationDriver", "destination": "/api/animations/browser/AnimationDriver"},
|
||||
|
||||
// The `testing` package was renamed to `core/testing`
|
||||
{"type": 301, "source": "/api/testing/:api-*", "destination": "/api/core/testing/:api"},
|
||||
|
||||
// CORE_DIRECTIVES & PLATFORM_PIPES were removed and are now in the CommonModule
|
||||
{"type": 301, "source": "/**/CORE_DIRECTIVES*", "destination": "/api/common/CommonModule"},
|
||||
{"type": 301, "source": "/**/PLATFORM_PIPES*", "destination": "/api/common/CommonModule"},
|
||||
|
||||
// DirectiveMetadata is now covered by the Directive decorator
|
||||
{"type": 301, "source": "/**/DirectiveMetadata-*", "destination": "/api/core/Directive"},
|
||||
|
||||
// OptionalMetadata is now covered by the Optional decorator
|
||||
{"type": 301, "source": "/**/OptionalMetadata-*", "destination": "/api/core/Optional"},
|
||||
|
||||
// HTTP_PROVIDERS was removed and is now provided in HttpModule
|
||||
{"type": 301, "source": "/**/HTTP_PROVIDERS*", "destination": "/api/http/HttpModule"},
|
||||
|
||||
// URLs that use the old scheme of adding the type to the end (e.g. `SomeClass-class`)
|
||||
{"type": 301, "source": "/api/:package/:api-*", "destination": "/api/:package/:api"},
|
||||
{"type": 301, "source": "/api/:package/testing/index/:api-*", "destination": "/api/:package/testing/:api"},
|
||||
{"type": 301, "source": "/api/:package/testing/:api-*", "destination": "/api/:package/testing/:api"},
|
||||
{"type": 301, "source": "/api/upgrade/:package/index/:api-*", "destination": "/api/upgrade/:package/:api"},
|
||||
{"type": 301, "source": "/api/upgrade/:package/:api-*", "destination": "/api/upgrade/:package/:api"},
|
||||
|
||||
// URLs that use the old scheme before we moved the docs to the angular/angular repo
|
||||
{"type": 301, "source": "/docs/*/latest", "destination": "/docs"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/", "destination": "/api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/:package", "destination": "/api/:package"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/testing/:api-*", "destination": "/api/core/testing/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/:package/:api-*", "destination": "/api/:package/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/:package/index/:api-*", "destination": "/api/:package/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/:package/testing", "destination": "/api/:package/testing"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/:package/testing/index/:api-*", "destination": "/api/:package/testing/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/platform-browser/animations/index/:api-*", "destination": "/api/platform-browser/animations/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/upgrade/:package/:api-*", "destination": "/api/upgrade/:package/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/upgrade/:package/index/:api-*", "destination": "/api/upgrade/:package/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/glossary", "destination": "/guide/glossary"},
|
||||
{"type": 301, "source": "/docs/*/latest/guide/", "destination": "/docs"},
|
||||
{"type": 301, "source": "/docs/*/latest/guide/lifecycle-hooks", "destination": "/guide/lifecycle-hooks"},
|
||||
{"type": 301, "source": "/docs/*/latest/:rest*", "destination": "/:rest*"},
|
||||
{"type": 301, "source": "/docs/latest/:rest*", "destination": "/:rest*"},
|
||||
{"type": 301, "source": "/docs/styleguide*", "destination": "/guide/styleguide"},
|
||||
{"type": 301, "source": "/guide/metadata", "destination": "/guide/aot-compiler"},
|
||||
{"type": 301, "source": "/guide/ngmodule", "destination": "/guide/ngmodules"},
|
||||
{"type": 301, "source": "/guide/learning-angular*", "destination": "/guide/quickstart"},
|
||||
{"type": 301, "source": "/testing", "destination": "/guide/testing"},
|
||||
{"type": 301, "source": "/testing/**", "destination": "/guide/testing"}
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
|
@ -19,7 +19,7 @@
|
||||
"routing": {
|
||||
"index": "/index.html",
|
||||
"routes": {
|
||||
"^(?!/docs/ts/latest|/guide/(?:cli-quickstart|metadata|ngmodule|service-worker-(?:getstart|comm|configref))/?$|/styleguide).*/(?!e?plnkr)[^/.]*$": {
|
||||
"^(?!/styleguide|/docs/.|(?:/guide/(?:cli-quickstart|metadata|ngmodule|service-worker-(?:getstart|comm|configref)|learning-angular)|/news)(?:\\.html|/)?$|/testing|/api/(?:.+/[^/]+-|platform-browser/AnimationDriver|testing|api/|(?:common/(?:NgModel|Control|MaxLengthValidator))|(?:[^/]+/)?(?:NgFor(?:$|-)|AnimationStateDeclarationMetadata|CORE_DIRECTIVES|PLATFORM_PIPES|DirectiveMetadata|HTTP_PROVIDERS))|.*/stackblitz(?:\\.html)?$|.*\\.[^\/.]+$)": {
|
||||
"match": "regex"
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
"build": "yarn ~~build",
|
||||
"prebuild-local": "yarn setup-local",
|
||||
"build-local": "yarn ~~build",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
||||
"test": "yarn check-env && ng test",
|
||||
"pree2e": "yarn check-env && yarn ~~update-webdriver",
|
||||
"e2e": "ng e2e --no-webdriver-update",
|
||||
@ -23,7 +23,7 @@
|
||||
"preinstall": "node ../tools/yarn/check-yarn.js",
|
||||
"presetup": "yarn install --frozen-lockfile && yarn ~~check-env && yarn boilerplate:remove",
|
||||
"setup": "yarn aio-use-npm && yarn example-use-npm",
|
||||
"postsetup": "yarn boilerplate:add && yarn build-ie-polyfills && yarn generate-stackblitz && yarn generate-zips && yarn docs",
|
||||
"postsetup": "yarn boilerplate:add && yarn build-ie-polyfills && yarn docs",
|
||||
"presetup-local": "yarn presetup",
|
||||
"setup-local": "yarn aio-use-local && yarn example-use-local",
|
||||
"postsetup-local": "yarn postsetup",
|
||||
@ -39,11 +39,15 @@
|
||||
"check-env": "yarn ~~check-env",
|
||||
"postcheck-env": "yarn aio-check-local",
|
||||
"payload-size": "scripts/payload.sh",
|
||||
"predocs": "yarn generate-stackblitz && yarn generate-zips",
|
||||
"docs": "dgeni ./tools/transforms/angular.io-package",
|
||||
"docs-watch": "node tools/transforms/authors-package/watchr.js",
|
||||
"docs-lint": "eslint --ignore-path=\"tools/transforms/.eslintignore\" tools/transforms",
|
||||
"docs-test": "node tools/transforms/test.js",
|
||||
"tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test && yarn boilerplate:test && jasmine tools/ng-packages-installer/index.spec.js",
|
||||
"deployment-config-test": "jasmine-ts tests/deployment/**/*.spec.ts",
|
||||
"firebase-utils-test": "jasmine-ts tools/firebase-test-utils/*.spec.ts",
|
||||
"tools-lint": "tslint -c \"tools/tslint.json\" \"tools/firebase-test-utils/**/*.ts\"",
|
||||
"tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test && yarn boilerplate:test && jasmine tools/ng-packages-installer/index.spec.js && yarn firebase-utils-test",
|
||||
"preserve-and-sync": "yarn docs",
|
||||
"serve-and-sync": "concurrently --kill-others \"yarn docs-watch --watch-only\" \"yarn start\"",
|
||||
"boilerplate:add": "node ./tools/examples/example-boilerplate add",
|
||||
@ -97,12 +101,13 @@
|
||||
"archiver": "^1.3.0",
|
||||
"canonical-path": "^0.0.2",
|
||||
"chalk": "^2.1.0",
|
||||
"cjson": "^0.5.0",
|
||||
"codelyzer": "~2.0.0",
|
||||
"concurrently": "^3.4.0",
|
||||
"cross-spawn": "^5.1.0",
|
||||
"css-selector-parser": "^1.3.0",
|
||||
"dgeni": "^0.4.7",
|
||||
"dgeni-packages": "^0.24.0",
|
||||
"dgeni-packages": "0.24.1",
|
||||
"entities": "^1.1.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
@ -117,6 +122,7 @@
|
||||
"image-size": "^0.5.1",
|
||||
"jasmine-core": "^2.8.0",
|
||||
"jasmine-spec-reporter": "^4.1.0",
|
||||
"jasmine-ts": "^0.2.1",
|
||||
"jsdom": "^9.12.0",
|
||||
"karma": "^1.7.0",
|
||||
"karma-chrome-launcher": "^2.1.1",
|
||||
@ -146,6 +152,7 @@
|
||||
"unist-util-visit-parents": "^1.1.1",
|
||||
"vrsource-tslint-rules": "^4.0.1",
|
||||
"watchr": "^3.0.1",
|
||||
"xregexp": "^4.0.0",
|
||||
"yargs": "^7.0.2"
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
<mat-progress-bar mode="indeterminate" color="warn"></mat-progress-bar>
|
||||
</div>
|
||||
|
||||
<mat-toolbar color="primary" class="app-toolbar" [class.transitioning]="isTransitioning">
|
||||
<mat-toolbar color="primary" class="app-toolbar no-print" [class.transitioning]="isTransitioning">
|
||||
<mat-toolbar-row class="notification-container">
|
||||
<aio-notification
|
||||
icon="insert_comment"
|
||||
@ -18,7 +18,7 @@
|
||||
</aio-notification>
|
||||
</mat-toolbar-row>
|
||||
<mat-toolbar-row>
|
||||
<button mat-button class="hamburger" (click)="sidenav.toggle()" title="Docs menu">
|
||||
<button mat-button class="hamburger" [class.starting]="isStarting" (click)="sidenav.toggle()" title="Docs menu">
|
||||
<mat-icon svgIcon="menu"></mat-icon>
|
||||
</button>
|
||||
<a class="nav-link home" href="/" [ngSwitch]="isSideBySide">
|
||||
@ -29,6 +29,7 @@
|
||||
<aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box>
|
||||
</mat-toolbar-row>
|
||||
</mat-toolbar>
|
||||
|
||||
<aio-search-results #searchResultsView *ngIf="showSearchResults" [searchResults]="searchResults | async" (resultSelected)="hideSearchResults()"></aio-search-results>
|
||||
|
||||
<mat-sidenav-container class="sidenav-container" [class.starting]="isStarting" [class.has-floating-toc]="hasFloatingToc" role="main">
|
||||
@ -56,11 +57,11 @@
|
||||
|
||||
</mat-sidenav-container>
|
||||
|
||||
<div *ngIf="hasFloatingToc" class="toc-container" [style.max-height.px]="tocMaxHeight" (mousewheel)="restrainScrolling($event)">
|
||||
<div *ngIf="hasFloatingToc" class="toc-container no-print" [style.max-height.px]="tocMaxHeight" (mousewheel)="restrainScrolling($event)">
|
||||
<aio-toc></aio-toc>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<footer class="no-print">
|
||||
<aio-footer [nodes]="footerNodes" [versionInfo]="versionInfo" ></aio-footer>
|
||||
</footer>
|
||||
|
||||
|
@ -8,9 +8,12 @@ import { By } from '@angular/platform-browser';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import { timer } from 'rxjs/observable/timer';
|
||||
import 'rxjs/add/operator/mapTo';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppModule } from './app.module';
|
||||
import { DocumentService } from 'app/documents/document.service';
|
||||
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
|
||||
import { Deployment } from 'app/shared/deployment.service';
|
||||
import { EmbedComponentsService } from 'app/embed-components/embed-components.service';
|
||||
@ -31,18 +34,30 @@ import { TocItem, TocService } from 'app/shared/toc.service';
|
||||
|
||||
const sideBySideBreakPoint = 992;
|
||||
const hideToCBreakPoint = 800;
|
||||
const startedDelay = 100;
|
||||
|
||||
describe('AppComponent', () => {
|
||||
let component: AppComponent;
|
||||
let fixture: ComponentFixture<AppComponent>;
|
||||
|
||||
let documentService: DocumentService;
|
||||
let docViewer: HTMLElement;
|
||||
let docViewerComponent: DocViewerComponent;
|
||||
let hamburger: HTMLButtonElement;
|
||||
let locationService: MockLocationService;
|
||||
let sidenav: MatSidenav;
|
||||
let tocService: TocService;
|
||||
|
||||
const initializeTest = () => {
|
||||
async function awaitDocRendered() {
|
||||
const newDocPromise = new Promise(resolve => documentService.currentDocument.subscribe(resolve));
|
||||
const docRenderedPromise = new Promise(resolve => docViewerComponent.docRendered.subscribe(resolve));
|
||||
|
||||
await newDocPromise; // Wait for the new document to be fetched.
|
||||
fixture.detectChanges(); // Propagate document change to the view (i.e to `DocViewer`).
|
||||
await docRenderedPromise; // Wait for the `docRendered` event.
|
||||
};
|
||||
|
||||
function initializeTest(waitForDoc = true) {
|
||||
fixture = TestBed.createComponent(AppComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
@ -50,21 +65,27 @@ describe('AppComponent', () => {
|
||||
component.onResize(sideBySideBreakPoint + 1); // wide by default
|
||||
|
||||
const de = fixture.debugElement;
|
||||
docViewer = de.query(By.css('aio-doc-viewer')).nativeElement;
|
||||
const docViewerDe = de.query(By.css('aio-doc-viewer'));
|
||||
|
||||
documentService = de.injector.get(DocumentService) as DocumentService;
|
||||
docViewer = docViewerDe.nativeElement;
|
||||
docViewerComponent = docViewerDe.componentInstance;
|
||||
hamburger = de.query(By.css('.hamburger')).nativeElement;
|
||||
locationService = de.injector.get(LocationService) as any as MockLocationService;
|
||||
locationService = de.injector.get(LocationService) as any;
|
||||
sidenav = de.query(By.directive(MatSidenav)).componentInstance;
|
||||
tocService = de.injector.get(TocService);
|
||||
|
||||
return waitForDoc && awaitDocRendered();
|
||||
};
|
||||
|
||||
|
||||
describe('with proper DocViewer', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
DocViewerComponent.animationsEnabled = false;
|
||||
|
||||
createTestingModule('a/b');
|
||||
initializeTest();
|
||||
await initializeTest();
|
||||
});
|
||||
|
||||
afterEach(() => DocViewerComponent.animationsEnabled = true);
|
||||
@ -356,43 +377,43 @@ describe('AppComponent', () => {
|
||||
let selectElement: DebugElement;
|
||||
let selectComponent: SelectComponent;
|
||||
|
||||
function setupSelectorForTesting(mode?: string) {
|
||||
async function setupSelectorForTesting(mode?: string) {
|
||||
createTestingModule('a/b', mode);
|
||||
initializeTest();
|
||||
await initializeTest();
|
||||
component.onResize(sideBySideBreakPoint + 1); // side-by-side
|
||||
selectElement = fixture.debugElement.query(By.directive(SelectComponent));
|
||||
selectComponent = selectElement.componentInstance;
|
||||
}
|
||||
|
||||
it('should select the version that matches the deploy mode', () => {
|
||||
setupSelectorForTesting();
|
||||
it('should select the version that matches the deploy mode', async () => {
|
||||
await setupSelectorForTesting();
|
||||
expect(selectComponent.selected.title).toContain('stable');
|
||||
setupSelectorForTesting('next');
|
||||
await setupSelectorForTesting('next');
|
||||
expect(selectComponent.selected.title).toContain('next');
|
||||
setupSelectorForTesting('archive');
|
||||
await setupSelectorForTesting('archive');
|
||||
expect(selectComponent.selected.title).toContain('v4');
|
||||
});
|
||||
|
||||
it('should add the current raw version string to the selected version', () => {
|
||||
setupSelectorForTesting();
|
||||
it('should add the current raw version string to the selected version', async () => {
|
||||
await setupSelectorForTesting();
|
||||
expect(selectComponent.selected.title).toContain(`(v${component.versionInfo.raw})`);
|
||||
setupSelectorForTesting('next');
|
||||
await setupSelectorForTesting('next');
|
||||
expect(selectComponent.selected.title).toContain(`(v${component.versionInfo.raw})`);
|
||||
setupSelectorForTesting('archive');
|
||||
await setupSelectorForTesting('archive');
|
||||
expect(selectComponent.selected.title).toContain(`(v${component.versionInfo.raw})`);
|
||||
});
|
||||
|
||||
// Older docs versions have an href
|
||||
it('should navigate when change to a version with a url', () => {
|
||||
setupSelectorForTesting();
|
||||
it('should navigate when change to a version with a url', async () => {
|
||||
await setupSelectorForTesting();
|
||||
const versionWithUrlIndex = component.docVersions.findIndex(v => !!v.url);
|
||||
const versionWithUrl = component.docVersions[versionWithUrlIndex];
|
||||
selectElement.triggerEventHandler('change', { option: versionWithUrl, index: versionWithUrlIndex});
|
||||
expect(locationService.go).toHaveBeenCalledWith(versionWithUrl.url);
|
||||
});
|
||||
|
||||
it('should not navigate when change to a version without a url', () => {
|
||||
setupSelectorForTesting();
|
||||
it('should not navigate when change to a version without a url', async () => {
|
||||
await setupSelectorForTesting();
|
||||
const versionWithoutUrlIndex = component.docVersions.length;
|
||||
const versionWithoutUrl = component.docVersions[versionWithoutUrlIndex] = { title: 'foo' };
|
||||
selectElement.triggerEventHandler('change', { option: versionWithoutUrl, index: versionWithoutUrlIndex });
|
||||
@ -401,37 +422,39 @@ describe('AppComponent', () => {
|
||||
});
|
||||
|
||||
describe('currentDocument', () => {
|
||||
it('should display a guide page (guide/pipes)', () => {
|
||||
locationService.go('guide/pipes');
|
||||
fixture.detectChanges();
|
||||
const navigateTo = async (path: string) => {
|
||||
locationService.go(path);
|
||||
await awaitDocRendered();
|
||||
};
|
||||
|
||||
it('should display a guide page (guide/pipes)', async () => {
|
||||
await navigateTo('guide/pipes');
|
||||
expect(docViewer.textContent).toMatch(/Pipes/i);
|
||||
});
|
||||
|
||||
it('should display the api page', () => {
|
||||
locationService.go('api');
|
||||
fixture.detectChanges();
|
||||
it('should display the api page', async () => {
|
||||
await navigateTo('api');
|
||||
expect(docViewer.textContent).toMatch(/API/i);
|
||||
});
|
||||
|
||||
it('should display a marketing page', () => {
|
||||
locationService.go('features');
|
||||
fixture.detectChanges();
|
||||
it('should display a marketing page', async () => {
|
||||
await navigateTo('features');
|
||||
expect(docViewer.textContent).toMatch(/Features/i);
|
||||
});
|
||||
|
||||
it('should update the document title', () => {
|
||||
it('should update the document title', async () => {
|
||||
const titleService = TestBed.get(Title);
|
||||
spyOn(titleService, 'setTitle');
|
||||
locationService.go('guide/pipes');
|
||||
fixture.detectChanges();
|
||||
|
||||
await navigateTo('guide/pipes');
|
||||
expect(titleService.setTitle).toHaveBeenCalledWith('Angular - Pipes');
|
||||
});
|
||||
|
||||
it('should update the document title, with a default value if the document has no title', () => {
|
||||
it('should update the document title, with a default value if the document has no title', async () => {
|
||||
const titleService = TestBed.get(Title);
|
||||
spyOn(titleService, 'setTitle');
|
||||
locationService.go('no-title');
|
||||
fixture.detectChanges();
|
||||
|
||||
await navigateTo('no-title');
|
||||
expect(titleService.setTitle).toHaveBeenCalledWith('Angular');
|
||||
});
|
||||
});
|
||||
@ -509,7 +532,9 @@ describe('AppComponent', () => {
|
||||
expect(scrollToTopSpy).not.toHaveBeenCalled();
|
||||
|
||||
locationService.go('guide/pipes');
|
||||
tick(1); // triggers the HTTP response for the document
|
||||
fixture.detectChanges(); // triggers the event that calls `onDocInserted`
|
||||
|
||||
expect(scrollToTopSpy).toHaveBeenCalled();
|
||||
expect(scrollSpy).not.toHaveBeenCalled();
|
||||
|
||||
@ -658,18 +683,16 @@ describe('AppComponent', () => {
|
||||
});
|
||||
|
||||
describe('deployment banner', () => {
|
||||
it('should show a message if the deployment mode is "archive"', () => {
|
||||
it('should show a message if the deployment mode is "archive"', async () => {
|
||||
createTestingModule('a/b', 'archive');
|
||||
initializeTest();
|
||||
fixture.detectChanges();
|
||||
await initializeTest();
|
||||
const banner: HTMLElement = fixture.debugElement.query(By.css('aio-mode-banner')).nativeElement;
|
||||
expect(banner.textContent).toContain('archived documentation for Angular v4');
|
||||
});
|
||||
|
||||
it('should show no message if the deployment mode is not "archive"', () => {
|
||||
it('should show no message if the deployment mode is not "archive"', async () => {
|
||||
createTestingModule('a/b', 'stable');
|
||||
initializeTest();
|
||||
fixture.detectChanges();
|
||||
await initializeTest();
|
||||
const banner: HTMLElement = fixture.debugElement.query(By.css('aio-mode-banner')).nativeElement;
|
||||
expect(banner.textContent!.trim()).toEqual('');
|
||||
});
|
||||
@ -678,7 +701,6 @@ describe('AppComponent', () => {
|
||||
describe('search', () => {
|
||||
describe('initialization', () => {
|
||||
it('should initialize the search worker', inject([SearchService], (searchService: SearchService) => {
|
||||
fixture.detectChanges(); // triggers ngOnInit
|
||||
expect(searchService.initWorker).toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
@ -771,103 +793,103 @@ describe('AppComponent', () => {
|
||||
describe('archive redirection', () => {
|
||||
it('should redirect to `docs` if deployment mode is `archive` and not at a docs page', () => {
|
||||
createTestingModule('', 'archive');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).toHaveBeenCalledWith('docs');
|
||||
|
||||
createTestingModule('resources', 'archive');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).toHaveBeenCalledWith('docs');
|
||||
|
||||
createTestingModule('guide/aot-compiler', 'archive');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('tutorial', 'archive');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('tutorial/toh-pt1', 'archive');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('docs', 'archive');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('api', 'archive');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('api/core/getPlatform', 'archive');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not redirect if deployment mode is `next`', () => {
|
||||
createTestingModule('', 'next');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('resources', 'next');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('guide/aot-compiler', 'next');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('tutorial', 'next');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('tutorial/toh-pt1', 'next');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('docs', 'next');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('api', 'next');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('api/core/getPlatform', 'next');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not redirect to `docs` if deployment mode is `stable`', () => {
|
||||
createTestingModule('', 'stable');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('resources', 'stable');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('guide/aot-compiler', 'stable');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('tutorial', 'stable');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('tutorial/toh-pt1', 'stable');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('docs', 'stable');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('api', 'stable');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
|
||||
createTestingModule('api/core/getPlatform', 'stable');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@ -889,27 +911,68 @@ describe('AppComponent', () => {
|
||||
});
|
||||
|
||||
describe('initial rendering', () => {
|
||||
it('should initially add the starting class until a document is rendered', () => {
|
||||
const getSidenavContainer = () => fixture.debugElement.query(By.css('mat-sidenav-container'));
|
||||
beforeEach(jasmine.clock().install);
|
||||
afterEach(jasmine.clock().uninstall);
|
||||
|
||||
initializeTest();
|
||||
it('should initially disable Angular animations until a document is rendered', () => {
|
||||
initializeTest(false);
|
||||
jasmine.clock().tick(1); // triggers the HTTP response for the document
|
||||
|
||||
expect(component.isStarting).toBe(true);
|
||||
expect(getSidenavContainer().classes['starting']).toBe(true);
|
||||
expect(fixture.debugElement.properties['@.disabled']).toBe(true);
|
||||
|
||||
triggerDocViewerEvent('docInserted');
|
||||
jasmine.clock().tick(startedDelay);
|
||||
fixture.detectChanges();
|
||||
expect(component.isStarting).toBe(true);
|
||||
expect(fixture.debugElement.properties['@.disabled']).toBe(true);
|
||||
|
||||
triggerDocViewerEvent('docRendered');
|
||||
jasmine.clock().tick(startedDelay);
|
||||
fixture.detectChanges();
|
||||
expect(component.isStarting).toBe(false);
|
||||
expect(getSidenavContainer().classes['starting']).toBe(false);
|
||||
expect(fixture.debugElement.properties['@.disabled']).toBe(false);
|
||||
});
|
||||
|
||||
it('should initially add the starting class until a document is rendered', () => {
|
||||
initializeTest(false);
|
||||
jasmine.clock().tick(1); // triggers the HTTP response for the document
|
||||
const sidenavContainer = fixture.debugElement.query(By.css('mat-sidenav-container')).nativeElement;
|
||||
|
||||
expect(component.isStarting).toBe(true);
|
||||
expect(hamburger.classList.contains('starting')).toBe(true);
|
||||
expect(sidenavContainer.classList.contains('starting')).toBe(true);
|
||||
|
||||
triggerDocViewerEvent('docInserted');
|
||||
jasmine.clock().tick(startedDelay);
|
||||
fixture.detectChanges();
|
||||
expect(component.isStarting).toBe(true);
|
||||
expect(hamburger.classList.contains('starting')).toBe(true);
|
||||
expect(sidenavContainer.classList.contains('starting')).toBe(true);
|
||||
|
||||
triggerDocViewerEvent('docRendered');
|
||||
jasmine.clock().tick(startedDelay);
|
||||
fixture.detectChanges();
|
||||
expect(component.isStarting).toBe(false);
|
||||
expect(hamburger.classList.contains('starting')).toBe(false);
|
||||
expect(sidenavContainer.classList.contains('starting')).toBe(false);
|
||||
});
|
||||
|
||||
it('should initially disable animations on the DocViewer for the first rendering', () => {
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
jasmine.clock().tick(1); // triggers the HTTP response for the document
|
||||
|
||||
expect(component.isStarting).toBe(true);
|
||||
expect(docViewer.classList.contains('no-animations')).toBe(true);
|
||||
|
||||
triggerDocViewerEvent('docInserted');
|
||||
jasmine.clock().tick(startedDelay);
|
||||
fixture.detectChanges();
|
||||
expect(component.isStarting).toBe(true);
|
||||
expect(docViewer.classList.contains('no-animations')).toBe(true);
|
||||
|
||||
triggerDocViewerEvent('docRendered');
|
||||
jasmine.clock().tick(startedDelay);
|
||||
fixture.detectChanges();
|
||||
expect(component.isStarting).toBe(false);
|
||||
expect(docViewer.classList.contains('no-animations')).toBe(false);
|
||||
@ -921,50 +984,63 @@ describe('AppComponent', () => {
|
||||
afterEach(jasmine.clock().uninstall);
|
||||
|
||||
it('should set the transitioning class on `.app-toolbar` while a document is being rendered', () => {
|
||||
const getToolbar = () => fixture.debugElement.query(By.css('.app-toolbar'));
|
||||
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
jasmine.clock().tick(1); // triggers the HTTP response for the document
|
||||
const toolbar = fixture.debugElement.query(By.css('.app-toolbar'));
|
||||
|
||||
// Initially, `isTransitoning` is true.
|
||||
expect(component.isTransitioning).toBe(true);
|
||||
expect(getToolbar().classes['transitioning']).toBe(true);
|
||||
expect(toolbar.classes['transitioning']).toBe(true);
|
||||
|
||||
triggerDocViewerEvent('docRendered');
|
||||
fixture.detectChanges();
|
||||
expect(component.isTransitioning).toBe(false);
|
||||
expect(getToolbar().classes['transitioning']).toBe(false);
|
||||
expect(toolbar.classes['transitioning']).toBe(false);
|
||||
|
||||
// While a document is being rendered, `isTransitoning` is set to true.
|
||||
triggerDocViewerEvent('docReady');
|
||||
fixture.detectChanges();
|
||||
expect(component.isTransitioning).toBe(true);
|
||||
expect(getToolbar().classes['transitioning']).toBe(true);
|
||||
expect(toolbar.classes['transitioning']).toBe(true);
|
||||
|
||||
triggerDocViewerEvent('docRendered');
|
||||
fixture.detectChanges();
|
||||
expect(component.isTransitioning).toBe(false);
|
||||
expect(getToolbar().classes['transitioning']).toBe(false);
|
||||
expect(toolbar.classes['transitioning']).toBe(false);
|
||||
});
|
||||
|
||||
it('should update the sidenav state as soon as a new document is inserted', () => {
|
||||
initializeTest();
|
||||
it('should update the sidenav state as soon as a new document is inserted (but not before)', () => {
|
||||
initializeTest(false);
|
||||
jasmine.clock().tick(1); // triggers the HTTP response for the document
|
||||
jasmine.clock().tick(0); // calls `updateSideNav()` for initial rendering
|
||||
const updateSideNavSpy = spyOn(component, 'updateSideNav');
|
||||
|
||||
triggerDocViewerEvent('docReady');
|
||||
jasmine.clock().tick(0);
|
||||
expect(updateSideNavSpy).not.toHaveBeenCalled();
|
||||
|
||||
triggerDocViewerEvent('docInserted');
|
||||
jasmine.clock().tick(0);
|
||||
expect(updateSideNavSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
updateSideNavSpy.calls.reset();
|
||||
|
||||
triggerDocViewerEvent('docReady');
|
||||
jasmine.clock().tick(0);
|
||||
expect(updateSideNavSpy).not.toHaveBeenCalled();
|
||||
|
||||
triggerDocViewerEvent('docInserted');
|
||||
jasmine.clock().tick(0);
|
||||
expect(updateSideNavSpy).toHaveBeenCalledTimes(2);
|
||||
expect(updateSideNavSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pageId', () => {
|
||||
const navigateTo = (path: string) => {
|
||||
locationService.go(path);
|
||||
jasmine.clock().tick(1); // triggers the HTTP response for the document
|
||||
triggerDocViewerEvent('docInserted');
|
||||
jasmine.clock().tick(0);
|
||||
jasmine.clock().tick(0); // triggers `updateHostClasses()`
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
@ -972,7 +1048,7 @@ describe('AppComponent', () => {
|
||||
afterEach(jasmine.clock().uninstall);
|
||||
|
||||
it('should set the id of the doc viewer container based on the current doc', () => {
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
const container = fixture.debugElement.query(By.css('section.sidenav-content'));
|
||||
|
||||
navigateTo('guide/pipes');
|
||||
@ -989,7 +1065,7 @@ describe('AppComponent', () => {
|
||||
});
|
||||
|
||||
it('should not be affected by changes to the query', () => {
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
const container = fixture.debugElement.query(By.css('section.sidenav-content'));
|
||||
|
||||
navigateTo('guide/pipes');
|
||||
@ -1002,8 +1078,9 @@ describe('AppComponent', () => {
|
||||
|
||||
describe('hostClasses', () => {
|
||||
const triggerUpdateHostClasses = () => {
|
||||
jasmine.clock().tick(1); // triggers the HTTP response for document
|
||||
triggerDocViewerEvent('docInserted');
|
||||
jasmine.clock().tick(0);
|
||||
jasmine.clock().tick(0); // triggers `updateHostClasses()`
|
||||
fixture.detectChanges();
|
||||
};
|
||||
const navigateTo = (path: string) => {
|
||||
@ -1015,7 +1092,7 @@ describe('AppComponent', () => {
|
||||
afterEach(jasmine.clock().uninstall);
|
||||
|
||||
it('should set the css classes of the host container based on the current doc and navigation view', () => {
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
|
||||
navigateTo('guide/pipes');
|
||||
checkHostClass('page', 'guide-pipes');
|
||||
@ -1034,7 +1111,7 @@ describe('AppComponent', () => {
|
||||
});
|
||||
|
||||
it('should set the css class of the host container based on the open/closed state of the side nav', async () => {
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
|
||||
navigateTo('guide/pipes');
|
||||
checkHostClass('sidenav', 'open');
|
||||
@ -1059,7 +1136,7 @@ describe('AppComponent', () => {
|
||||
|
||||
it('should set the css class of the host container based on the initial deployment mode', () => {
|
||||
createTestingModule('a/b', 'archive');
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
|
||||
triggerUpdateHostClasses();
|
||||
checkHostClass('mode', 'archive');
|
||||
@ -1079,13 +1156,13 @@ describe('AppComponent', () => {
|
||||
const HIDE_DELAY = 500;
|
||||
const getProgressBar = () => fixture.debugElement.query(By.directive(MatProgressBar));
|
||||
const initializeAndCompleteNavigation = () => {
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
triggerDocViewerEvent('docReady');
|
||||
tick(HIDE_DELAY);
|
||||
};
|
||||
|
||||
it('should initially be hidden', () => {
|
||||
initializeTest();
|
||||
initializeTest(false);
|
||||
expect(getProgressBar()).toBeFalsy();
|
||||
});
|
||||
|
||||
@ -1300,6 +1377,8 @@ class TestHttpClient {
|
||||
const contents = `${h1}<h2 id="#somewhere">Some heading</h2>`;
|
||||
data = { id, contents };
|
||||
}
|
||||
return of(data);
|
||||
|
||||
// Preserve async nature of `HttpClient`.
|
||||
return timer(1).mapTo(data);
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ export class AppComponent implements OnInit {
|
||||
|
||||
currentDocument: DocumentContents;
|
||||
currentDocVersion: NavigationNode;
|
||||
currentNodes: CurrentNodes;
|
||||
currentNodes: CurrentNodes = {};
|
||||
currentPath: string;
|
||||
docVersions: NavigationNode[];
|
||||
dtOn = false;
|
||||
@ -57,9 +57,11 @@ export class AppComponent implements OnInit {
|
||||
@HostBinding('class')
|
||||
hostClasses = '';
|
||||
|
||||
isFetching = false;
|
||||
// Disable all Angular animations for the initial render.
|
||||
@HostBinding('@.disabled')
|
||||
isStarting = true;
|
||||
isTransitioning = true;
|
||||
isFetching = false;
|
||||
isSideBySide = false;
|
||||
private isFetchingTimeout: any;
|
||||
private isSideNavDoc = false;
|
||||
@ -118,12 +120,6 @@ export class AppComponent implements OnInit {
|
||||
/* No need to unsubscribe because this root component never dies */
|
||||
|
||||
this.documentService.currentDocument.subscribe(doc => this.currentDocument = doc);
|
||||
// Generally, we want to delay updating the host classes for the new document, until after the
|
||||
// leaving document has been removed (to avoid having the styles for the new document applied
|
||||
// prematurely).
|
||||
// On the first document, though, (when we know there is no previous document), we want to
|
||||
// ensure the styles are applied as soon as possible to avoid flicker.
|
||||
this.documentService.currentDocument.first().subscribe(doc => this.updateHostClassesForDoc(doc));
|
||||
|
||||
this.locationService.currentPath.subscribe(path => {
|
||||
// Redirect to docs if we are in archive mode and are not hitting a docs page
|
||||
@ -175,11 +171,22 @@ export class AppComponent implements OnInit {
|
||||
this.topMenuNarrowNodes = views['TopBarNarrow'] || this.topMenuNodes;
|
||||
});
|
||||
|
||||
this.navigationService.versionInfo.subscribe( vi => this.versionInfo = vi );
|
||||
this.navigationService.versionInfo.subscribe(vi => this.versionInfo = vi);
|
||||
|
||||
const hasNonEmptyToc = this.tocService.tocList.map(tocList => tocList.length > 0);
|
||||
combineLatest(hasNonEmptyToc, this.showFloatingToc)
|
||||
.subscribe(([hasToc, showFloatingToc]) => this.hasFloatingToc = hasToc && showFloatingToc);
|
||||
|
||||
// Generally, we want to delay updating the shell (e.g. host classes, sidenav state) for the new
|
||||
// document, until after the leaving document has been removed (to avoid having the styles for
|
||||
// the new document applied prematurely).
|
||||
// For the first document, though, (when we know there is no previous document), we want to
|
||||
// ensure the styles are applied as soon as possible to avoid flicker.
|
||||
combineLatest(
|
||||
this.documentService.currentDocument, // ...needed to determine host classes
|
||||
this.navigationService.currentNodes) // ...needed to determine `sidenav` state
|
||||
.first()
|
||||
.subscribe(() => this.updateShell());
|
||||
}
|
||||
|
||||
// Scroll to the anchor in the hash fragment or top of doc.
|
||||
@ -205,14 +212,11 @@ export class AppComponent implements OnInit {
|
||||
}
|
||||
|
||||
onDocInserted() {
|
||||
// TODO: Find a better way to avoid `ExpressionChangedAfterItHasBeenChecked` error.
|
||||
setTimeout(() => {
|
||||
// Update the SideNav state (if necessary).
|
||||
this.updateSideNav();
|
||||
|
||||
// Update the host classes to match the new document.
|
||||
this.updateHostClassesForDoc(this.currentDocument);
|
||||
});
|
||||
// Update the shell (host classes, sidenav state) to match the new document.
|
||||
// This may be called as a result of actions initiated by view updates.
|
||||
// In order to avoid errors (e.g. `ExpressionChangedAfterItHasBeenChecked`), updating the view
|
||||
// (e.g. sidenav, host classes) needs to happen asynchronously.
|
||||
setTimeout(() => this.updateShell());
|
||||
|
||||
// Scroll 500ms after the new document has been inserted into the doc-viewer.
|
||||
// The delay is to allow time for async layout to complete.
|
||||
@ -220,7 +224,14 @@ export class AppComponent implements OnInit {
|
||||
}
|
||||
|
||||
onDocRendered() {
|
||||
this.isStarting = false;
|
||||
if (this.isStarting) {
|
||||
// In order to ensure that the initial sidenav-content left margin
|
||||
// adjustment happens without animation, we need to ensure that
|
||||
// `isStarting` remains `true` until the margin change is triggered.
|
||||
// (Apparently, this happens with a slight delay.)
|
||||
setTimeout(() => this.isStarting = false, 100);
|
||||
}
|
||||
|
||||
this.isTransitioning = false;
|
||||
}
|
||||
|
||||
@ -241,7 +252,7 @@ export class AppComponent implements OnInit {
|
||||
// items in the top-bar, ensure the sidenav is closed.
|
||||
// (This condition can only be met when the resize event changes the value of `isSideBySide`
|
||||
// from `false` to `true` while on a non-sidenav doc.)
|
||||
this.sideNavToggle(false);
|
||||
this.sidenav.toggle(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,10 +283,6 @@ export class AppComponent implements OnInit {
|
||||
return true;
|
||||
}
|
||||
|
||||
sideNavToggle(value?: boolean) {
|
||||
this.sidenav.toggle(value);
|
||||
}
|
||||
|
||||
setPageId(id: string) {
|
||||
// Special case the home page
|
||||
this.pageId = (id === 'index') ? 'home' : id.replace('/', '-');
|
||||
@ -300,7 +307,7 @@ export class AppComponent implements OnInit {
|
||||
const sideNavOpen = `sidenav-${this.sidenav.opened ? 'open' : 'closed'}`;
|
||||
const pageClass = `page-${this.pageId}`;
|
||||
const folderClass = `folder-${this.folderId}`;
|
||||
const viewClasses = Object.keys(this.currentNodes || {}).map(view => `view-${view}`).join(' ');
|
||||
const viewClasses = Object.keys(this.currentNodes).map(view => `view-${view}`).join(' ');
|
||||
const notificationClass = `aio-notification-${this.notification.showNotification}`;
|
||||
const notificationAnimatingClass = this.notificationAnimating ? 'aio-notification-animating' : '';
|
||||
|
||||
@ -315,9 +322,13 @@ export class AppComponent implements OnInit {
|
||||
].join(' ');
|
||||
}
|
||||
|
||||
updateHostClassesForDoc(doc: DocumentContents) {
|
||||
this.setPageId(doc.id);
|
||||
this.setFolderId(doc.id);
|
||||
updateShell() {
|
||||
// Update the SideNav state (if necessary).
|
||||
this.updateSideNav();
|
||||
|
||||
// Update the host classes.
|
||||
this.setPageId(this.currentDocument.id);
|
||||
this.setFolderId(this.currentDocument.id);
|
||||
this.updateHostClasses();
|
||||
}
|
||||
|
||||
@ -333,7 +344,7 @@ export class AppComponent implements OnInit {
|
||||
}
|
||||
|
||||
// May be open or closed when wide; always closed when narrow.
|
||||
this.sideNavToggle(this.isSideBySide && openSideNav);
|
||||
this.sidenav.toggle(this.isSideBySide && openSideNav);
|
||||
}
|
||||
|
||||
// Dynamically change height of table of contents container
|
||||
|
@ -23,14 +23,16 @@ describe('AppModule', () => {
|
||||
});
|
||||
|
||||
it('should provide a list of eagerly-loaded embedded components', () => {
|
||||
const eagerSelector = Object.keys(componentsMap).find(selector => Array.isArray(componentsMap[selector]))!;
|
||||
const selectorCount = eagerSelector.split(',').length;
|
||||
|
||||
expect(eagerSelector).not.toBeNull();
|
||||
expect(selectorCount).toBe(componentsMap[eagerSelector].length);
|
||||
const eagerConfig = Object.keys(componentsMap).filter(selector => Array.isArray(componentsMap[selector]));
|
||||
expect(eagerConfig.length).toBeGreaterThan(0);
|
||||
|
||||
const eagerSelectors = eagerConfig.reduce<string[]>((selectors, config) => selectors.concat(config.split(',')), []);
|
||||
expect(eagerSelectors.length).toBeGreaterThan(0);
|
||||
|
||||
// For example...
|
||||
expect(eagerSelector).toContain('aio-toc');
|
||||
expect(eagerSelectors).toContain('aio-toc');
|
||||
expect(eagerSelectors).toContain('aio-announcement-bar');
|
||||
});
|
||||
|
||||
it('should provide a list of lazy-loaded embedded components', () => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ErrorHandler, NgModule } from '@angular/core';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
@ -14,6 +14,7 @@ import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { ROUTES } from '@angular/router';
|
||||
|
||||
|
||||
import { AnnouncementBarComponent } from 'app/embedded/announcement-bar/announcement-bar.component';
|
||||
import { AppComponent } from 'app/app.component';
|
||||
import { EMBEDDED_COMPONENTS, EmbeddedComponentsMap } from 'app/embed-components/embed-components.service';
|
||||
import { CustomIconRegistry, SVG_ICONS } from 'app/shared/custom-icon-registry';
|
||||
@ -31,6 +32,7 @@ import { TopMenuComponent } from 'app/layout/top-menu/top-menu.component';
|
||||
import { FooterComponent } from 'app/layout/footer/footer.component';
|
||||
import { NavMenuComponent } from 'app/layout/nav-menu/nav-menu.component';
|
||||
import { NavItemComponent } from 'app/layout/nav-item/nav-item.component';
|
||||
import { ReportingErrorHandler } from 'app/shared/reporting-error-handler';
|
||||
import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { ScrollSpyService } from 'app/shared/scroll-spy.service';
|
||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
@ -109,6 +111,7 @@ export const svgIconProviders = [
|
||||
SharedModule
|
||||
],
|
||||
declarations: [
|
||||
AnnouncementBarComponent,
|
||||
AppComponent,
|
||||
DocViewerComponent,
|
||||
DtComponent,
|
||||
@ -124,6 +127,7 @@ export const svgIconProviders = [
|
||||
providers: [
|
||||
Deployment,
|
||||
DocumentService,
|
||||
{ provide: ErrorHandler, useClass: ReportingErrorHandler },
|
||||
GaService,
|
||||
Logger,
|
||||
Location,
|
||||
@ -143,6 +147,7 @@ export const svgIconProviders = [
|
||||
provide: EMBEDDED_COMPONENTS,
|
||||
useValue: {
|
||||
/* tslint:disable: max-line-length */
|
||||
'aio-announcement-bar': [AnnouncementBarComponent],
|
||||
'aio-toc': [TocComponent],
|
||||
'aio-api-list, aio-contributor-list, aio-file-not-found-search, aio-resource-list, code-example, code-tabs, current-location, live-example': embeddedModulePath,
|
||||
/* tslint:enable: max-line-length */
|
||||
@ -156,7 +161,7 @@ export const svgIconProviders = [
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
entryComponents: [ TocComponent ],
|
||||
entryComponents: [ AnnouncementBarComponent, TocComponent ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule {
|
||||
|
@ -0,0 +1,109 @@
|
||||
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
import { MockLogger } from 'testing/logger.service';
|
||||
import { AnnouncementBarComponent } from './announcement-bar.component';
|
||||
|
||||
const today = new Date();
|
||||
const lastWeek = changeDays(today, -7);
|
||||
const yesterday = changeDays(today, -1);
|
||||
const tomorrow = changeDays(today, 1);
|
||||
const nextWeek = changeDays(today, 7);
|
||||
|
||||
describe('AnnouncementBarComponent', () => {
|
||||
|
||||
let element: HTMLElement;
|
||||
let fixture: ComponentFixture<AnnouncementBarComponent>;
|
||||
let component: AnnouncementBarComponent;
|
||||
let httpMock: HttpTestingController;
|
||||
let mockLogger: MockLogger;
|
||||
|
||||
beforeEach(() => {
|
||||
const injector = TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule],
|
||||
declarations: [AnnouncementBarComponent],
|
||||
providers: [{ provide: Logger, useClass: MockLogger }]
|
||||
});
|
||||
|
||||
httpMock = injector.get(HttpTestingController);
|
||||
mockLogger = injector.get(Logger);
|
||||
fixture = TestBed.createComponent(AnnouncementBarComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
});
|
||||
|
||||
it('should have no announcement when first created', () => {
|
||||
expect(component.announcement).toBeUndefined();
|
||||
});
|
||||
|
||||
describe('ngOnInit', () => {
|
||||
it('should make a single request to the server', () => {
|
||||
component.ngOnInit();
|
||||
httpMock.expectOne('generated/announcements.json');
|
||||
});
|
||||
|
||||
it('should set the announcement to the first "live" one in the list loaded from `announcements.json`', () => {
|
||||
component.ngOnInit();
|
||||
const request = httpMock.expectOne('generated/announcements.json');
|
||||
request.flush([
|
||||
{ startDate: lastWeek, endDate: yesterday, message: 'Test Announcement 0' },
|
||||
{ startDate: tomorrow, endDate: nextWeek, message: 'Test Announcement 1' },
|
||||
{ startDate: yesterday, endDate: tomorrow, message: 'Test Announcement 2' },
|
||||
{ startDate: yesterday, endDate: tomorrow, message: 'Test Announcement 3' }
|
||||
]);
|
||||
expect(component.announcement.message).toEqual('Test Announcement 2');
|
||||
});
|
||||
|
||||
it('should set the announcement to `undefined` if there are no announcements in `announcements.json`', () => {
|
||||
component.ngOnInit();
|
||||
const request = httpMock.expectOne('generated/announcements.json');
|
||||
request.flush([]);
|
||||
expect(component.announcement).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle invalid data in `announcements.json`', () => {
|
||||
component.ngOnInit();
|
||||
const request = httpMock.expectOne('generated/announcements.json');
|
||||
request.flush('some random response');
|
||||
expect(component.announcement).toBeUndefined();
|
||||
expect(mockLogger.output.error[0][0]).toContain('generated/announcements.json contains invalid data:');
|
||||
});
|
||||
|
||||
it('should handle a failed request for `announcements.json`', () => {
|
||||
component.ngOnInit();
|
||||
const request = httpMock.expectOne('generated/announcements.json');
|
||||
request.error(new ErrorEvent('404'));
|
||||
expect(component.announcement).toBeUndefined();
|
||||
expect(mockLogger.output.error[0][0]).toContain('generated/announcements.json request failed:');
|
||||
});
|
||||
});
|
||||
|
||||
describe('rendering', () => {
|
||||
beforeEach(() => {
|
||||
component.announcement = {
|
||||
imageUrl: 'link/to/image',
|
||||
linkUrl: 'link/to/website',
|
||||
message: 'this is an <b>important</b> message',
|
||||
endDate: '2018-03-01',
|
||||
startDate: '2018-02-01'
|
||||
};
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the message as HTML', () => {
|
||||
expect(element.innerHTML).toContain('this is an <b>important</b> message');
|
||||
});
|
||||
|
||||
it('should display an image', () => {
|
||||
expect(element.querySelector('img')!.src).toContain('link/to/image');
|
||||
});
|
||||
|
||||
it('should display a link', () => {
|
||||
expect(element.querySelector('a')!.href).toContain('link/to/website');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function changeDays(initial: Date, days: number) {
|
||||
return (new Date(initial.valueOf()).setDate(initial.getDate() + days));
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
import { CONTENT_URL_PREFIX } from 'app/documents/document.service';
|
||||
const announcementsPath = CONTENT_URL_PREFIX + 'announcements.json';
|
||||
|
||||
export interface Announcement {
|
||||
imageUrl: string;
|
||||
message: string;
|
||||
linkUrl: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the latest live announcement. This is used on the homepage.
|
||||
*
|
||||
* The data for the announcements is kept in `aio/content/marketing/announcements.json`.
|
||||
*
|
||||
* The format for that data file looks like:
|
||||
*
|
||||
* ```
|
||||
* [
|
||||
* {
|
||||
* "startDate": "2018-02-01",
|
||||
* "endDate": "2018-03-01",
|
||||
* "message": "This is an <b>important</b> announcement",
|
||||
* "imageUrl": "url/to/image",
|
||||
* "linkUrl": "url/to/website"
|
||||
* },
|
||||
* ...
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Only one announcement will be shown at any time. This is determined as the first "live"
|
||||
* announcement in the file, where "live" means that its start date is before today, and its
|
||||
* end date is after today.
|
||||
*
|
||||
* **Security Note:**
|
||||
* The `message` field can contain unsanitized HTML but this field should only updated by
|
||||
* verified members of the Angular team.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'aio-announcement-bar',
|
||||
template: `
|
||||
<div class="homepage-container" *ngIf="announcement">
|
||||
<div class="announcement-bar">
|
||||
<img [src]="announcement.imageUrl">
|
||||
<p [innerHTML]="announcement.message"></p>
|
||||
<a class="button" [href]="announcement.linkUrl">Learn More</a>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
export class AnnouncementBarComponent implements OnInit {
|
||||
announcement: Announcement;
|
||||
|
||||
constructor(private http: HttpClient, private logger: Logger) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.http.get<Announcement[]>(announcementsPath)
|
||||
.catch(error => {
|
||||
this.logger.error(`${announcementsPath} request failed: ${error.message}`);
|
||||
return [];
|
||||
})
|
||||
.map(announcements => this.findCurrentAnnouncement(announcements))
|
||||
.catch(error => {
|
||||
this.logger.error(`${announcementsPath} contains invalid data: ${error.message}`);
|
||||
return [];
|
||||
})
|
||||
.subscribe(announcement => this.announcement = announcement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first date in the list that is "live" now
|
||||
*/
|
||||
private findCurrentAnnouncement(announcements: Announcement[]) {
|
||||
return announcements
|
||||
.filter(announcement => new Date(announcement.startDate).valueOf() < Date.now())
|
||||
.filter(announcement => new Date(announcement.endDate).valueOf() > Date.now())
|
||||
[0];
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ const defaultLineNumsCount = 10; // by default, show linenums over this number
|
||||
selector: 'aio-code',
|
||||
template: `
|
||||
<pre class="prettyprint lang-{{language}}">
|
||||
<button *ngIf="!hideCopy" class="material-icons copy-button"
|
||||
<button *ngIf="!hideCopy" class="material-icons copy-button no-print"
|
||||
title="Copy code snippet"
|
||||
[attr.aria-label]="ariaLabel"
|
||||
(click)="doCopy()">
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div *ngIf="type !== 'None'" class="toc-inner" [class.collapsed]="isCollapsed">
|
||||
<div *ngIf="type !== 'None'" class="toc-inner no-print" [class.collapsed]="isCollapsed">
|
||||
|
||||
<div *ngIf="type === 'EmbeddedSimple'" class="toc-heading embedded">
|
||||
Contents
|
||||
|
@ -30,6 +30,9 @@ export class GaService {
|
||||
}
|
||||
|
||||
ga(...args: any[]) {
|
||||
(this.window as any)['ga'](...args);
|
||||
const gaFn = (this.window as any)['ga'];
|
||||
if (gaFn) {
|
||||
gaFn(...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
46
aio/src/app/shared/logger.service.spec.ts
Normal file
46
aio/src/app/shared/logger.service.spec.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { ErrorHandler, ReflectiveInjector } from '@angular/core';
|
||||
import { Logger } from './logger.service';
|
||||
|
||||
describe('logger service', () => {
|
||||
let logSpy: jasmine.Spy;
|
||||
let warnSpy: jasmine.Spy;
|
||||
let logger: Logger;
|
||||
let errorHandler: ErrorHandler;
|
||||
|
||||
beforeEach(() => {
|
||||
logSpy = spyOn(console, 'log');
|
||||
warnSpy = spyOn(console, 'warn');
|
||||
const injector = ReflectiveInjector.resolveAndCreate([
|
||||
Logger,
|
||||
{ provide: ErrorHandler, useClass: MockErrorHandler }
|
||||
]);
|
||||
logger = injector.get(Logger);
|
||||
errorHandler = injector.get(ErrorHandler);
|
||||
});
|
||||
|
||||
describe('log', () => {
|
||||
it('should delegate to console.log', () => {
|
||||
logger.log('param1', 'param2', 'param3');
|
||||
expect(console.log).toHaveBeenCalledWith('param1', 'param2', 'param3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('warn', () => {
|
||||
it('should delegate to console.warn', () => {
|
||||
logger.warn('param1', 'param2', 'param3');
|
||||
expect(console.warn).toHaveBeenCalledWith('param1', 'param2', 'param3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('error', () => {
|
||||
it('should delegate to ErrorHandler', () => {
|
||||
logger.error('param1', 'param2', 'param3');
|
||||
expect(errorHandler.handleError).toHaveBeenCalledWith('param1 param2 param3');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
class MockErrorHandler implements ErrorHandler {
|
||||
handleError = jasmine.createSpy('handleError');
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ErrorHandler, Injectable } from '@angular/core';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class Logger {
|
||||
|
||||
constructor(private errorHandler: ErrorHandler) {}
|
||||
|
||||
log(value: any, ...rest: any[]) {
|
||||
if (!environment.production) {
|
||||
console.log(value, ...rest);
|
||||
@ -12,7 +14,8 @@ export class Logger {
|
||||
}
|
||||
|
||||
error(value: any, ...rest: any[]) {
|
||||
console.error(value, ...rest);
|
||||
const message = [value, ...rest].join(' ');
|
||||
this.errorHandler.handleError(message);
|
||||
}
|
||||
|
||||
warn(value: any, ...rest: any[]) {
|
||||
|
64
aio/src/app/shared/reporting-error-handler.spec.ts
Normal file
64
aio/src/app/shared/reporting-error-handler.spec.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { ErrorHandler, ReflectiveInjector } from '@angular/core';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { WindowToken } from 'app/shared/window';
|
||||
import { AppModule } from 'app/app.module';
|
||||
|
||||
import { ReportingErrorHandler } from './reporting-error-handler';
|
||||
|
||||
describe('ReportingErrorHandler service', () => {
|
||||
let handler: ReportingErrorHandler;
|
||||
let superHandler: jasmine.Spy;
|
||||
let onerrorSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
onerrorSpy = jasmine.createSpy('onerror');
|
||||
superHandler = spyOn(ErrorHandler.prototype, 'handleError');
|
||||
|
||||
const injector = ReflectiveInjector.resolveAndCreate([
|
||||
{ provide: ErrorHandler, useClass: ReportingErrorHandler },
|
||||
{ provide: WindowToken, useFactory: () => ({ onerror: onerrorSpy }) }
|
||||
]);
|
||||
handler = injector.get(ErrorHandler);
|
||||
});
|
||||
|
||||
it('should be registered on the AppModule', () => {
|
||||
handler = TestBed.configureTestingModule({ imports: [AppModule] }).get(ErrorHandler);
|
||||
expect(handler).toEqual(jasmine.any(ReportingErrorHandler));
|
||||
});
|
||||
|
||||
describe('handleError', () => {
|
||||
it('should call the super class handleError', () => {
|
||||
const error = new Error();
|
||||
handler.handleError(error);
|
||||
expect(superHandler).toHaveBeenCalledWith(error);
|
||||
});
|
||||
|
||||
it('should cope with the super handler throwing an error', () => {
|
||||
const error = new Error('initial error');
|
||||
superHandler.and.throwError('super handler error');
|
||||
handler.handleError(error);
|
||||
|
||||
expect(onerrorSpy).toHaveBeenCalledTimes(2);
|
||||
|
||||
// Error from super handler is reported first
|
||||
expect(onerrorSpy.calls.argsFor(0)[0]).toEqual('super handler error');
|
||||
expect(onerrorSpy.calls.argsFor(0)[4]).toEqual(jasmine.any(Error));
|
||||
|
||||
// Then error from initial exception
|
||||
expect(onerrorSpy.calls.argsFor(1)[0]).toEqual('initial error');
|
||||
expect(onerrorSpy.calls.argsFor(1)[4]).toEqual(error);
|
||||
});
|
||||
|
||||
it('should send an error object to window.onerror', () => {
|
||||
const error = new Error('this is an error message');
|
||||
handler.handleError(error);
|
||||
expect(onerrorSpy).toHaveBeenCalledWith(error.message, undefined, undefined, undefined, error);
|
||||
});
|
||||
|
||||
it('should send an error string to window.onerror', () => {
|
||||
const error = 'this is an error message';
|
||||
handler.handleError(error);
|
||||
expect(onerrorSpy).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
});
|
37
aio/src/app/shared/reporting-error-handler.ts
Normal file
37
aio/src/app/shared/reporting-error-handler.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { ErrorHandler, Inject, Injectable } from '@angular/core';
|
||||
import { WindowToken } from './window';
|
||||
|
||||
/**
|
||||
* Extend the default error handling to report errors to an external service - e.g Google Analytics.
|
||||
*
|
||||
* Errors outside the Angular application may also be handled by `window.onerror`.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ReportingErrorHandler extends ErrorHandler {
|
||||
|
||||
constructor(@Inject(WindowToken) private window: Window) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send error info to Google Analytics, in addition to the default handling.
|
||||
* @param error Information about the error.
|
||||
*/
|
||||
handleError(error: string | Error) {
|
||||
|
||||
try {
|
||||
super.handleError(error);
|
||||
} catch (e) {
|
||||
this.reportError(e);
|
||||
}
|
||||
this.reportError(error);
|
||||
}
|
||||
|
||||
private reportError(error: string | Error) {
|
||||
if (typeof error === 'string') {
|
||||
this.window.onerror(error);
|
||||
} else {
|
||||
this.window.onerror(error.message, undefined, undefined, undefined, error);
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,14 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="translucent">
|
||||
|
||||
<script>
|
||||
// Dynamically, pre-emptively, add `noindex`, which will be removed when the doc is ready and valid
|
||||
var tag = document.createElement('meta'); tag.name = 'googlebot'; tag.content = 'noindex';
|
||||
document.head.appendChild(tag);
|
||||
tag = document.createElement('meta'); tag.name = 'robots'; tag.content = 'noindex';
|
||||
document.head.appendChild(tag);
|
||||
</script>
|
||||
|
||||
<!-- Google Analytics -->
|
||||
<script>
|
||||
// Note this is a customised version of the GA tracking snippet
|
||||
@ -44,6 +52,38 @@
|
||||
</script>
|
||||
<!-- End Google Analytics -->
|
||||
|
||||
<script>
|
||||
// Report fatal errors to Google Analytics
|
||||
window.onerror = function() {
|
||||
ga('send', 'exception', {exDescription: formatError.apply(null, arguments), exFatal: true});
|
||||
|
||||
function formatError(msg, url, line, col, e) {
|
||||
var stack;
|
||||
msg = msg.replace(/^Error: /, '');
|
||||
if (e) {
|
||||
stack = e.stack
|
||||
// strip the leading "Error: " from the stack trace
|
||||
.replace(/^Error: /, '')
|
||||
// strip the message from the stack trace, if present
|
||||
.replace(msg + '\n', '')
|
||||
// strip leading spaces
|
||||
.replace(/^ +/gm, '')
|
||||
// strip all leading "at " for each frame
|
||||
.replace(/^at /gm, '')
|
||||
// replace long urls with just the last segment: `filename:line:column`
|
||||
.replace(/(?: \(|@)http.+\/([^/)]+)\)?(?:\n|$)/gm, '@$1\n')
|
||||
// replace "eval code" in Edge
|
||||
.replace(/ *\(eval code(:\d+:\d+)\)(?:\n|$)/gm, '@???$1\n')
|
||||
} else {
|
||||
line = line || '?';
|
||||
col = col || '?';
|
||||
stack = url + ':' + line + ':' + col;
|
||||
}
|
||||
return (msg + '\n' + stack).substr(0, 150);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
if (window.document.documentMode) {
|
||||
// polyfill IE11 in a blocking way
|
||||
|
@ -143,13 +143,13 @@ th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
p > code, li > code, table code {
|
||||
p > code, li > code, td > code, th > code {
|
||||
font-family: $code-font;
|
||||
font-size: 85%;
|
||||
color: $darkgray;
|
||||
letter-spacing: 0;
|
||||
line-height: 1;
|
||||
padding: 2px 6px;
|
||||
padding: 2px 0;
|
||||
background-color: $backgroundgray;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
text-transform: none;
|
||||
padding: 8px 24px;
|
||||
}
|
||||
|
||||
|
||||
tbody {
|
||||
pre {
|
||||
white-space: normal;
|
||||
@ -36,34 +36,44 @@
|
||||
}
|
||||
}
|
||||
|
||||
.api-body {
|
||||
|
||||
.api-header label {
|
||||
border-radius: 4px;
|
||||
padding: 4px 16px;
|
||||
display: inline;
|
||||
font-size: 14px;
|
||||
color: white;
|
||||
margin: 0 8px 0 16px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
display: block;
|
||||
margin: 8px 0;
|
||||
}
|
||||
max-width: 1200px;
|
||||
|
||||
&.api-status-label {
|
||||
background-color: $mediumgray;
|
||||
}
|
||||
table {
|
||||
|
||||
&.api-type-label {
|
||||
background-color: $accentblue;
|
||||
th {
|
||||
text-transform: none;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@each $name, $symbol in $api-symbols {
|
||||
&.#{$name} {
|
||||
background: map-get($symbol, background);
|
||||
tr {
|
||||
border-bottom: 1px solid $lightgray;
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
tr:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.item-table {
|
||||
td {
|
||||
padding: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
&.list-table {
|
||||
td {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -37,3 +37,11 @@ aio-shell.page-docs {
|
||||
margin: 24px 0px;
|
||||
background: $lightgray;
|
||||
}
|
||||
|
||||
.page-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
right: 24px;
|
||||
}
|
@ -12,3 +12,4 @@
|
||||
@import 'sidenav';
|
||||
@import 'table-of-contents';
|
||||
@import 'top-menu';
|
||||
@import 'print-layout';
|
@ -125,6 +125,7 @@ section#intro {
|
||||
.announcement-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
max-width: 50vw;
|
||||
|
110
aio/src/styles/1-layouts/_print-layout.scss
Normal file
110
aio/src/styles/1-layouts/_print-layout.scss
Normal file
@ -0,0 +1,110 @@
|
||||
@media print {
|
||||
|
||||
// General Adjustments
|
||||
* {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
h1 {
|
||||
height: 40px !important;
|
||||
color: $darkgray !important;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
ul, ol, img, code-example, table, tr, .alert, .l-subsection, .feature {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
table tbody tr:last-child td {
|
||||
border-bottom: 1px solid $lightgray !important;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
p {
|
||||
widows: 4;
|
||||
}
|
||||
|
||||
p > code, li > code, table code {
|
||||
color: $blue !important;
|
||||
}
|
||||
|
||||
// No Print Class
|
||||
.no-print {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// Custom No Print for Sidenav Menu
|
||||
mat-sidenav.sidenav.mat-sidenav {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// Custom No Print Element Adjustments
|
||||
.mat-sidenav-content {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
mat-sidenav-container.sidenav-container {
|
||||
min-width: 100vw;
|
||||
}
|
||||
|
||||
.sidenav-content {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.filetree {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
aio-code code {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
code-example {
|
||||
pre.lang-bash code span {
|
||||
color: $mediumgray !important;
|
||||
}
|
||||
|
||||
pre.lang-sh code span {
|
||||
color: $darkgray !important;
|
||||
}
|
||||
|
||||
header {
|
||||
border: 0.5px solid $lightgray;
|
||||
color: $darkgray;
|
||||
}
|
||||
}
|
||||
|
||||
.content code {
|
||||
border: 0.5px solid $lightgray;
|
||||
}
|
||||
|
||||
.mat-tab-labels {
|
||||
div.mat-tab-label {
|
||||
&:not(.mat-tab-label-active) span {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
&.mat-tab-label-active span {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.api-header label {
|
||||
color: $darkgray !important;
|
||||
font-weight: bold !important;
|
||||
margin: 2px !important;
|
||||
padding: 0 !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.feature-section img {
|
||||
max-width: 70px !important;
|
||||
}
|
||||
}
|
@ -1,11 +1,6 @@
|
||||
// Disable sidenav animations while starting the app
|
||||
// See https://github.com/angular/material2/blob/master/src/lib/sidenav/sidenav-transitions.scss
|
||||
.starting.mat-sidenav-transition {
|
||||
.mat-sidenav,
|
||||
.mat-sidenav-content,
|
||||
.mat-sidenav-backdrop.mat-sidenav-shown {
|
||||
transition: none;
|
||||
}
|
||||
// Disable sidenav animations for the initial render.
|
||||
.starting.mat-drawer-transition .mat-drawer-content {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
aio-nav-menu {
|
||||
@ -42,19 +37,19 @@ mat-sidenav.mat-sidenav.sidenav {
|
||||
}
|
||||
|
||||
mat-sidenav-container.sidenav-container {
|
||||
min-height: 100%;
|
||||
height: auto !important;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
transform: none;
|
||||
min-height: 100%;
|
||||
height: auto !important;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
transform: none;
|
||||
|
||||
&.has-floating-toc {
|
||||
max-width: 82%;
|
||||
}
|
||||
&.has-floating-toc {
|
||||
max-width: 82%;
|
||||
}
|
||||
}
|
||||
|
||||
mat-sidenav-container div.mat-sidenav-content {
|
||||
height: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.vertical-menu-item {
|
||||
@ -128,6 +123,12 @@ button.vertical-menu-item {
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
.no-animations {
|
||||
.heading-children.expanded, .heading-children.collapsed {
|
||||
transition: none! important;
|
||||
}
|
||||
}
|
||||
|
||||
.level-1 {
|
||||
font-family: $main-font;
|
||||
font-size: 14px;
|
||||
@ -159,7 +160,6 @@ button.vertical-menu-item {
|
||||
|
||||
.level-1:not(.expanded) .mat-icon, .level-2:not(.expanded) .mat-icon {
|
||||
@include rotate(0deg);
|
||||
// margin: 4px;
|
||||
}
|
||||
|
||||
aio-nav-menu.top-menu {
|
||||
|
@ -68,9 +68,12 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
|
||||
height: 100%;
|
||||
margin: $hamburgerShownMargin;
|
||||
padding: 0;
|
||||
transition-duration: .4s;
|
||||
transition-property: color, margin;
|
||||
transition-timing-function: cubic-bezier(.25, .8, .25, 1);
|
||||
|
||||
&:not(.starting) {
|
||||
transition-duration: .4s;
|
||||
transition-property: color, margin;
|
||||
transition-timing-function: cubic-bezier(.25, .8, .25, 1);
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
// Hamburger hidden by default on large screens.
|
||||
|
@ -1,23 +1,102 @@
|
||||
.api-info-bar {
|
||||
max-width: 800px;
|
||||
text-align: left;
|
||||
.api-body {
|
||||
|
||||
span {
|
||||
margin: 0 16px 0 0;
|
||||
.class-overview {
|
||||
position: relative;
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
display: block;
|
||||
code-example {
|
||||
clear: left;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
box-shadow: 0 2px 2px rgba(10, 16, 20, 0.24), 0 0 2px rgba(10, 16, 20, 0.12);
|
||||
border-radius: 2px;
|
||||
background: #FAFAFA;
|
||||
float: right;
|
||||
margin: 20px;
|
||||
padding: 0 24px 14px;
|
||||
|
||||
h2 {
|
||||
margin: 18px 0 4px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding-left: 14px;
|
||||
}
|
||||
}
|
||||
.inline-sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
.inline-sidebar {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.method-table {
|
||||
h3 {
|
||||
margin: 6px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.api-heading {
|
||||
padding: 5px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.properties-table {
|
||||
font-size: 14px;
|
||||
|
||||
thead th {
|
||||
&:nth-child(1) {
|
||||
width: 20%;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.parameters-table {
|
||||
margin-top: 0;
|
||||
font-size: 14px;
|
||||
td:nth-child(1) {
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
|
||||
details.overloads {
|
||||
margin-left: -8px;
|
||||
|
||||
summary {
|
||||
height: inherit;
|
||||
padding: 8px 12px;
|
||||
h4 {
|
||||
margin: 0;
|
||||
clear: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.api-section aio-code {
|
||||
background-color: rgba(241, 241, 241, 0.2);
|
||||
}
|
||||
|
||||
.from-constructor {
|
||||
font-style: italic;
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
|
||||
.api-heading {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 18px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.overloads .detail-contents {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ code-tabs mat-tab-body-content .fadeIn {
|
||||
aio-code pre {
|
||||
display: flex;
|
||||
min-height: 32px;
|
||||
margin: 16px 32px;
|
||||
margin: 16px 24px;
|
||||
white-space: pre-wrap;
|
||||
align-items: center;
|
||||
|
||||
@ -85,7 +85,6 @@ aio-code pre {
|
||||
|
||||
|
||||
.copy-button {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -32px;
|
||||
|
@ -25,16 +25,13 @@ summary {
|
||||
display: none; // Remove the built in details marker in webkit
|
||||
}
|
||||
|
||||
&::after {
|
||||
&::before {
|
||||
content: '\E5CE'; // See https://material.io/icons/#ic_expand_less
|
||||
font-family: 'Material Icons';
|
||||
font-size: 24px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
@include rotate(0deg); // We will rotate 180 degrees when details is open
|
||||
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 22px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +42,7 @@ details {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
&[open] > summary::after {
|
||||
&[open] > summary::before {
|
||||
@include rotate(180deg); // Rotate the icon
|
||||
}
|
||||
}
|
||||
|
@ -5,25 +5,26 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mat-icon, .material-icons {
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
.header-link {
|
||||
box-sizing: border-box;
|
||||
color: $mediumgray;
|
||||
margin: 0 8px;
|
||||
}
|
||||
display: inline-block;
|
||||
margin-left: -42px;
|
||||
padding: 0 8px;
|
||||
text-decoration: none;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
visibility: hidden;
|
||||
width: 40px;
|
||||
|
||||
&:hover {
|
||||
.mat-icon, .material-icons {
|
||||
visibility: visible;
|
||||
@media (max-width: 600px) {
|
||||
float: right;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
a.header-link {
|
||||
text-decoration: none;
|
||||
padding-left: 8px;
|
||||
margin-left: -50px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
&:hover .header-link {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
|
55
aio/src/styles/2-modules/_label.scss
Normal file
55
aio/src/styles/2-modules/_label.scss
Normal file
@ -0,0 +1,55 @@
|
||||
label.raised, .api-header label {
|
||||
border-radius: 4px;
|
||||
padding: 4px 16px;
|
||||
display: inline;
|
||||
font-size: 14px;
|
||||
color: white;
|
||||
margin-right: 8px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
display: block;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
&.page-label {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: $mist;
|
||||
color: $mediumgray;
|
||||
margin-bottom: 8px;
|
||||
width: 140px;
|
||||
|
||||
.material-icons {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&.property-type-label {
|
||||
font-size: 12px;
|
||||
background-color: $darkgray;
|
||||
color: $white;
|
||||
text-transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.api-header label {
|
||||
|
||||
&.api-status-label {
|
||||
background-color: $mediumgray;
|
||||
}
|
||||
|
||||
&.api-type-label {
|
||||
background-color: $accentblue;
|
||||
|
||||
@each $name, $symbol in $api-symbols {
|
||||
&.#{$name} {
|
||||
background: map-get($symbol, background);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -30,3 +30,4 @@
|
||||
@import 'select-menu';
|
||||
@import 'deploy-theme';
|
||||
@import 'notification';
|
||||
@import 'label';
|
||||
|
@ -12,7 +12,7 @@ table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
thead {
|
||||
thead > {
|
||||
vertical-align: middle;
|
||||
border-color: inherit;
|
||||
|
||||
@ -21,20 +21,20 @@ table {
|
||||
border-color: inherit;
|
||||
}
|
||||
|
||||
th {
|
||||
tr > th {
|
||||
background: rgba($lightgray, 0.2);
|
||||
border-bottom: 1px solid $lightgray;
|
||||
color: $darkgray;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
padding: 8px 32px;
|
||||
padding: 8px 24px;
|
||||
text-align: left;
|
||||
text-transform: uppercase;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
tbody > tr > {
|
||||
th,
|
||||
td {
|
||||
border-bottom: 1px solid $lightgray;
|
||||
@ -70,7 +70,7 @@ table {
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
&:last-child td {
|
||||
border: none;
|
||||
|
||||
@media (max-width: 480px) {
|
||||
|
@ -30,7 +30,7 @@
|
||||
box-shadow: 0 2px 2px rgba(0,0,0,0.24), 0 0 2px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
table tbody th{
|
||||
table > tbody > tr > th {
|
||||
border: 1px solid rgba(mat-color($foreground, secondary-text), .03);
|
||||
}
|
||||
|
||||
|
182
aio/tests/deployment/URLS_TO_REDIRECT.txt
Normal file
182
aio/tests/deployment/URLS_TO_REDIRECT.txt
Normal file
@ -0,0 +1,182 @@
|
||||
/api/api/core/ElementRef /api/core/ElementRef
|
||||
/api/common/Control-class /api/forms/FormControl
|
||||
/api/common/CORE_DIRECTIVES /api/common/CommonModule
|
||||
/api/common/DatePipe-class /api/common/DatePipe
|
||||
/api/common/NgClass-directive.html /api/common/NgClass
|
||||
/api/common/NgFor-directive.html /api/common/NgForOf
|
||||
/api/common/NgModel-directive /api/forms/NgModel
|
||||
/api/common/SelectControlValueAccessor-directive /api/forms/SelectControlValueAccessor
|
||||
/api/common/SlicePipe-class.html /api/common/SlicePipe
|
||||
/api/core/AnimationStateDeclarationMetadata /api/animations
|
||||
/api/core/DoCheck-interface.html /api/core/DoCheck
|
||||
/api/core/HostBinding-var /api/core/HostBinding
|
||||
/api/core/OpaqueToken-class /api/core/OpaqueToken
|
||||
/api/core/OptionalMetadata-class /api/core/Optional
|
||||
/api/core/PLATFORM_PIPES /api/common/CommonModule
|
||||
/api/core/Provider-class.html /api/core/Provider
|
||||
/api/core/Renderer-class /api/core/Renderer
|
||||
/api/core/testing/async-function /api/core/testing/async
|
||||
/api/core/testing/index/async-function /api/core/testing/async
|
||||
/api/core/testing/index/TestBed-class.html /api/core/testing/TestBed
|
||||
/api/core/testing/inject-function /api/core/testing/inject
|
||||
/api/core/testing/inject-function.html /api/core/testing/inject
|
||||
/api/http/Headers-class /api/http/Headers
|
||||
/api/http/Headers-class.html /api/http/Headers
|
||||
/api/http/HTTP_PROVIDERS-let /api/http/HttpModule
|
||||
/api/http/testing/index/MockBackend-class /api/http/testing/MockBackend
|
||||
/api/http/testing/index/MockBackend-class.html /api/http/testing/MockBackend
|
||||
/api/platform-browser-dynamic/testing/index/platformBrowserDynamicTesting-let.html /api/platform-browser-dynamic/testing/platformBrowserDynamicTesting
|
||||
/api/platform-browser/AnimationDriver /api/animations/browser/AnimationDriver
|
||||
/api/router/Route-class /api/router/Route
|
||||
/api/router/RouterLink-directive /api/router/RouterLink
|
||||
/api/testing/tick-function /api/core/testing/tick
|
||||
/api/upgrade/static/downgradeComponent-function /api/upgrade/static/downgradeComponent
|
||||
/api/upgrade/static/downgradeComponent-function.html /api/upgrade/static/downgradeComponent
|
||||
/api/upgrade/static/index/downgradeComponent-function /api/upgrade/static/downgradeComponent
|
||||
/api/upgrade/static/UpgradeModule-class /api/upgrade/static/UpgradeModule
|
||||
/docs/js/latest/api/ /api
|
||||
/docs/js/latest/api/animate/AnimationBuilder-class /api/animations/AnimationBuilder
|
||||
/docs/js/latest/api/animate/CssAnimationBuilder-class.html /api/animations/CssAnimationBuilder
|
||||
/docs/js/latest/api/animations/index/trigger-function /api/animations/trigger
|
||||
/docs/js/latest/api/common/ControlGroup-class.html /api/forms/FormGroup
|
||||
/docs/js/latest/api/common/index/DatePipe-pipe /api/common/DatePipe
|
||||
/docs/js/latest/api/common/index/Location-class.html /api/common/Location
|
||||
/docs/js/latest/api/common/index/MaxLengthValidator-directive.html /api/forms/MaxLengthValidator
|
||||
/docs/js/latest/api/common/index/NgFor-directive /api/common/NgForOf
|
||||
/docs/js/latest/api/common/index/NgFor-directive.html /api/common/NgForOf
|
||||
/docs/js/latest/api/common/index/NgFor-type-alias /api/common/NgForOf
|
||||
/docs/js/latest/api/common/index/NgFor-type-alias.html /api/common/NgForOf
|
||||
/docs/js/latest/api/common/index/NgTemplateOutlet-directive /api/common/NgTemplateOutlet
|
||||
/docs/js/latest/api/common/NgStyle-directive /api/common/NgStyle
|
||||
/docs/js/latest/api/core/DynamicComponentLoader-class.html /api/core/DynamicComponentLoader
|
||||
/docs/js/latest/api/core/HostListener-var /api/core/HostListener
|
||||
/docs/js/latest/api/core/index/AfterViewChecked-class /api/core/AfterViewChecked
|
||||
/docs/js/latest/api/core/index/AnimationStateTransitionMetadata-class.html /api/core/AnimationStateTransitionMetadata
|
||||
/docs/js/latest/api/core/index/AnimationStateTransitionMetadata-type-alias /api/core/AnimationStateTransitionMetadata
|
||||
/docs/js/latest/api/core/index/ApplicationModule-class /api/core/ApplicationModule
|
||||
/docs/js/latest/api/core/index/ApplicationRef-class /api/core/ApplicationRef
|
||||
/docs/js/latest/api/core/index/ChangeDetectorRef-class /api/core/ChangeDetectorRef
|
||||
/docs/js/latest/api/core/index/ComponentFactory-class /api/core/ComponentFactory
|
||||
/docs/js/latest/api/core/index/DebugNode-class /api/core/DebugNode
|
||||
/docs/js/latest/api/core/index/destroyPlatform-function /api/core/destroyPlatform
|
||||
/docs/js/latest/api/core/index/DirectiveMetadata-class /api/core/Directive
|
||||
/docs/js/latest/api/core/index/ErrorHandler-class /api/core/ErrorHandler
|
||||
/docs/js/latest/api/core/index/EventEmitter-class /api/core/EventEmitter
|
||||
/docs/js/latest/api/core/index/EventEmitter-class.html /api/core/EventEmitter
|
||||
/docs/js/latest/api/core/index/getPlatform-function /api/core/getPlatform
|
||||
/docs/js/latest/api/core/index/NgModule-interface /api/core/NgModule
|
||||
/docs/js/latest/api/core/index/NgModuleRef-class /api/core/NgModuleRef
|
||||
/docs/js/latest/api/core/index/NgModuleRef-class.html /api/core/NgModuleRef
|
||||
/docs/js/latest/api/core/index/OnInit-class /api/core/OnInit
|
||||
/docs/js/latest/api/core/index/OnInit-class.html /api/core/OnInit
|
||||
/docs/js/latest/api/core/index/OnInit-interface /api/core/OnInit
|
||||
/docs/js/latest/api/core/index/PlatformRef-class /api/core/PlatformRef
|
||||
/docs/js/latest/api/core/index/QueryList-class.html /api/core/QueryList
|
||||
/docs/js/latest/api/core/index/RenderComponentType-class /api/core/RenderComponentType
|
||||
/docs/js/latest/api/core/index/Renderer2-class.html /api/core/Renderer2
|
||||
/docs/js/latest/api/core/index/state-function.html /api/core/state
|
||||
/docs/js/latest/api/core/index/transition-function.html /api/core/transition
|
||||
/docs/js/latest/api/core/index/trigger-function /api/core/trigger
|
||||
/docs/js/latest/api/core/index/trigger-function.html /api/core/trigger
|
||||
/docs/js/latest/api/core/index/ViewChild-decorator /api/core/ViewChild
|
||||
/docs/js/latest/api/core/index/wtfLeave-let /api/core/wtfLeave
|
||||
/docs/js/latest/api/core/Injector-class.html /api/core/Injector
|
||||
/docs/js/latest/api/core/Query-var /api/core/Query
|
||||
/docs/js/latest/api/core/ResolvedProvider-interface.html /api/core/ResolvedProvider
|
||||
/docs/js/latest/api/core/testing/index/TestBed-class /api/core/testing/TestBed
|
||||
/docs/js/latest/api/core/testing/index/tick-function /api/core/testing/tick
|
||||
/docs/js/latest/api/core/testing/index/tick-function.html /api/core/testing/tick
|
||||
/docs/js/latest/api/core/ViewContainerRef-class.html /api/core/ViewContainerRef
|
||||
/docs/js/latest/api/forms/index/AbstractControl-class /api/forms/AbstractControl
|
||||
/docs/js/latest/api/forms/index/AbstractControl-class.html /api/forms/AbstractControl
|
||||
/docs/js/latest/api/forms/index/FormArray-class.html /api/forms/FormArray
|
||||
/docs/js/latest/api/forms/index/FormBuilder-class.html /api/forms/FormBuilder
|
||||
/docs/js/latest/api/forms/index/NG_VALIDATORS-let /api/forms/NG_VALIDATORS
|
||||
/docs/js/latest/api/forms/index/Validator-interface.html /api/forms/Validator
|
||||
/docs/js/latest/api/http/ConnectionBackend-class /api/http/ConnectionBackend
|
||||
/docs/js/latest/api/http/index/Http-class.html /api/http/Http
|
||||
/docs/js/latest/api/http/index/Jsonp-class.html /api/http/Jsonp
|
||||
/docs/js/latest/api/http/index/ResponseOptions-class.html /api/http/ResponseOptions
|
||||
/docs/js/latest/api/http/index/URLSearchParams-class /api/http/URLSearchParams
|
||||
/docs/js/latest/api/http/index/XHRConnection-class /api/http/XHRConnection
|
||||
/docs/js/latest/api/http/index/XHRConnection-class.html /api/http/XHRConnection
|
||||
/docs/js/latest/api/http/testing/index/MockConnection-class.html /api/http/testing/MockConnection
|
||||
/docs/js/latest/api/http/testing/MockBackend-class /api/http/testing/MockBackend
|
||||
/docs/js/latest/api/platform-browser-dynamic/index/platformBrowserDynamic-let.html /api/platform-browser-dynamic/platformBrowserDynamic
|
||||
/docs/js/latest/api/platform-browser-dynamic/testing/index/BrowserDynamicTestingModule-class.html /api/platform-browser-dynamic/testing/BrowserDynamicTestingModule
|
||||
/docs/js/latest/api/platform-browser/animations/index/BrowserAnimationsModule-class /api/platform-browser/animations/BrowserAnimationsModule
|
||||
/docs/js/latest/api/platform-browser/animations/index/BrowserAnimationsModule-class.html /api/platform-browser/animations/BrowserAnimationsModule
|
||||
/docs/js/latest/api/platform-browser/animations/index/NoopAnimationsModule-class.html /api/platform-browser/animations/NoopAnimationsModule
|
||||
/docs/js/latest/api/platform-browser/index/DomSanitizer-class /api/platform-browser/DomSanitizer
|
||||
/docs/js/latest/api/platform-browser/index/Meta-class /api/platform-browser/Meta
|
||||
/docs/js/latest/api/platform-browser/index/MetaDefinition-type-alias /api/platform-browser/MetaDefinition
|
||||
/docs/js/latest/api/platform-browser/index/SafeUrl-interface /api/platform-browser/SafeUrl
|
||||
/docs/js/latest/api/platform-browser/index/Title-class /api/platform-browser/Title
|
||||
/docs/js/latest/api/platform-browser/index/Title-class.html /api/platform-browser/Title
|
||||
/docs/js/latest/api/platform-server/index/PlatformState-class /api/platform-server/PlatformState
|
||||
/docs/js/latest/api/platform-server/index/PlatformState-class.html /api/platform-server/PlatformState
|
||||
/docs/js/latest/api/platform-webworker /api/platform-webworker
|
||||
/docs/js/latest/api/platform-webworker/index/MessageBus-class /api/platform-webworker/MessageBus
|
||||
/docs/js/latest/api/router/index/ActivatedRoute-interface /api/router/ActivatedRoute
|
||||
/docs/js/latest/api/router/index/CanActivate-interface.html /api/router/CanActivate
|
||||
/docs/js/latest/api/router/index/CanActivateChild-interface /api/router/CanActivateChild
|
||||
/docs/js/latest/api/router/index/CanDeactivate-interface.html /api/router/CanDeactivate
|
||||
/docs/js/latest/api/router/index/CanLoad-interface /api/router/CanLoad
|
||||
/docs/js/latest/api/router/index/CanLoad-interface.html /api/router/CanLoad
|
||||
/docs/js/latest/api/router/index/NavigationEnd-class /api/router/NavigationEnd
|
||||
/docs/js/latest/api/router/index/provideRoutes-function /api/router/provideRoutes
|
||||
/docs/js/latest/api/router/index/provideRoutes-function.html /api/router/provideRoutes
|
||||
/docs/js/latest/api/router/index/Router-class /api/router/Router
|
||||
/docs/js/latest/api/router/index/RouterLink-directive /api/router/RouterLink
|
||||
/docs/js/latest/api/router/index/Routes-type-alias /api/router/Routes
|
||||
/docs/js/latest/api/router/index/UrlSegment-class /api/router/UrlSegment
|
||||
/docs/js/latest/api/router/index/UrlSerializer-class.html /api/router/UrlSerializer
|
||||
/docs/js/latest/api/router/Instruction-class /api/router/Instruction
|
||||
/docs/js/latest/api/router/Location-class /api/router/Location
|
||||
/docs/js/latest/api/router/OnActivate-interface /api/router/OnActivate
|
||||
/docs/js/latest/api/router/Redirect-class.html /api/router/Redirect
|
||||
/docs/js/latest/api/router/RouteDefinition-interface.html /api/router/RouteDefinition
|
||||
/docs/js/latest/api/router/RouteParams-class /api/router/RouteParams
|
||||
/docs/js/latest/api/router/RouteParams-class.html /api/router/RouteParams
|
||||
/docs/js/latest/api/router/Router-class.html /api/router/Router
|
||||
/docs/js/latest/api/router/testing /api/router/testing
|
||||
/docs/js/latest/api/upgrade/index/UpgradeAdapter-class.html /api/upgrade/UpgradeAdapter
|
||||
/docs/js/latest/api/upgrade/static/UpgradeModule-class /api/upgrade/static/UpgradeModule
|
||||
/docs/js/latest/api/upgrade/static/UpgradeModule-class.html /api/upgrade/static/UpgradeModule
|
||||
/docs/js/latest/cookbook/ts-to-js.html https://github.com/angular/angular/blob/master/aio/content/guide/change-log.md#es6--described-in-typescript-to-javascript-2016-11-14
|
||||
/docs/js/latest/glossary /guide/glossary
|
||||
/docs/js/latest/guide/ /docs
|
||||
/docs/js/latest/guide/lifecycle-hooks /guide/lifecycle-hooks
|
||||
/docs/js/latest/guide/ngmodule /guide/ngmodules
|
||||
/docs/js/latest/resources /resources
|
||||
/docs/latest/tutorial /tutorial
|
||||
/styleguide /guide/styleguide
|
||||
/docs/styleguide /guide/styleguide
|
||||
/docs/styleguide.html /guide/styleguide
|
||||
/docs/ts/latest/api/core/HostBinding-var.html /api/core/HostBinding
|
||||
/docs/ts/latest/api/core/index/BaseException-class.html /api/core/BaseException
|
||||
/docs/ts/latest/api/core/index/PLATFORM_PIPES-let.html /api/common/CommonModule
|
||||
/docs/ts/latest/api/core/OnInit-interface.html /api/core/OnInit
|
||||
/docs/ts/latest/api/core/OpaqueToken-class.html /api/core/OpaqueToken
|
||||
/docs/ts/latest/api/core/OptionalMetadata-class.html /api/core/Optional
|
||||
/docs/ts/latest/api/core/testing/index/async-function.html /api/core/testing/async
|
||||
/docs/ts/latest/api/core/testing/index/fakeAsync-function.html /api/core/testing/fakeAsync
|
||||
/docs/ts/latest/api/core/testing/index/TestComponentRenderer-class.html /api/core/testing/TestComponentRenderer
|
||||
/docs/ts/latest/api/core/testing/index/tick-function.html /api/core/testing/tick
|
||||
/docs/ts/latest/api/http/Connection-class.html /api/http/Connection
|
||||
/docs/ts/latest/api/http/testing/index/MockBackend-class.html /api/http/testing/MockBackend
|
||||
/docs/ts/latest/api/http/testing/index/MockConnection-class.html /api/http/testing/MockConnection
|
||||
/docs/ts/latest/api/platform-browser-dynamic/index/workerAppDynamicPlatform-let.html /api/platform-browser-dynamic/workerAppDynamicPlatform
|
||||
/docs/ts/latest/api/testing/fakeAsync-function.html /api/core/testing/fakeAsync
|
||||
/docs/ts/latest/cookbook/ts-to-js.html https://github.com/angular/angular/blob/master/aio/content/guide/change-log.md#es6--described-in-typescript-to-javascript-2016-11-14
|
||||
/guide/cli-quickstart /guide/quickstart
|
||||
/guide/learning-angular /guide/quickstart
|
||||
/guide/learning-angular.html /guide/quickstart
|
||||
/guide/metadata /guide/aot-compiler
|
||||
/guide/service-worker-getstart /guide/service-worker-getting-started
|
||||
/guide/service-worker-comm /guide/service-worker-communications
|
||||
/guide/service-worker-configref /guide/service-worker-config
|
||||
/news https://blog.angular.io/
|
||||
/news.html https://blog.angular.io/
|
||||
/testing /guide/testing
|
||||
/testing/first-app-tests.html /guide/testing
|
49
aio/tests/deployment/helpers.ts
Normal file
49
aio/tests/deployment/helpers.ts
Normal file
@ -0,0 +1,49 @@
|
||||
const { readFileSync } = require('fs');
|
||||
const path = require('canonical-path');
|
||||
const cjson = require('cjson');
|
||||
|
||||
import { FirebaseRedirector, FirebaseRedirectConfig } from '../../tools/firebase-test-utils/FirebaseRedirector';
|
||||
|
||||
export function getRedirector() {
|
||||
return new FirebaseRedirector(loadRedirects());
|
||||
}
|
||||
|
||||
export function loadRedirects(): FirebaseRedirectConfig[] {
|
||||
const pathToFirebaseJSON = path.resolve(__dirname, '../../firebase.json');
|
||||
const contents = cjson.load(pathToFirebaseJSON);
|
||||
return contents.hosting.redirects;
|
||||
}
|
||||
|
||||
export function loadSitemapUrls() {
|
||||
const pathToSiteMap = path.resolve(__dirname, '../../src/generated/sitemap.xml');
|
||||
const xml = readFileSync(pathToSiteMap, 'utf8');
|
||||
const urls: string[] = [];
|
||||
xml.replace(/<loc>([^<]+)<\/loc>/g, (_, loc) => urls.push(loc.replace('%%DEPLOYMENT_HOST%%', '')));
|
||||
return urls;
|
||||
}
|
||||
|
||||
export function loadLegacyUrls() {
|
||||
const pathToLegacyUrls = path.resolve(__dirname, 'URLS_TO_REDIRECT.txt');
|
||||
const urls = readFileSync(pathToLegacyUrls, 'utf8').split('\n').map(line => line.split('\t'));
|
||||
return urls;
|
||||
}
|
||||
|
||||
export function loadSWRoutes() {
|
||||
const pathToSWManifest = path.resolve(__dirname, '../../ngsw-manifest.json');
|
||||
const contents = cjson.load(pathToSWManifest);
|
||||
const routes = contents.routing.routes;
|
||||
return Object.keys(routes).map(route => {
|
||||
const routeConfig = routes[route];
|
||||
switch (routeConfig.match) {
|
||||
case 'exact':
|
||||
return (url) => url === route;
|
||||
case 'prefix':
|
||||
return (url) => url.startsWith(route);
|
||||
case 'regex':
|
||||
const regex = new RegExp(route);
|
||||
return (url) => regex.test(url);
|
||||
default:
|
||||
throw new Error(`unknown route config: ${route} - ${routeConfig.match}`);
|
||||
}
|
||||
});
|
||||
}
|
31
aio/tests/deployment/testFirebaseRedirection.spec.ts
Normal file
31
aio/tests/deployment/testFirebaseRedirection.spec.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { getRedirector, loadLegacyUrls, loadRedirects, loadSitemapUrls } from './helpers';
|
||||
|
||||
describe('firebase.json redirect config', () => {
|
||||
describe('with sitemap urls', () => {
|
||||
loadSitemapUrls().forEach(url => {
|
||||
it('should not redirect any urls in the sitemap', () => {
|
||||
expect(getRedirector().redirect(url)).toEqual(url);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with legacy urls', () => {
|
||||
loadLegacyUrls().forEach(urlPair => {
|
||||
it('should redirect the legacy urls', () => {
|
||||
const redirector = getRedirector();
|
||||
expect(redirector.redirect(urlPair[0])).not.toEqual(urlPair[0]);
|
||||
if (urlPair[1]) {
|
||||
expect(redirector.redirect(urlPair[0])).toEqual(urlPair[1]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('destinations', () => {
|
||||
loadRedirects().forEach(redirect => {
|
||||
it('should match pattern "^(https?:/)?/.*"', () => {
|
||||
expect(redirect.destination).toMatch(/^(https?:\/)?\/.*/);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
33
aio/tests/deployment/testServiceWorkerRoutes.spec.ts
Normal file
33
aio/tests/deployment/testServiceWorkerRoutes.spec.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { loadLegacyUrls, loadSitemapUrls, loadSWRoutes } from './helpers';
|
||||
|
||||
describe('service-worker routes', () => {
|
||||
|
||||
loadSitemapUrls().forEach(url => {
|
||||
it('should process URLs in the Sitemap', () => {
|
||||
const routes = loadSWRoutes();
|
||||
expect(routes.some(test => test(url))).toBeTruthy(url);
|
||||
});
|
||||
});
|
||||
|
||||
loadLegacyUrls().forEach(urlPair => {
|
||||
const url = urlPair[0];
|
||||
it('should ignore legacy URLs that will be redirected', () => {
|
||||
const routes = loadSWRoutes();
|
||||
expect(routes.some(test => test(url))).toBeFalsy(url);
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore stackblitz URLs', () => {
|
||||
const routes = loadSWRoutes();
|
||||
expect(routes.some(test => test('/generated/live-examples/toh-pt6/stackblitz.html'))).toBeFalsy();
|
||||
expect(routes.some(test => test('/generated/live-examples/toh-pt6/stackblitz'))).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should ignore URLs to files with extensions', () => {
|
||||
const routes = loadSWRoutes();
|
||||
expect(routes.some(test => test('/generated/zips/animations/animations.zip'))).toBeFalsy();
|
||||
expect(routes.some(test => test('/generated/images/guide/animations/animation_auto.gif'))).toBeFalsy();
|
||||
expect(routes.some(test => test('/generated/ie-polyfills.min.js'))).toBeFalsy();
|
||||
expect(routes.some(test => test('/generated/docs/guide/animations.json'))).toBeFalsy();
|
||||
});
|
||||
});
|
@ -24,7 +24,7 @@ export class ApiPage extends SitePage {
|
||||
//
|
||||
// and we want to be able to pull out the code elements from only the first level
|
||||
// if `onlyDirect` is set to `true`.
|
||||
const selector = `.descendants.${docType} ${onlyDirect ? '>' : ''} li > :not(ul) code`;
|
||||
const selector = `.inline-sidebar .descendants.${docType} ${onlyDirect ? '>' : ''} ul > li > code`;
|
||||
return element.all(by.css(selector)).map<string>(item => item && item.getText());
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { browser, by, element } from 'protractor';
|
||||
import { browser, by, element, ElementFinder } from 'protractor';
|
||||
import { SitePage } from './app.po';
|
||||
|
||||
describe('site App', function() {
|
||||
@ -11,7 +11,7 @@ describe('site App', function() {
|
||||
|
||||
it('should show features text after clicking "Features"', () => {
|
||||
page.navigateTo('');
|
||||
page.getTopMenuLink('features').click();
|
||||
page.click(page.getTopMenuLink('features'));
|
||||
expect(page.getDocViewerText()).toMatch(/Progressive web apps/i);
|
||||
});
|
||||
|
||||
@ -19,28 +19,74 @@ describe('site App', function() {
|
||||
page.navigateTo('');
|
||||
expect(browser.getTitle()).toBe('Angular');
|
||||
|
||||
page.getTopMenuLink('features').click();
|
||||
page.click(page.getTopMenuLink('features'));
|
||||
expect(browser.getTitle()).toBe('Angular - FEATURES & BENEFITS');
|
||||
|
||||
page.homeLink.click();
|
||||
page.click(page.homeLink);
|
||||
expect(browser.getTitle()).toBe('Angular');
|
||||
});
|
||||
|
||||
it('should not navigate when clicking on nav-item headings (sub-menu toggles)', () => {
|
||||
// Show the sidenav.
|
||||
page.navigateTo('docs');
|
||||
expect(page.locationPath()).toBe('/docs');
|
||||
|
||||
// Get the top-level nav-item headings (sub-menu toggles).
|
||||
const navItemHeadings = page.getNavItemHeadings(page.sidenav, 1);
|
||||
|
||||
// Test all headings (and sub-headings).
|
||||
expect(navItemHeadings.count()).toBeGreaterThan(0);
|
||||
navItemHeadings.each(heading => testNavItemHeading(heading!, 1));
|
||||
|
||||
// Helpers
|
||||
function expectToBeCollapsed(element: ElementFinder) {
|
||||
expect(element.getAttribute('class')).toMatch(/\bcollapsed\b/);
|
||||
expect(element.getAttribute('class')).not.toMatch(/\bexpanded\b/);
|
||||
}
|
||||
|
||||
function expectToBeExpanded(element: ElementFinder) {
|
||||
expect(element.getAttribute('class')).not.toMatch(/\bcollapsed\b/);
|
||||
expect(element.getAttribute('class')).toMatch(/\bexpanded\b/);
|
||||
}
|
||||
|
||||
function testNavItemHeading(heading: ElementFinder, level: number) {
|
||||
const children = page.getNavItemHeadingChildren(heading, level);
|
||||
|
||||
// Headings are initially collapsed.
|
||||
expectToBeCollapsed(children);
|
||||
|
||||
// Ensure heading does not cause navigation when expanding.
|
||||
page.click(heading);
|
||||
expectToBeExpanded(children);
|
||||
expect(page.locationPath()).toBe('/docs');
|
||||
|
||||
// Recursively test child-headings (while this heading is expanded).
|
||||
const nextLevel = level + 1;
|
||||
const childNavItemHeadings = page.getNavItemHeadings(children, nextLevel);
|
||||
childNavItemHeadings.each(childHeading => testNavItemHeading(childHeading!, nextLevel));
|
||||
|
||||
// Ensure heading does not cause navigation when collapsing.
|
||||
page.click(heading);
|
||||
expectToBeCollapsed(children);
|
||||
expect(page.locationPath()).toBe('/docs');
|
||||
}
|
||||
});
|
||||
|
||||
it('should show the tutorial index page at `/tutorial` after jitterbugging through features', () => {
|
||||
// check that we can navigate directly to the tutorial page
|
||||
page.navigateTo('tutorial');
|
||||
expect(page.getDocViewerText()).toMatch(/Tutorial: Tour of Heroes/i);
|
||||
|
||||
// navigate to a different page
|
||||
page.getTopMenuLink('features').click();
|
||||
page.click(page.getTopMenuLink('features'));
|
||||
expect(page.getDocViewerText()).toMatch(/Progressive web apps/i);
|
||||
|
||||
// Show the menu
|
||||
page.docsMenuLink.click();
|
||||
page.click(page.docsMenuLink);
|
||||
|
||||
// Tutorial folder should still be expanded because this test runs in wide mode
|
||||
// Navigate to the tutorial introduction via a link in the sidenav
|
||||
page.getNavItem(/introduction/i).click();
|
||||
page.click(page.getNavItem(/introduction/i));
|
||||
expect(page.getDocViewerText()).toMatch(/Tutorial: Tour of Heroes/i);
|
||||
});
|
||||
|
||||
@ -57,8 +103,7 @@ describe('site App', function() {
|
||||
page.scrollToBottom();
|
||||
expect(page.getScrollTop()).toBeGreaterThan(0);
|
||||
|
||||
page.getNavItem(/api/i).click();
|
||||
browser.waitForAngular();
|
||||
page.click(page.getNavItem(/api/i));
|
||||
expect(page.locationPath()).toBe('/api');
|
||||
expect(page.getScrollTop()).toBe(0);
|
||||
});
|
||||
@ -69,8 +114,7 @@ describe('site App', function() {
|
||||
page.scrollToBottom();
|
||||
expect(page.getScrollTop()).toBeGreaterThan(0);
|
||||
|
||||
page.getNavItem(/security/i).click();
|
||||
browser.waitForAngular();
|
||||
page.click(page.getNavItem(/security/i));
|
||||
expect(page.locationPath()).toBe('/guide/security');
|
||||
expect(page.getScrollTop()).toBe(0);
|
||||
});
|
||||
@ -102,7 +146,7 @@ describe('site App', function() {
|
||||
it('should call ga with new URL on navigation', done => {
|
||||
let path: string;
|
||||
page.navigateTo('');
|
||||
page.getTopMenuLink('features').click();
|
||||
page.click(page.getTopMenuLink('features'));
|
||||
page.locationPath()
|
||||
.then(p => path = p)
|
||||
.then(() => page.ga())
|
||||
@ -115,18 +159,6 @@ describe('site App', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
it('should find pages when searching by a partial word in the title', () => {
|
||||
page.navigateTo('');
|
||||
|
||||
page.enterSearch('ngCont');
|
||||
expect(page.getSearchResults()).toContain('NgControl');
|
||||
|
||||
page.enterSearch('accessor');
|
||||
expect(page.getSearchResults()).toContain('ControlValueAccessor');
|
||||
});
|
||||
});
|
||||
|
||||
describe('404 page', () => {
|
||||
it('should add or remove the "noindex" meta tag depending upon the validity of the page', () => {
|
||||
page.navigateTo('');
|
||||
@ -137,7 +169,7 @@ describe('site App', function() {
|
||||
expect(element(by.css('meta[name="googlebot"][content="noindex"]')).isPresent()).toBeTruthy();
|
||||
expect(element(by.css('meta[name="robots"][content="noindex"]')).isPresent()).toBeTruthy();
|
||||
|
||||
page.getTopMenuLink('features').click();
|
||||
page.click(page.getTopMenuLink('features'));
|
||||
expect(element(by.css('meta[name="googlebot"]')).isPresent()).toBeFalsy();
|
||||
expect(element(by.css('meta[name="robots"]')).isPresent()).toBeFalsy();
|
||||
});
|
@ -7,6 +7,7 @@ export class SitePage {
|
||||
links = element.all(by.css('md-toolbar a'));
|
||||
homeLink = element(by.css('a.home'));
|
||||
docsMenuLink = element(by.cssContainingText('aio-top-menu a', 'Docs'));
|
||||
sidenav = element(by.css('mat-sidenav'));
|
||||
docViewer = element(by.css('aio-doc-viewer'));
|
||||
codeExample = element.all(by.css('aio-doc-viewer pre > code'));
|
||||
ghLink = this.docViewer
|
||||
@ -24,7 +25,17 @@ export class SitePage {
|
||||
.filter(element => element.getText().then(text => pattern.test(text)))
|
||||
.first();
|
||||
}
|
||||
getNavItemHeadings(parent: ElementFinder, level: number) {
|
||||
const targetSelector = `aio-nav-item .vertical-menu-item.heading.level-${level}`;
|
||||
return parent.all(by.css(targetSelector));
|
||||
}
|
||||
getNavItemHeadingChildren(heading: ElementFinder, level: number) {
|
||||
const targetSelector = `.heading-children.level-${level}`;
|
||||
const script = `return arguments[0].parentNode.querySelector('${targetSelector}');`;
|
||||
return element(() => browser.executeScript(script, heading));
|
||||
}
|
||||
getTopMenuLink(path) { return element(by.css(`aio-top-menu a[href="${path}"]`)); }
|
||||
|
||||
ga() { return browser.executeScript('return window["ga"].q') as promise.Promise<any[][]>; }
|
||||
locationPath() { return browser.executeScript('return document.location.pathname') as promise.Promise<string>; }
|
||||
|
||||
@ -53,6 +64,10 @@ export class SitePage {
|
||||
return browser.executeScript('window.scrollTo(0, document.body.scrollHeight)');
|
||||
}
|
||||
|
||||
click(element: ElementFinder) {
|
||||
return element.click().then(() => browser.waitForAngular());
|
||||
}
|
||||
|
||||
enterSearch(query: string) {
|
||||
const input = element(by.css('.search-container input[type=search]'));
|
||||
input.clear();
|
203
aio/tests/e2e/onerror.e2e-spec.ts
Normal file
203
aio/tests/e2e/onerror.e2e-spec.ts
Normal file
@ -0,0 +1,203 @@
|
||||
import { browser } from 'protractor';
|
||||
import { SitePage } from './app.po';
|
||||
|
||||
/* tslint:disable:max-line-length */
|
||||
|
||||
describe('onerror handler', function() {
|
||||
let page: SitePage;
|
||||
|
||||
beforeAll(() => {
|
||||
page = new SitePage();
|
||||
page.navigateTo('');
|
||||
});
|
||||
|
||||
|
||||
it('(called without an error object) should call ga with a payload based on the message, url, row and column arguments', async () => {
|
||||
const message1 = await callOnError('Error: some error message', 'some-file.js', 12, 3, undefined);
|
||||
expect(message1).toEqual('some error message\nsome-file.js:12:3');
|
||||
const message2 = await callOnError('Error: some error message', undefined, undefined, undefined, undefined);
|
||||
expect(message2).toEqual('some error message\nnull:?:?');
|
||||
});
|
||||
|
||||
it('(called without an error object) should call ga with a payload that is no longer that 150 characters', async () => {
|
||||
const message = await callOnError(
|
||||
'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz' +
|
||||
'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz',
|
||||
'some-file.js', 12, 3, undefined);
|
||||
expect(message).toEqual(
|
||||
'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz' +
|
||||
'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst');
|
||||
});
|
||||
|
||||
it('(called with a Firefox on android style error) should call ga with a payload based on the error object', async () => {
|
||||
const message = await callOnError('Error: something terrible has happened. oh no. oh no.', undefined, undefined, undefined, {
|
||||
stack: `AppComponent@https://example.com/app/app.component.ts:31:29
|
||||
createClass@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:12200:20
|
||||
createDirectiveInstance@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:12049:37
|
||||
createViewNodes@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:13487:53
|
||||
createRootView@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:13377:5
|
||||
callWithDebugContext@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:14778:39
|
||||
debugCreateRootView@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:14079:12
|
||||
ComponentFactory_.prototype.create@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:10998:37
|
||||
ComponentFactoryBoundToModule.prototype.create@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:3958:16
|
||||
ApplicationRef.prototype.bootstrap@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:5769:40
|
||||
PlatformRef.prototype._moduleDoBootstrap/<@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:5496:74
|
||||
PlatformRef.prototype._moduleDoBootstrap@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:5496:13
|
||||
PlatformRef.prototype.bootstrapModuleFactory/</</<@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:5417:21
|
||||
ZoneDelegate.prototype.invoke@https://example.com/packages/zone.js@0.8.18/dist/zone.js:392:17
|
||||
forkInnerZoneWithAngularBehavior/zone._inner<.onInvoke@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:4665:24
|
||||
ZoneDelegate.prototype.invoke@https://example.com/packages/zone.js@0.8.18/dist/zone.js:391:17
|
||||
Zone.prototype.run@https://example.com/packages/zone.js@0.8.18/dist/zone.js:142:24
|
||||
scheduleResolveOrReject/<@https://example.com/packages/zone.js@0.8.18/dist/zone.js:873:52
|
||||
ZoneDelegate.prototype.invokeTask@https://example.com/packages/zone.js@0.8.18/dist/zone.js:425:17
|
||||
forkInnerZoneWithAngularBehavior/zone._inner<.onInvokeTask@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:4656:24
|
||||
ZoneDelegate.prototype.invokeTask@https://example.com/packages/zone.js@0.8.18/dist/zone.js:424:17
|
||||
Zone.prototype.runTask@https://example.com/packages/zone.js@0.8.18/dist/zone.js:192:28
|
||||
drainMicroTaskQueue@https://example.com/packages/zone.js@0.8.18/dist/zone.js:602:25` });
|
||||
|
||||
expect(message).toEqual(`something terrible has happened. oh no. oh no.
|
||||
AppComponent@app.component.ts:31:29
|
||||
createClass@core.umd.js:12200:20
|
||||
createDirectiveInstance@core.umd.j`);
|
||||
});
|
||||
|
||||
it('(called with a Safari 11 style error) should call ga with a payload based on the error object', async () => {
|
||||
const message = await callOnError('Error: something terrible has happened. oh no. oh no.', undefined, undefined, undefined, {
|
||||
stack: `AppComponent
|
||||
createClass
|
||||
createDirectiveInstance
|
||||
createViewNodes
|
||||
createRootView
|
||||
callWithDebugContext
|
||||
create
|
||||
bootstrap
|
||||
forEach@[native code]
|
||||
_moduleDoBootstrap
|
||||
|
||||
onInvoke
|
||||
run
|
||||
|
||||
onInvokeTask
|
||||
runTask
|
||||
drainMicroTaskQueue
|
||||
promiseReactionJob@[native code]` });
|
||||
|
||||
expect(message).toEqual(`something terrible has happened. oh no. oh no.
|
||||
AppComponent
|
||||
createClass
|
||||
createDirectiveInstance
|
||||
createViewNodes
|
||||
createRootView
|
||||
callWithDebugContext
|
||||
cr`);
|
||||
});
|
||||
|
||||
it('(called with a Opera 50 style error) should call ga with a payload based on the error object', async () => {
|
||||
const message = await callOnError('Error: something terrible has happened. oh no. oh no.', undefined, undefined, undefined, {
|
||||
stack: `Error: something terrible has happened. oh no. oh no.
|
||||
at new AppComponent (https://example.com/app/app.component.ts:31:29)
|
||||
at createClass (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:12200:20)
|
||||
at createDirectiveInstance (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:12049:37)
|
||||
at createViewNodes (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:13487:53)
|
||||
at createRootView (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:13377:5)
|
||||
at callWithDebugContext (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:14778:42)
|
||||
at Object.debugCreateRootView [as createRootView] (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:14079:12)
|
||||
at ComponentFactory_.create (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:10998:46)
|
||||
at ComponentFactoryBoundToModule.create (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:3958:29)
|
||||
at ApplicationRef.bootstrap (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:5769:57)` });
|
||||
|
||||
expect(message).toEqual(`something terrible has happened. oh no. oh no.
|
||||
new AppComponent@app.component.ts:31:29
|
||||
createClass@core.umd.js:12200:20
|
||||
createDirectiveInstance@core.u`);
|
||||
});
|
||||
|
||||
it('(called with a Chrome 64 style error) should call ga with a payload based on the error object', async () => {
|
||||
const message = await callOnError('Error: something terrible has happened. oh no. oh no.', undefined, undefined, undefined, {
|
||||
stack: `Error: something terrible has happened. oh no. oh no.
|
||||
at new AppComponent (https://example.com/app/app.component.ts:31:29)
|
||||
at createClass (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:12200:20)
|
||||
at createDirectiveInstance (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:12049:37)
|
||||
at createViewNodes (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:13487:53)
|
||||
at createRootView (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:13377:5)
|
||||
at callWithDebugContext (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:14778:42)
|
||||
at Object.debugCreateRootView [as createRootView] (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:14079:12)
|
||||
at ComponentFactory_.create (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:10998:46)
|
||||
at ComponentFactoryBoundToModule.create (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:3958:29)
|
||||
at ApplicationRef.bootstrap (https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:5769:57)` });
|
||||
|
||||
expect(message).toEqual(`something terrible has happened. oh no. oh no.
|
||||
new AppComponent@app.component.ts:31:29
|
||||
createClass@core.umd.js:12200:20
|
||||
createDirectiveInstance@core.u`);
|
||||
});
|
||||
|
||||
it('(called with a Firefox 58 style error) should call ga with a payload based on the error object', async () => {
|
||||
const message = await callOnError('Error: something terrible has happened. oh no. oh no.', undefined, undefined, undefined, {
|
||||
stack: `AppComponent@https://example.com/app/app.component.ts:31:29
|
||||
createClass@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:12200:20
|
||||
createDirectiveInstance@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:12049:37
|
||||
createViewNodes@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:13487:53
|
||||
createRootView@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:13377:5
|
||||
callWithDebugContext@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:14778:39
|
||||
debugCreateRootView@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:14079:12
|
||||
ComponentFactory_.prototype.create@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:10998:37
|
||||
ComponentFactoryBoundToModule.prototype.create@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:3958:16
|
||||
ApplicationRef.prototype.bootstrap@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:5769:40
|
||||
PlatformRef.prototype._moduleDoBootstrap/<@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:5496:74
|
||||
PlatformRef.prototype._moduleDoBootstrap@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:5496:13
|
||||
PlatformRef.prototype.bootstrapModuleFactory/</</<@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:5417:21
|
||||
ZoneDelegate.prototype.invoke@https://example.com/packages/zone.js@0.8.18/dist/zone.js:392:17
|
||||
onInvoke@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:4665:24
|
||||
ZoneDelegate.prototype.invoke@https://example.com/packages/zone.js@0.8.18/dist/zone.js:391:17
|
||||
Zone.prototype.run@https://example.com/packages/zone.js@0.8.18/dist/zone.js:142:24
|
||||
scheduleResolveOrReject/<@https://example.com/packages/zone.js@0.8.18/dist/zone.js:873:52
|
||||
ZoneDelegate.prototype.invokeTask@https://example.com/packages/zone.js@0.8.18/dist/zone.js:425:17
|
||||
onInvokeTask@https://example.com/packages/@angular/core@5.0.0/bundles/core.umd.js:4656:24
|
||||
ZoneDelegate.prototype.invokeTask@https://example.com/packages/zone.js@0.8.18/dist/zone.js:424:17
|
||||
Zone.prototype.runTask@https://example.com/packages/zone.js@0.8.18/dist/zone.js:192:28
|
||||
drainMicroTaskQueue@https://example.com/packages/zone.js@0.8.18/dist/zone.js:602:25` });
|
||||
|
||||
expect(message).toEqual(`something terrible has happened. oh no. oh no.
|
||||
AppComponent@app.component.ts:31:29
|
||||
createClass@core.umd.js:12200:20
|
||||
createDirectiveInstance@core.umd.j`);
|
||||
});
|
||||
|
||||
it('(called with a Edge 16 style error) should call ga with a payload based on the error object', async () => {
|
||||
const message = await callOnError('Error: something terrible has happened. oh no. oh no.', undefined, undefined, undefined, {
|
||||
stack: `Error: something terrible has happened. oh no. oh no.
|
||||
at AppComponent (eval code:31:21)
|
||||
at createClass (eval code:12200:13)
|
||||
at createDirectiveInstance (eval code:12049:5)
|
||||
at createViewNodes (eval code:13487:21)
|
||||
at createRootView (eval code:13377:5)
|
||||
at callWithDebugContext (eval code:14778:9)
|
||||
at debugCreateRootView (eval code:14079:5)
|
||||
at ComponentFactory_.prototype.create (eval code:10998:9)
|
||||
at ComponentFactoryBoundToModule.prototype.create (eval code:3958:9)
|
||||
at ApplicationRef.prototype.bootstrap (eval code:5769:9)` });
|
||||
|
||||
expect(message).toEqual(`something terrible has happened. oh no. oh no.
|
||||
AppComponent@???:31:21
|
||||
createClass@???:12200:13
|
||||
createDirectiveInstance@???:12049:5
|
||||
createViewNodes@???`);
|
||||
});
|
||||
|
||||
async function callOnError(message, url, line, column, error) {
|
||||
await browser.executeScript(function() {
|
||||
// reset the ga queue
|
||||
(window as any).ga.q.length = 0;
|
||||
// post the error to the handler
|
||||
window.onerror(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
|
||||
}, message, url, line, column, error);
|
||||
const gaCalls = await page.ga();
|
||||
const exceptionCall = gaCalls.find(call => call[0] === 'send' && call[1] === 'exception');
|
||||
if (exceptionCall) {
|
||||
const payload = exceptionCall[2];
|
||||
expect(payload.exFatal).toBe(true);
|
||||
return payload.exDescription;
|
||||
}
|
||||
}
|
||||
});
|
@ -6,7 +6,7 @@ const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./e2e/**/*.e2e-spec.ts'
|
||||
'./*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
@ -26,7 +26,7 @@ exports.config = {
|
||||
},
|
||||
beforeLaunch: function() {
|
||||
require('ts-node').register({
|
||||
project: 'e2e/tsconfig.e2e.json'
|
||||
project: 'tests/e2e/tsconfig.e2e.json'
|
||||
});
|
||||
},
|
||||
onPrepare() {
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"outDir": "../../out-tsc/e2e",
|
||||
"baseUrl": "./",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user