Compare commits

...

124 Commits
4.4.2 ... 4.4.x

Author SHA1 Message Date
018750154d test: fix firebase deployment script test
When I fixed the project id in 2c4850dc58,
I didn't realize we had a test that verified the wrong behavior.
2018-05-04 15:10:17 -07:00
b19216d58b fix(aio): correct project id for deployment of archive sites 2018-05-03 15:09:17 -07:00
84fc1a3663 docs: add changelog for the 4.4.7 release 2018-04-16 02:00:31 -06:00
1c40be26c6 release: cut the 4.4.7 release 2018-04-16 02:00:02 -06:00
2c5cf19c6d fix(core): use appropriate inert document strategy for Firefox & Safari (#22077)
Both Firefox and Safari are vulnerable to XSS if we use an inert document
created via `document.implementation.createHTMLDocument()`.

Now we check for those vulnerabilities and then use a DOMParser or XHR
strategy if needed.

Further the platform-server has its own library for parsing HTML, so we
sniff for that (by checking whether DOMParser exists) and fall back to
the standard strategy.

Thanks to @cure53 for the heads up on this issue.
2018-02-13 10:05:14 -08:00
0dacf6d5f1 ci: use sudo: false on Travis (#21641)
Related to #21422.

PR Close #21641
2018-02-11 21:18:03 +02:00
e9f1d44015 ci: downgrade Chromium to a version that does not cause flakes
There seems to be some issue that causes Chrome/ChromeDriver to
unexpectedly reload during the aio e2e tests, causing flakes. It is not
clear what exactly is causing the reloading, but to the best of my
knowledge it is something inside Chrome or ChromeDriver.

Pinning Chrome to r494239 (between 62.0.3185.0 and 62.0.3186.0) fixes
the flakes.

Fixes #20159
2018-02-11 21:18:03 +02:00
1d9024ee9a build: pin ChromeDriver version (#20940)
Since our version of Chromium is also pinned, a new ChromeDriver (that
drops support for our Chromium version) can cause random (and unrelated
to the corresponding changes) errors on CI.
This commit pins the version of ChromeDriver and it should now be
manually upgraded to a vrsion that is compatible with th currently used
Chromium version.

PR Close #20940
2018-02-11 21:18:03 +02:00
6a6164ab4f revert: ci: use chrome stable (#18307)
This reverts commit 8bcb268140.
2018-02-11 19:39:55 +02:00
7231f5e26a docs: add changelog for 4.4.6 2017-10-18 16:14:53 -07:00
86415223cb release: cut the 4.4.6 release 2017-10-18 16:12:10 -07:00
269f5acc54 fix(common): attempt to JSON.parse errors for JSON responses (#19773)
Prior behavior for HttpClient was to parse the body as JSON when
responseType was set to 'json', even if the response was
unsuccessful. This changed due to a recent bugfix, and
unsuccessful responses had their bodies treated as strings.

There is no guarantee that if a service returns JSON in the
successful case that it will do so for errors. However, users
indicate that most APIs in the wild do work this way. Therefore,
this commit changes the error case behavior to attempt a JSON
parsing of the response body, and falls back on returning it as
a string if that fails.

PR Close #19773
2017-10-18 12:58:49 -07:00
6e6c866de9 docs(aio): changed confusing term (#19762)
Controller should be decorator I believe

PR Close #19762
2017-10-18 12:58:40 -07:00
732ed92cb7 test(animations): ensure :enter callbacks fire on container insertion (#19674)
PR Close #19674
2017-10-18 12:58:26 -07:00
53a807ae09 fix(router): RouterLinkActive should update its state right after checking the children
Closes #18983
2017-10-18 12:57:53 -07:00
3342a8253b fix(aio): make tests less flaky (#19784)
PR Close #19784
2017-10-18 10:19:48 -07:00
630c19f52d build: remove required BrowserStack run as it fails with “Access denied” (#19769)
See #19768
PR Close #19769
2017-10-17 15:53:05 -07:00
af8c2fa4be build: don’t make BrowserStack required as it fails with “Access denied”
See #19768
2017-10-17 14:56:41 -07:00
0789601dd6 build: fix broken path for animations in .pullapprove (#19453)
PR Close #19453
2017-10-17 10:44:43 -07:00
ce0ac46e42 style: fix formatting of check-node-modules (#19720)
PR Close #19720
2017-10-17 10:41:18 -07:00
b531d87580 ci: validate commit messages correctly when not on master (#19720)
PR Close #19720
2017-10-17 10:41:18 -07:00
23a2154817 docs(aio): update 2018 events (#19706)
update ac 2017 dates

PR Close #19706
2017-10-17 10:41:09 -07:00
76d2496f24 perf(animations): reduce size of bundle by removing AST classes (#19673)
This CL refactors the animation AST code to make use of interfaces
instead of classes. Given that interfaces are not persisted during
runtime the removal of classes should nicely cut down on size for the
animations-browser bundle.

PR Close #19673
2017-10-17 10:41:01 -07:00
b85cb410f1 docs(aio): change in-mem-web-api version for examples (#19668)
PR Close #19668
2017-10-17 10:40:52 -07:00
1be22df0df ci(aio): raise payload limit to accommodate the new search feature (#19704)
PR Close #19704
2017-10-13 15:20:23 -07:00
a805839d38 feat(aio): add search to 404 page (#19704)
The 404 page will now run a search based on the given URL to offer
suggestions for the page that the user really wanted.

PR Close #19704
2017-10-13 15:20:23 -07:00
3ac61a7550 refactor(aio): move SearchResultsComponent into shared module (#19704)
This will allow it to be used by an embedded component.

PR Close #19704
2017-10-13 15:20:23 -07:00
57ea33bc5c feat(aio): allow SearchService to have multiple clients (#19704)
PR Close #19704
2017-10-13 15:20:23 -07:00
4891649d68 test(aio): tidy up e2e tests that used invalid URLs (#19704)
PR Close #19704
2017-10-13 15:20:23 -07:00
93aba1bb1c build(aio): remove unused imports and local variables (#19704)
PR Close #19704
2017-10-13 15:20:23 -07:00
f983a6c615 fix(animations): properly support boolean-based transitions and state changes (#19672)
Closes #9396
Closes #12337

PR Close #19672
2017-10-13 09:40:29 -07:00
18f1b016e5 build(aio): consider devDependencies when overwriting dependencies of local Angular packages (#19687)
Previously, only `dependencies` were taken into account.

PR Close #19687
2017-10-13 09:30:10 -07:00
591dcc26af docs(animations): add missing bracket to fadeAnimation
Closes #18899
2017-10-13 09:14:29 -07:00
4acd322128 fix(core): don't refer to hydration in docs anymore.
Closes #18458
2017-10-13 09:05:30 -07:00
32a814bdfa docs: removing unnecessary commits from change log 2017-10-12 13:32:23 -07:00
912068e71c docs: add changelog for 4.4.5 2017-10-12 10:26:21 -07:00
df8e57dc5d release: cut the 4.4.5 release 2017-10-12 10:07:33 -07:00
f27f6e498f docs(aio): improve the Angular Connect homepage banner (#19684)
The original logo was too small.
The conference is over two days.

PR Close #19684
2017-10-12 09:49:58 -07:00
01bfbcb84a build(aio): update yarn.lock (#19683)
PR Close #19683
2017-10-12 09:49:48 -07:00
a15abbb324 docs(aio): change Material 2 to Material (#19663)
it is just called Angular Material now
PR Close #19663
2017-10-11 16:54:09 -07:00
28c29d560e build: fix rxjs version for aio (#19585)
PR Close #19585
2017-10-11 15:30:52 -07:00
65ca7fd4aa docs(aio): update banner ad (#19669)
PR Close #19669
2017-10-11 14:45:06 -07:00
3e3f918bb3 ci: freeze yarn lockfile when installing dependencies (#19616)
PR Close #19616
2017-10-11 14:37:38 -07:00
fc1dcffbdd build(aio): freeze lockfile when setting up examples (#19616)
PR Close #19616
2017-10-11 14:37:38 -07:00
ee3c681f98 build(aio): freeze yarn lockfile for aio-builds-setup scripts (#19616)
PR Close #19616
2017-10-11 14:37:37 -07:00
6225fedcb8 build(aio): freeze lockfile when installing example dependencies (#19616)
PR Close #19616
2017-10-11 14:37:37 -07:00
2905069559 ci(aio): freeze the lockfile for CI builds (#19616)
The CI will now fail if the dependencies in the AIO
package.json have been modified without the
lockfile being updated.

PR Close #19616
2017-10-11 14:37:37 -07:00
47202dd747 build(aio): improve accessor rendering (#19637)
Includes an update to `dgeni-packages@0.22.0` which provides more info
about accessors if required.

PR Close #19637
2017-10-11 14:37:28 -07:00
fb130c4eae build(aio): append information about links in and out of docs (#19583)
Closes #19560

PR Close #19583
2017-10-11 14:37:16 -07:00
734378c90b fix(compiler): TestBed.overrideProvider should keep imported NgModules eager (#19624)
Before, as soon as a user called `TestBed.overrideProvider` for a provider
of a `NgModule` that was imported via `TestBed.configureTestingModule`,
that `NgModule` became lazy.

This commit changes this behavior to keep the `NgModule` eager,
with or without a call to `TestBed.overrideProvider`.

PR Close #19624
2017-10-11 14:01:04 -07:00
3959b7ef28 refactor(compiler): introduce TestBed.deprecatedOverrideProvider (#19558)
This allows use to fix `TestBed.overrideProvider` to keep imported `NgModule`s eager,
while allowing our users to still keep the old semantics until they have fixed their
tests.

PR Close #19558
2017-10-11 14:00:32 -07:00
e292548523 fix(compiler): correctly instantiate eager providers that are used via Injector.get (#19558)
Closes #15501

PR Close #19558
2017-10-11 14:00:32 -07:00
c2506a78a1 docs(aio): add copy about ngNoForm to NgForm API doc (#19561)
PR Close #19561
2017-10-11 14:00:12 -07:00
34f70c6de2 build(aio): sort API list alphabetically (#19566)
Closes #19559

PR Close #19566
2017-10-11 13:52:19 -07:00
3d9c2a6352 build: add a Git .mailmap with my new name (#19550)
In this way my past contribution is mapped correctly.

PR Close #19550
2017-10-11 13:52:06 -07:00
3232125650 build(aio): remove unnecessary -- from yarn commands (#19565)
Essentially backports ffceae0a0 and f7199aa8c to 4.4.x.

PR Close #19565
2017-10-11 13:26:07 -07:00
c9f8718d2a fix(tsc-wrapped): don't rewrite imports when annotating for closure (#19579)
Port of f24ea59f74 to 4.4.x

Closure no longer needs to have the imports rewritten so avoid
rewriting as this can cause issues when the source directory
structure differs from what is deployed.

Fixes: #17171

PR Close #19579
2017-10-11 13:25:01 -07:00
f89ac92e5e test(aio): temporarily disable failing examples e2e tests (#19600)
Temporarily disable a couple of failing upgrade example e2e tests
to unblock the 4.4.x branch while investigating and fixing the
issues.
It seems to be some typings-related issues that TypeScript 2.5.3
complains about.

(Note: The same tests are temporarily disabled on master too.)

PR Close #19600
2017-10-10 15:58:45 -07:00
f82efcf942 build(aio): do not fail on first yarn setup (#19600)
PR Close #19600
2017-10-10 15:58:45 -07:00
dc22f4dc69 build(aio): fix overwriting with local Angular packages that depend on other local ones (#19600)
PR Close #19600
2017-10-10 15:58:45 -07:00
173ccf03ab test(aio): fix testing of NgPackagesInstaller (#19600)
PR Close #19600
2017-10-10 15:58:44 -07:00
1d5c3c1c9b build(aio): example-boilerplate is no longer responsible for yarn install (#19600)
The tooling for boilerplate was also running `yarn install` on the examples'
shared folder. But since this is handled by `ng-packages-installer` this
commit refactors the tools so that the boilerplate no longer does this
anymore.

PR Close #19600
2017-10-10 15:58:44 -07:00
e4dac421db build(aio): ignore @angular/mobile-toolkit in dist builds (#19600)
The new v5 version of this toolkit is too incompatible for us to run
AIO against both versions. So for now we will ignore this libary when
running against local Angular distributions and continue to use the
version on npm.

PR Close #19600
2017-10-10 15:58:44 -07:00
c639cdd3b3 build(aio): support ignoring dist packages in "local" mode (#19600)
PR Close #19600
2017-10-10 15:58:44 -07:00
5bcfa7cdfe build(aio): provide cleaner scripts for local build switching (#19600)
PR Close #19600
2017-10-10 15:58:44 -07:00
d8fd892e71 ci(aio): use custom package.json to run with local distributables (#19600)
Closes #19388

PR Close #19600
2017-10-10 15:58:44 -07:00
074a997302 build(aio): add support for using the locally built Angular packages for aio (#19600)
This commit allows building angular.io against the locally built Angular
packages. It adds two new npm scripts:

- `setup-local`: Same as `setup`, but overwrites the Angular packages for both
  angular.io and the examples boilerplate with the locally built ones.
- `build-local`: Same as `build`, but uses `setup-local` instead of `setup`
  under the hood, thus overwriting installed Angular packages with locally built
  ones.

Fixes #18611

PR Close #19600
2017-10-10 15:58:44 -07:00
62616f541a build(aio): ensure webdriver is updated when switching between local and npm deps (#19600)
PR Close #19600
2017-10-10 15:58:44 -07:00
156442f80d docs(aio): fix animations example to work with Angular v5 (#19600)
PR Close #19600
2017-10-10 15:58:44 -07:00
37112f549a build(aio): upgrade ts-node to support newer TypeScript config (#19600)
This was causing `Cannot find type definition file for 'jasmine'. (2688)`
errors when running Protractor.

PR Close #19600
2017-10-10 15:58:44 -07:00
cf4b4d53ba fix(aio): fix SearchService to work with TypeScript 2.4 (#19600)
The call to `race` required a type parameter to disambiguate the return type.

PR Close #19600
2017-10-10 15:58:43 -07:00
d45e3aa433 fix(aio): downgrade yarn to 1.0.2 temporarily (#19600)
PR Close #19600
2017-10-10 15:58:43 -07:00
ccc25ee901 fix: reformat files from previous cherry-picks 2017-10-05 13:54:46 -07:00
3052063c05 refactor(compiler): remove stale metadata classes for animations
These are no longer needed as animations are a purely
runtime concept.
2017-10-05 13:54:46 -07:00
e107322f5c refactor(core): add a checkIndex to the compiler view nodes
Each node now has two index: nodeIndex and checkIndex.

nodeIndex is the index in both the view definition and the view data.
checkIndex is the index in in the update function (update directives and update
renderer).

While nodeIndex and checkIndex have the same value for now, having both of them
will allow changing the structure of view definition after compilation (ie for
runtime translations).
2017-10-05 13:54:46 -07:00
f0774254de refactor(compiler): misc minor refactor / cleanup (#19189)
PR Close #19189
2017-10-05 13:54:46 -07:00
6c66031c4a fix(core): make dynamic & inline code checking behave the same (#19189)
PR Close #19189
2017-10-05 13:54:46 -07:00
8f668807cf Revert "fix(platform-browser): support customEqualityTesters when overriding Jasmine toEqual"
This reverts commit cc8ae32503.

Reason: broke a few existing tests.
2017-10-05 13:25:05 -07:00
f98eb35179 docs(aio): document Custom Async validators
docs(aio): Custom Async validators edits

incorporating suggestions from kapunahelewong
2017-10-04 15:08:00 -07:00
cc8ae32503 fix(platform-browser): support customEqualityTesters when overriding Jasmine toEqual 2017-10-04 15:01:11 -07:00
95f3b1dbe6 fix(compiler): disallow references for select and index evaluation
Also fixes an issue where enum values of 0 or '``' where not treated
correctly.

Fixes: #18170
2017-10-04 14:54:53 -07:00
6b96b069bf docs(aio): fix typo in preview server config file comment 2017-10-04 12:43:37 -07:00
ecfe85b06c docs(aio): document the special treatment of undefined 2017-10-04 12:43:28 -07:00
5439d4cd49 docs(aio): fix broken link in toh-pt5 2017-10-04 12:43:10 -07:00
df91fd032d build: update npm dependencies 2017-10-04 10:22:48 -07:00
2d5ef15e08 build: switch from npm to yarn 2017-10-04 10:22:48 -07:00
544a7ad0a5 docs(aio): Fix incorrect wording 2017-10-03 08:16:16 -07:00
ec2c1bec4a build(aio): fix various API rendering issues
Upgrading to dgeni-packages 0.21.4 gives us
access to more properties on the API docs, which
allows us to fix the following issues:

Closes #19450
Closes #19452
Closes #19456
2017-10-03 08:15:25 -07:00
e064d2607d docs(aio): add ngVikings to the events page 2017-10-03 08:14:30 -07:00
d489ad595d docs: add changelog for 4.4.4 2017-09-28 12:18:14 -07:00
0a3753bcce release: cut the 4.4.4 release 2017-09-28 12:13:13 -07:00
7fc2dceaf5 fix(compiler): do not consider a reference with members as a reference (#19466)
fixes: #18170
ref: #19454
2017-09-28 12:04:20 -07:00
c3b39bac52 fix(compiler): correctly map error message locations (#19424) 2017-09-28 10:55:17 -07:00
2cd88bfb0f docs(animations): document usage of negative limit values for query (#19451) 2017-09-28 09:35:10 -07:00
0cefb0b79b docs(aio): add copy about NgForOf (#18686) 2017-09-27 15:29:23 -07:00
55a7443974 docs(aio): edit summary and next step headers (#16962) 2017-09-27 13:47:03 -07:00
f9ebaf1b90 docs(aio): remove links to top of documents (#16971) 2017-09-27 13:46:50 -07:00
bc81fbdd27 fix(animations): properly support the query limit option value (#19419)
Closes #19232
2017-09-26 13:18:47 -07:00
c7aa8a132d fix(compiler): skip   when trimming / removing whitespaces (#19310)
Fixes #19304
2017-09-25 13:35:32 -07:00
5c99b01512 build(aio): run the upload server as a non-previleged user
closes #19352

Previously, the upload server (for PR previews) was run as root and
"downleveled" to a non-privileged user from inside the node script.

Now, with the latest version of `pm2` (which is used to run the upload server
scripts), we can get rid of that workaround and set the desired UID directly
through `pm2`.
2017-09-25 12:05:00 -07:00
992ba33a28 build(aio): upgrade all preview server dependencies 2017-09-25 12:04:51 -07:00
17e7c58981 build(aio): add metadata aliases for directives, components and pipes (#19317)
This change will enable people to link to the API docs via their selectors
or names, as used in a template.

Since the selectors can be quite complex we are not able to get 100%
accuracy.

Closes #16787
2017-09-25 12:04:41 -07:00
b8f15d2b77 build(aio): render class/interface "descendants" in API docs (#19343)
For classes, the tree of subclasses is rendered, recursively.

For interfaces, the descendants are separated into child interfaces, which
extend the interface, and classes, which implement the interface.

Closes #19306
2017-09-25 12:04:28 -07:00
5f9a10aab9 build(aio): ensure decorators with shared interface types are found (#19361)
Closes #19358
2017-09-25 12:03:31 -07:00
04bc5257a6 docs(aio): tidy up tooling documentation (#18151)
PR Close #18151
2017-09-21 11:27:01 -07:00
5bca58e748 docs(aio): applying some feedback (#18151)
PR Close #18151
2017-09-21 11:27:01 -07:00
526a67f8f4 docs(aio): add zipper documentation (#18151)
PR Close #18151
2017-09-21 11:27:00 -07:00
53a27e07b1 docs(aio): complete the plunker-builder docs (#18151)
PR Close #18151
2017-09-21 11:27:00 -07:00
ba0fb1e055 docs(aio): add boilerplate readme (#18151)
PR Close #18151
2017-09-21 11:27:00 -07:00
ed2c8aa6f8 docs(aio): high-level documentation of the transforms folder (#18151)
PR Close #18151
2017-09-21 11:27:00 -07:00
9226760120 docs(aio): high-level documentation of AIO tooling (#18151)
PR Close #18151
2017-09-21 11:27:00 -07:00
b9ee8b46a0 refactor(core): viewDef related refactoring (#19272)
- optimize the way node flags are propagated in `viewDef()`,
- fix `elementDef()` signature to make `namespaceAndName` nullable,
- move render parent computation with the parent computation

PR Close #19272
2017-09-21 11:26:43 -07:00
e7eb0b8b7c docs: update README and author guide about doc build errors (#19276)
- tells reader about `yarn serve-and-sync`.
- directs reader to look to docs-style-guide if get doc build error.
- update docs-style-guide to warn about ignored code files.

PR Close #19276
2017-09-21 09:56:33 -07:00
ae52851458 fix(tsc-wrapped): add metadata for type declarations (#19040)
PR Close #19040
2017-09-20 17:06:25 -07:00
e63cf3b89e ci(aio): correctly serialize commit messages with "double-quotes" (#19184)
PR Close #19184
2017-09-20 17:05:31 -07:00
9624fda082 build(aio): improve error message for ignored example files (#19265)
Addresses https://github.com/angular/angular/pull/18707#issuecomment-330396771

PR Close #19265
2017-09-20 16:50:36 -07:00
e19b6a8f38 build(aio): remove commented out code (#19265)
PR Close #19265
2017-09-20 16:50:36 -07:00
2aa6b54201 build: pin docker image to a tag for CircleCI (#19295)
Fixes the broken build
PR Close #19295
2017-09-20 16:50:20 -07:00
fe8550d278 docs: animations - replace iterator with simple code style (#18965)
Replaces iterator facade over the HeroService because webpack threw up.
Also this was an obscure distraction for readers with no obvious benefits.

PR Close #18965
2017-09-20 16:50:05 -07:00
e2e8ba6ffa release: cut the 4.4.3 release 2017-09-19 15:22:05 -07:00
60cdf9dc38 docs(CHANGELOG.MD): add release notes for 4.4.2 and 4.4.3 2017-09-19 15:21:51 -07:00
0371538d10 fix(tsc-wrapped): deduplicate metadata only when the module is the same (#19261)
Fixes #19219
PR Close #19261
2017-09-19 14:25:27 -07:00
351331a81b ci: update yarn to 1.0.2 (#19270)
PR Close #19270
2017-09-19 13:53:09 -07:00
0a54713f8a build: automatically version tsc-wrapped just like other packages (#19270)
This removes manual steps from the release list.

I had to modify a few integration tests not to install compiler-cli dirrectly
because it depends on tsc-wrapped which yarn was eager to fetch from npm rather
than from the local build.

PR Close #19270
2017-09-19 13:53:09 -07:00
337 changed files with 11953 additions and 17726 deletions

View File

@ -11,7 +11,7 @@
anchor_1: &job_defaults
working_directory: ~/ng
docker:
- image: angular/ngcontainer
- image: angular/ngcontainer:0.0.2
# After checkout, rebase on top of master.
# Similar to travis behavior, but not quite the same.
@ -27,10 +27,9 @@ jobs:
- checkout:
<<: *post_checkout
- restore_cache:
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
- run: npm install
- run: npm run postinstall
- run: yarn install --freeze-lockfile --non-interactive
- run: ./node_modules/.bin/gulp lint
build:
@ -39,12 +38,12 @@ jobs:
- checkout:
<<: *post_checkout
- restore_cache:
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
- run: bazel run @build_bazel_rules_typescript_node//:bin/npm install
- run: bazel run @yarn//:yarn
- run: bazel build ...
- save_cache:
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
paths:
- "node_modules"

1
.mailmap Normal file
View File

@ -0,0 +1 @@
Michał Gołębiowski-Owczarek <m.goleb@gmail.com>

View File

@ -104,7 +104,7 @@ groups:
animations:
conditions:
files:
- "packages/animation/*"
- "packages/animations/*"
- "packages/platform-browser/animations/*"
users:
- matsko #primary

View File

@ -1,12 +1,10 @@
language: node_js
sudo: false
# force trusty as Google Chrome addon is not supported on Precise
dist: trusty
node_js:
- '6.9.5'
addons:
chrome: stable
# firefox: "38.0"
apt:
sources:
@ -50,7 +48,8 @@ env:
- CI_MODE=e2e_2
- CI_MODE=js
- CI_MODE=saucelabs_required
- CI_MODE=browserstack_required
# deactivated, see #19768
# - CI_MODE=browserstack_required
- CI_MODE=saucelabs_optional
- CI_MODE=browserstack_optional
- CI_MODE=aio_tools_test

View File

@ -1,3 +1,76 @@
<a name="4.4.7"></a>
## [4.4.7](https://github.com/angular/angular/compare/4.4.6...4.4.7) (2018-04-16)
### Bug Fixes
* **core:** use appropriate inert document strategy for Firefox & Safari ([#22077](https://github.com/angular/angular/issues/22077)) ([2c5cf19](https://github.com/angular/angular/commit/2c5cf19))
<a name="4.4.6"></a>
## [4.4.6](https://github.com/angular/angular/compare/4.4.5...4.4.6) (2017-10-18)
### Bug Fixes
* **animations:** properly support boolean-based transitions and state changes ([#19672](https://github.com/angular/angular/issues/19672)) ([f983a6c](https://github.com/angular/angular/commit/f983a6c)), closes [#9396](https://github.com/angular/angular/issues/9396) [#12337](https://github.com/angular/angular/issues/12337)
* **common:** attempt to JSON.parse errors for JSON responses ([#19773](https://github.com/angular/angular/issues/19773)) ([269f5ac](https://github.com/angular/angular/commit/269f5ac))
* **router:** RouterLinkActive should update its state right after checking the children ([53a807a](https://github.com/angular/angular/commit/53a807a)), closes [#18983](https://github.com/angular/angular/issues/18983)
### Performance Improvements
* **animations:** reduce size of bundle by removing AST classes ([#19673](https://github.com/angular/angular/issues/19673)) ([76d2496](https://github.com/angular/angular/commit/76d2496))
<a name="4.4.5"></a>
## [4.4.5](https://github.com/angular/angular/compare/4.4.4...4.4.5) (2017-10-12)
### Bug Fixes
* **compiler:** `TestBed.overrideProvider` should keep imported `NgModule`s eager ([#19624](https://github.com/angular/angular/issues/19624)) ([734378c](https://github.com/angular/angular/commit/734378c))
* **compiler:** correctly instantiate eager providers that are used via `Injector.get` ([#19558](https://github.com/angular/angular/issues/19558)) ([e292548](https://github.com/angular/angular/commit/e292548)), closes [#15501](https://github.com/angular/angular/issues/15501)
* **compiler:** disallow references for select and index evaluation ([95f3b1d](https://github.com/angular/angular/commit/95f3b1d))
* **core:** make dynamic & inline code checking behave the same ([#19189](https://github.com/angular/angular/issues/19189)) ([6c66031](https://github.com/angular/angular/commit/6c66031))
* **platform-browser:** support customEqualityTesters when overriding Jasmine toEqual ([cc8ae32](https://github.com/angular/angular/commit/cc8ae32))
* **tsc-wrapped:** don't rewrite imports when annotating for closure ([#19579](https://github.com/angular/angular/issues/19579)) ([c9f8718](https://github.com/angular/angular/commit/c9f8718))
<a name="4.4.4"></a>
## [4.4.4](https://github.com/angular/angular/compare/4.4.3...4.4.4) (2017-09-28)
### Bug Fixes
* **animations:** support negative query limit value ([#19419](https://github.com/angular/angular/issues/19419)) ([bc81fbd](https://github.com/angular/angular/commit/bc81fbd)), closes [#19232](https://github.com/angular/angular/issues/19232)
* **compiler:** correctly map error message locations ([#19424](https://github.com/angular/angular/issues/19424)) ([c3b39ba](https://github.com/angular/angular/commit/c3b39ba))
* **compiler:** do not consider a reference with members as a reference ([#19466](https://github.com/angular/angular/issues/19466)) ([7fc2dce](https://github.com/angular/angular/commit/7fc2dce))
* **compiler:** skip &nbsp; when trimming / removing whitespaces ([#19310](https://github.com/angular/angular/issues/19310)) ([c7aa8a1](https://github.com/angular/angular/commit/c7aa8a1)), closes [#19304](https://github.com/angular/angular/issues/19304)
* **tsc-wrapped:** add metadata for `type` declarations ([#19040](https://github.com/angular/angular/issues/19040)) ([ae52851](https://github.com/angular/angular/commit/ae52851))
<a name="4.4.3"></a>
## [4.4.3](https://github.com/angular/angular/compare/4.4.2...4.4.3) (2017-09-19)
### Bug Fixes
* **tsc-wrapped:** deduplicate metadata only when the module is the same ([#19261](https://github.com/angular/angular/issues/19261)) ([0371538](https://github.com/angular/angular/commit/0371538)), closes [#19219](https://github.com/angular/angular/issues/19219)
<a name="4.4.2"></a>
## [4.4.2](https://github.com/angular/angular/compare/4.4.1...4.4.2) (2017-09-18)
### Bug Fixes
* **platform-server**: fix for packaging issues [#19250](https://github.com/angular/angular/issues/19250)
<a name="4.4.1"></a>
# [4.4.1](https://github.com/angular/angular/compare/4.3.6...4.4.1) (2017-09-15)

View File

@ -13,7 +13,11 @@ You should run all these tasks from the `angular/aio` folder.
Here are the most important tasks you might need to use:
* `yarn` - install all the dependencies.
* `yarn setup` - Install all the dependencies, boilerplate, plunkers, zips and runs dgeni on the docs.
* `yarn setup` - install all the dependencies, boilerplate, plunkers, zips and run dgeni on the docs.
* `yarn setup-local` - same as `setup`, but use the locally built Angular packages for aio and docs examples boilerplate.
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
* `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
* `yarn serve-and-sync` - run both the `docs-watch` and `start` in the same console.
@ -26,33 +30,33 @@ Here are the most important tasks you might need to use:
* `yarn docs-lint` - check that the doc gen code follows our style rules.
* `yarn docs-test` - run the unit tests for the doc generation code.
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally. Add the option `-- --local` to use your local version of Angular contained in the "dist" folder.
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally. Add the option `--local` to use your local version of Angular contained in the "dist" folder.
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
* `yarn generate-plunkers` - generate the plunker files that are used by the `live-example` tags in the docs.
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
* `yarn example-e2e` - run all e2e tests for examples
- `yarn example-e2e -- --setup` - force webdriver update & other setup, then run tests
- `yarn example-e2e -- --filter=foo` - limit e2e tests to those containing the word "foo"
- `yarn example-e2e -- --setup --local` - run e2e tests with the local version of Angular contained in the "dist" folder
- `yarn example-e2e --setup` - force webdriver update & other setup, then run tests
- `yarn example-e2e --filter=foo` - limit e2e tests to those containing the word "foo"
- `yarn example-e2e --setup --local` - run e2e tests with the local version of Angular contained in the "dist" folder
* `yarn build-ie-polyfills` - generates a js file of polyfills that can be loaded in Internet Explorer.
## Using ServiceWorker locally
Since abb36e3cb, running `yarn start -- --prod` will no longer set up the ServiceWorker, which
Since abb36e3cb, running `yarn start --prod` will no longer set up the ServiceWorker, which
would require manually running `yarn sw-manifest` and `yarn sw-copy` (something that is not possible
with webpack serving the files from memory).
If you want to test ServiceWorker locally, you can use `yarn build` and serve the files in `dist/`
with `yarn http-server -- dist -p 4200`.
with `yarn http-server dist -p 4200`.
For more details see #16745.
## Guide to authoring
There are two types of content in the documentatation:
There are two types of content in the documentation:
* **API docs**: descriptions of the modules, classes, interfaces, decorators, etc that make up the Angular platform.
API docs are generated directly from the source code.
@ -107,8 +111,16 @@ yarn start
yarn docs-watch
```
>Alternatively, try the consolidated `serve-and-sync` command that builds, watches and serves in the same terminal window
```bash
yarn serve-and-sync
```
* Open a browser at https://localhost:4200/ and navigate to the document on which you want to work.
You can automatically open the browser by using `yarn start -- -o` in the first terminal.
You can automatically open the browser by using `yarn start -o` in the first terminal.
* Make changes to the page's associated doc or example files. Every time a file is saved, the doc will
be regenerated, the app will rebuild and the page will reload.
* If you get a build error complaining about examples or any other odd behavior, be sure to consult
the [Authors Style Guide](https://angular.io/guide/docs-style-guide).

View File

@ -156,7 +156,7 @@ RUN find $AIO_SCRIPTS_SH_DIR -maxdepth 1 -type f -printf "%P\n" \
# Set up the Node.js scripts
COPY scripts-js/ $AIO_SCRIPTS_JS_DIR/
WORKDIR $AIO_SCRIPTS_JS_DIR/
RUN yarn install --production
RUN yarn install --production --freeze-lockfile
# Set up health check

View File

@ -6,7 +6,7 @@ server=8.8.4.4
# Listen for DHCP and DNS requests only on this address.
listen-address=127.0.0.1
# Force an IP addres for these domains.
# Force an IP address for these domains.
address=/{{$AIO_NGINX_HOSTNAME}}/127.0.0.1
address=/{{$AIO_UPLOAD_HOSTNAME}}/127.0.0.1
address=/{{$TEST_AIO_NGINX_HOSTNAME}}/127.0.0.1

View File

@ -13,10 +13,8 @@ const AIO_REPO_SLUG = getEnvVar('AIO_REPO_SLUG');
const AIO_TRUSTED_PR_LABEL = getEnvVar('AIO_TRUSTED_PR_LABEL');
const AIO_UPLOAD_HOSTNAME = getEnvVar('AIO_UPLOAD_HOSTNAME');
const AIO_UPLOAD_PORT = +getEnvVar('AIO_UPLOAD_PORT');
const AIO_WWW_USER = getEnvVar('AIO_WWW_USER');
// Run
process.setuid(AIO_WWW_USER); // TODO(gkalpak): Find more suitable way to run as `www-data`.
_main();
// Functions

View File

@ -18,7 +18,7 @@ const TEST_AIO_UPLOAD_PORT = +getEnvVar('TEST_AIO_UPLOAD_PORT');
const WWW_USER = getEnvVar('AIO_WWW_USER');
// Interfaces - Types
export interface CmdResult { success: boolean; err: Error; stdout: string; stderr: string; }
export interface CmdResult { success: boolean; err: Error | null; stdout: string; stderr: string; }
export interface FileSpecs { content?: string; size?: number; }
export type CleanUpFn = () => void;
@ -143,7 +143,7 @@ class Helper {
statusText = status[1];
} else {
statusCode = status;
statusText = http.STATUS_CODES[statusCode];
statusText = http.STATUS_CODES[statusCode] || 'UNKNOWN_STATUS_CODE';
}
return (result: CmdResult) => {
@ -196,7 +196,7 @@ class Helper {
}
// Methods - Protected
protected createCleanUpFn(fn: Function): CleanUpFn {
protected createCleanUpFn(fn: () => void): CleanUpFn {
const cleanUpFn = () => {
const idx = this.cleanUpFns.indexOf(cleanUpFn);
if (idx !== -1) {

View File

@ -8,7 +8,7 @@
"scripts": {
"prebuild": "yarn clean-dist",
"build": "tsc",
"build-watch": "yarn tsc -- --watch",
"build-watch": "yarn tsc --watch",
"clean-dist": "node --eval \"require('shelljs').rm('-rf', 'dist')\"",
"dev": "concurrently --kill-others --raw --success first \"yarn build-watch\" \"yarn test-watch\"",
"lint": "tslint --project tsconfig.json",
@ -20,26 +20,26 @@
"test-watch": "nodemon --exec \"yarn ~~test-only\" --watch dist"
},
"dependencies": {
"body-parser": "^1.17.2",
"express": "^4.14.1",
"jasmine": "^2.5.3",
"jsonwebtoken": "^7.3.0",
"shelljs": "^0.7.6"
"body-parser": "^1.18.2",
"express": "^4.15.4",
"jasmine": "^2.8.0",
"jsonwebtoken": "^8.0.1",
"shelljs": "^0.7.8",
"tslib": "^1.7.1"
},
"devDependencies": {
"@types/body-parser": "^1.16.4",
"@types/express": "^4.0.35",
"@types/jasmine": "^2.5.43",
"@types/jsonwebtoken": "^7.2.0",
"@types/node": "^7.0.5",
"@types/shelljs": "^0.7.0",
"@types/supertest": "^2.0.0",
"concurrently": "^3.3.0",
"eslint": "^3.15.0",
"eslint-plugin-jasmine": "^2.2.0",
"nodemon": "^1.11.0",
"@types/body-parser": "^1.16.5",
"@types/express": "^4.0.37",
"@types/jasmine": "^2.6.0",
"@types/jsonwebtoken": "^7.2.3",
"@types/node": "^8.0.30",
"@types/shelljs": "^0.7.4",
"@types/supertest": "^2.0.3",
"concurrently": "^3.5.0",
"nodemon": "^1.12.1",
"supertest": "^3.0.0",
"tslint": "^4.4.2",
"typescript": "^2.1.6"
"tslint": "^5.7.0",
"tslint-jasmine-noSkipOrFocus": "^1.0.8",
"typescript": "^2.5.2"
}
}

View File

@ -39,8 +39,8 @@ describe('BuildCleaner', () => {
let cleanerGetExistingBuildNumbersSpy: jasmine.Spy;
let cleanerGetOpenPrNumbersSpy: jasmine.Spy;
let cleanerRemoveUnnecessaryBuildsSpy: jasmine.Spy;
let existingBuildsDeferred: {resolve: Function, reject: Function};
let openPrsDeferred: {resolve: Function, reject: Function};
let existingBuildsDeferred: {resolve: (v?: any) => void, reject: (e?: any) => void};
let openPrsDeferred: {resolve: (v?: any) => void, reject: (e?: any) => void};
let promise: Promise<void>;
beforeEach(() => {
@ -195,7 +195,7 @@ describe('BuildCleaner', () => {
describe('getOpenPrNumbers()', () => {
let prDeferred: {resolve: Function, reject: Function};
let prDeferred: {resolve: (v: any) => void, reject: (v: any) => void};
let promise: Promise<number[]>;
beforeEach(() => {
@ -277,7 +277,10 @@ describe('BuildCleaner', () => {
it('should catch errors and log them', () => {
const consoleErrorSpy = spyOn(console, 'error');
shellRmSpy.and.callFake(() => { throw 'Test'; });
shellRmSpy.and.callFake(() => {
// tslint:disable-next-line: no-string-throw
throw 'Test';
});
(cleaner as any).removeDir('/foo/bar');

View File

@ -56,7 +56,7 @@ describe('GithubApi', () => {
it('should not pass data to \'request()\'', () => {
(api.get as Function)('foo', {}, {});
(api.get as any)('foo', {}, {});
expect(apiRequestSpy).toHaveBeenCalled();
expect(apiRequestSpy.calls.argsFor(0)[2]).toBeUndefined();
@ -144,7 +144,7 @@ describe('GithubApi', () => {
describe('getPaginated()', () => {
let deferreds: {resolve: Function, reject: Function}[];
let deferreds: {resolve: (v: any) => void, reject: (v: any) => void}[];
beforeEach(() => {
deferreds = [];
@ -292,7 +292,7 @@ describe('GithubApi', () => {
describe('onResponse', () => {
let promise: Promise<Object>;
let promise: Promise<object>;
let respond: (statusCode: number) => IncomingMessage;
beforeEach(() => {

View File

@ -22,7 +22,7 @@ describe('GithubPullRequests', () => {
describe('addComment()', () => {
let prs: GithubPullRequests;
let deferred: {resolve: Function, reject: Function};
let deferred: {resolve: (v: any) => void, reject: (v: any) => void};
beforeEach(() => {
prs = new GithubPullRequests('12345', 'foo/bar');

View File

@ -223,6 +223,7 @@ describe('BuildCreator', () => {
it('should reject with an UploadError', done => {
// tslint:disable-next-line: no-string-throw
shellMkdirSpy.and.callFake(() => { throw 'Test'; });
bc.create(pr, sha, archive, isPublic).catch(err => {
expectToBeUploadError(err, 500, `Error while uploading to directory: ${shaDir}\nTest`);
@ -407,6 +408,7 @@ describe('BuildCreator', () => {
it('should reject with an UploadError', done => {
// tslint:disable-next-line: no-string-throw
shellMvSpy.and.callFake(() => { throw 'Test'; });
bc.updatePrVisibility(pr, makePublic).catch(err => {
expectToBeUploadError(err, 500, `Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\nTest`);
@ -434,11 +436,11 @@ describe('BuildCreator', () => {
describe('exists()', () => {
let fsAccessSpy: jasmine.Spy;
let fsAccessCbs: Function[];
let fsAccessCbs: ((v?: any) => void)[];
beforeEach(() => {
fsAccessCbs = [];
fsAccessSpy = spyOn(fs, 'access').and.callFake((_: string, cb: Function) => fsAccessCbs.push(cb));
fsAccessSpy = spyOn(fs, 'access').and.callFake((_: string, cb: (v?: any) => void) => fsAccessCbs.push(cb));
});
@ -482,7 +484,7 @@ describe('BuildCreator', () => {
let shellChmodSpy: jasmine.Spy;
let shellRmSpy: jasmine.Spy;
let cpExecSpy: jasmine.Spy;
let cpExecCbs: Function[];
let cpExecCbs: ((...args: any[]) => void)[];
beforeEach(() => {
cpExecCbs = [];
@ -490,7 +492,7 @@ describe('BuildCreator', () => {
consoleWarnSpy = spyOn(console, 'warn');
shellChmodSpy = spyOn(shell, 'chmod');
shellRmSpy = spyOn(shell, 'rm');
cpExecSpy = spyOn(cp, 'exec').and.callFake((_: string, cb: Function) => cpExecCbs.push(cb));
cpExecSpy = spyOn(cp, 'exec').and.callFake((_: string, cb: (...args: any[]) => void) => cpExecCbs.push(cb));
});
@ -556,7 +558,11 @@ describe('BuildCreator', () => {
done();
});
shellChmodSpy.and.callFake(() => { throw 'Test'; });
shellChmodSpy.and.callFake(() => {
// tslint:disable-next-line: no-string-throw
throw 'Test';
});
cpExecCbs[0]();
});
@ -569,7 +575,11 @@ describe('BuildCreator', () => {
done();
});
shellRmSpy.and.callFake(() => { throw 'Test'; });
shellRmSpy.and.callFake(() => {
// tslint:disable-next-line: no-string-throw
throw 'Test';
});
cpExecCbs[0]();
});

View File

@ -116,7 +116,7 @@ describe('uploadServerFactory', () => {
it('should log the server address info on \'listening\'', () => {
const consoleInfoSpy = spyOn(console, 'info');
const server = createUploadServer('builds/dir');
const server = createUploadServer();
server.address = () => ({address: 'foo', family: '', port: 1337});
expect(consoleInfoSpy).not.toHaveBeenCalled();

View File

@ -1,25 +1,66 @@
{
"compilerOptions": {
"alwaysStrict": true,
"forceConsistentCasingInFileNames": true,
"inlineSourceMap": true,
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": [
"es2016"
],
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "dist",
"pretty": true,
"rootDir": ".",
"skipLibCheck": true,
"strictNullChecks": true,
"target": "es5",
"es2015",
"es2016.array.include"
], /* Specify library files to be included in the compilation: */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "dist", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
"importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
"noUnusedLocals": true, /* Report errors on unused locals. */
"noUnusedParameters": true, /* Report errors on unused parameters. */
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": [
"node_modules/@types"
]
], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
"inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Other */
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
"newLine": "LF", /* Use the specified end of line sequence to be used when emitting files: "crlf" (windows) or "lf" (unix). */
"pretty": true, /* Stylize errors and messages using color and context. */
"skipLibCheck": true /* Skip type checking of all declaration files (*.d.ts). */
},
"include": [
"lib/**/*",

View File

@ -1,5 +1,9 @@
{
"extends": "tslint:recommended",
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"jsRules": {},
"rules": {
"array-type": [true, "array"],
"arrow-parens": [true, "ban-single-arg-parens"],
@ -7,9 +11,14 @@
"max-classes-per-file": [true, 4],
"no-consecutive-blank-lines": [true, 2],
"no-console": [false],
"no-focused-test": true,
"no-namespace": [true, "allow-declarations"],
"no-skipped-test": true,
"no-string-literal": false,
"quotemark": [true, "single"],
"variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore"]
}
},
"rulesDirectory": [
"node_modules/tslint-jasmine-noSkipOrFocus"
]
}

File diff suppressed because it is too large Load Diff

View File

@ -6,10 +6,9 @@ export AIO_GITHUB_TOKEN=$(head -c -1 /aio-secrets/GITHUB_TOKEN 2>/dev/null || ec
export AIO_PREVIEW_DEPLOYMENT_TOKEN=$(head -c -1 /aio-secrets/PREVIEW_DEPLOYMENT_TOKEN 2>/dev/null || echo "MISSING_PREVIEW_DEPLOYMENT_TOKEN")
# Start the upload-server instance
# TODO(gkalpak): Ideally, the upload server should be run as a non-privileged user.
# (Currently, there doesn't seem to be a straight forward way.)
action=$([ "$1" == "stop" ] && echo "stop" || echo "start")
pm2 $action $AIO_SCRIPTS_JS_DIR/dist/lib/upload-server \
--uid $AIO_WWW_USER \
--log /var/log/aio/upload-server-prod.log \
--name aio-upload-server-prod \
${@:2}

View File

@ -15,13 +15,12 @@ export AIO_GITHUB_TOKEN=$(head -c -1 /aio-secrets/TEST_GITHUB_TOKEN 2>/dev/null
export AIO_PREVIEW_DEPLOYMENT_TOKEN=$(head -c -1 /aio-secrets/TEST_PREVIEW_DEPLOYMENT_TOKEN 2>/dev/null || echo "TEST_PREVIEW_DEPLOYMENT_TOKEN")
# Start the upload-server instance
# TODO(gkalpak): Ideally, the upload server should be run as a non-privileged user.
# (Currently, there doesn't seem to be a straight forward way.)
appName=aio-upload-server-test
if [[ "$1" == "stop" ]]; then
pm2 delete $appName
else
pm2 start $AIO_SCRIPTS_JS_DIR/dist/lib/verify-setup/start-test-upload-server.js \
--uid $AIO_WWW_USER \
--log /var/log/aio/upload-server-test.log \
--name $appName \
--no-autorestart \

View File

@ -9,7 +9,7 @@ VM host to update the preview server based on changes in the source code.
The script will pull the latest changes from the origin's master branch and examine if there have
been any changes in files inside the preview server source code directory (see below). If there are,
it will create a new image and verify that is works as expected. Finally, it will stop and remove
the old docker container and image, create and new container based on the new image and start it.
the old docker container and image, create a new container based on the new image and start it.
The script assumes that the preview server source code is in the repository's
`aio/aio-builds-setup/` directory and expects the following inputs:

View File

@ -9,7 +9,7 @@ readonly defaultImageNameAndTag="aio-builds:latest"
# (Necessary, because only `scripts-js/dist/` is copied to the docker image.)
(
cd "$SCRIPTS_JS_DIR"
yarn install
yarn install --freeze-lockfile --non-interactive
yarn build
)

View File

@ -7,6 +7,6 @@ source "`dirname $0`/_env.sh"
# Test `scripts-js/`
(
cd "$SCRIPTS_JS_DIR"
yarn install
yarn install --freeze-lockfile --non-interactive
yarn test
)

View File

@ -335,17 +335,17 @@ describe('Animation Tests', () => {
});
}
function getBoundingClientWidth(el: ElementFinder): promise.Promise<number> {
function getBoundingClientWidth(el: ElementFinder) {
return browser.executeScript(
'return arguments[0].getBoundingClientRect().width',
el.getWebElement()
);
) as PromiseLike<number>;
}
function getOffsetWidth(el: ElementFinder): promise.Promise<number> {
function getOffsetWidth(el: ElementFinder) {
return browser.executeScript(
'return arguments[0].offsetWidth',
el.getWebElement()
);
) as PromiseLike<number>;
}
});

View File

@ -1,5 +1,6 @@
// #docregion animations-module
// #docplaster
import { NgModule } from '@angular/core';
// #docregion animations-module
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
// #enddocregion animations-module
@ -15,11 +16,12 @@ import { HeroListAutoComponent } from './hero-list-auto.component';
import { HeroListGroupsComponent } from './hero-list-groups.component';
import { HeroListMultistepComponent } from './hero-list-multistep.component';
import { HeroListTimingsComponent } from './hero-list-timings.component';
// #docregion animations-module
// #docregion animation-module
@NgModule({
imports: [ BrowserModule, BrowserAnimationsModule ],
// #enddocregion animation-module
// ... more stuff ...
// #enddocregion animations-module
declarations: [
HeroTeamBuilderComponent,
HeroListBasicComponent,
@ -34,5 +36,8 @@ import { HeroListTimingsComponent } from './hero-list-timings.component';
HeroListGroupsComponent
],
bootstrap: [ HeroTeamBuilderComponent ]
// #docregion animations-module
})
export class AppModule { }
// #enddocregion animations-module

View File

@ -10,7 +10,7 @@ import {
transition
} from '@angular/animations';
import { Heroes } from './hero.service';
import { Hero } from './hero.service';
@Component({
selector: 'hero-list-auto',
@ -43,5 +43,5 @@ import { Heroes } from './hero.service';
// #enddocregion animationdef
})
export class HeroListAutoComponent {
@Input() heroes: Heroes;
@Input() heroes: Hero[];
}

View File

@ -14,7 +14,7 @@ import {
} from '@angular/animations';
// #enddocregion imports
import { Heroes } from './hero.service';
import { Hero } from './hero.service';
@Component({
selector: 'hero-list-basic',
@ -66,5 +66,5 @@ import { Heroes } from './hero.service';
// #enddocregion animationdef
})
export class HeroListBasicComponent {
@Input() heroes: Heroes;
@Input() heroes: Hero[];
}

View File

@ -13,7 +13,7 @@ import {
} from '@angular/animations';
// #enddocregion imports
import { Heroes } from './hero.service';
import { Hero } from './hero.service';
@Component({
selector: 'hero-list-combined-transitions',
@ -55,5 +55,5 @@ import { Heroes } from './hero.service';
// #enddocregion animationdef
})
export class HeroListCombinedTransitionsComponent {
@Input() heroes: Heroes;
@Input() heroes: Hero[];
}

View File

@ -10,7 +10,7 @@ import {
transition
} from '@angular/animations';
import { Heroes } from './hero.service';
import { Hero } from './hero.service';
@Component({
selector: 'hero-list-enter-leave-states',
@ -59,5 +59,5 @@ import { Heroes } from './hero.service';
// #enddocregion animationdef
})
export class HeroListEnterLeaveStatesComponent {
@Input() heroes: Heroes;
@Input() heroes: Hero[];
}

View File

@ -10,7 +10,7 @@ import {
transition
} from '@angular/animations';
import { Heroes } from './hero.service';
import { Hero } from './hero.service';
@Component({
selector: 'hero-list-enter-leave',
@ -47,5 +47,5 @@ import { Heroes } from './hero.service';
// #enddocregion animationdef
})
export class HeroListEnterLeaveComponent {
@Input() heroes: Heroes;
@Input() heroes: Hero[];
}

View File

@ -11,7 +11,7 @@ import {
group
} from '@angular/animations';
import { Heroes } from './hero.service';
import { Hero } from './hero.service';
@Component({
selector: 'hero-list-groups',
@ -76,5 +76,5 @@ import { Heroes } from './hero.service';
// #enddocregion animationdef
})
export class HeroListGroupsComponent {
@Input() heroes: Heroes;
@Input() heroes: Hero[];
}

View File

@ -12,7 +12,7 @@ import {
} from '@angular/animations';
// #enddocregion imports
import { Heroes } from './hero.service';
import { Hero } from './hero.service';
@Component({
selector: 'hero-list-inline-styles',
@ -56,5 +56,5 @@ import { Heroes } from './hero.service';
// #enddocregion animationdef
})
export class HeroListInlineStylesComponent {
@Input() heroes: Heroes;
@Input() heroes: Hero[];
}

View File

@ -12,7 +12,7 @@ import {
AnimationEvent
} from '@angular/animations';
import { Heroes } from './hero.service';
import { Hero } from './hero.service';
@Component({
selector: 'hero-list-multistep',
@ -59,7 +59,7 @@ import { Heroes } from './hero.service';
// #enddocregion animationdef
})
export class HeroListMultistepComponent {
@Input() heroes: Heroes;
@Input() heroes: Hero[];
animationStarted(event: AnimationEvent) {
console.warn('Animation started: ', event);

View File

@ -10,7 +10,7 @@ import {
transition
} from '@angular/animations';
import { Heroes } from './hero.service';
import { Hero } from './hero.service';
@Component({
selector: 'hero-list-timings',
@ -54,5 +54,5 @@ import { Heroes } from './hero.service';
// #enddocregion animationdef
})
export class HeroListTimingsComponent {
@Input() heroes: Heroes;
@Input() heroes: Hero[];
}

View File

@ -13,7 +13,7 @@ import {
} from '@angular/animations';
// #enddocregion imports
import { Heroes } from './hero.service';
import { Hero } from './hero.service';
@Component({
selector: 'hero-list-twoway',
@ -54,5 +54,5 @@ import { Heroes } from './hero.service';
// #enddocregion animationdef
})
export class HeroListTwowayComponent {
@Input() heroes: Heroes;
@Input() heroes: Hero[];
}

View File

@ -1,40 +1,41 @@
import { Component } from '@angular/core';
import { Heroes } from './hero.service';
import { Hero, HeroService } from './hero.service';
@Component({
selector: 'hero-team-builder',
template: `
<div class="buttons">
<button [disabled]="!heroes.canAdd()" (click)="heroes.addInactive()">Add inactive hero</button>
<button [disabled]="!heroes.canAdd()" (click)="heroes.addActive()">Add active hero</button>
<button [disabled]="!heroes.canRemove()" (click)="heroes.remove()">Remove hero</button>
<button [disabled]="!heroService.canAdd()" (click)="heroService.addInactive()">Add inactive hero</button>
<button [disabled]="!heroService.canAdd()" (click)="heroService.addActive()">Add active hero</button>
<button [disabled]="!heroService.canRemove()" (click)="heroService.remove()">Remove hero</button>
</div>
<div class="columns">
<div class="column">
<h4>Basic State</h4>
<p>Switch between active/inactive on click.</p>
<hero-list-basic [heroes]=heroes></hero-list-basic>
<hero-list-basic [heroes]="heroes"></hero-list-basic>
</div>
<div class="column">
<h4>Styles inline in transitions</h4>
<p>Animated effect on click, no persistend end styles.</p>
<hero-list-inline-styles [heroes]=heroes></hero-list-inline-styles>
<p>Animated effect on click, no persistent end styles.</p>
<hero-list-inline-styles [heroes]="heroes"></hero-list-inline-styles>
</div>
<div class="column">
<h4>Combined transition syntax</h4>
<p>Switch between active/inactive on click. Define just one transition used in both directions.</p>
<hero-list-combined-transitions [heroes]=heroes></hero-list-combined-transitions>
<hero-list-combined-transitions [heroes]="heroes"></hero-list-combined-transitions>
</div>
<div class="column">
<h4>Two-way transition syntax</h4>
<p>Switch between active/inactive on click. Define just one transition used in both directions using the <=> syntax.</p>
<hero-list-twoway [heroes]=heroes></hero-list-twoway>
<hero-list-twoway [heroes]="heroes"></hero-list-twoway>
</div>
<div class="column">
<h4>Enter & Leave</h4>
<p>Enter and leave animations using the void state.</p>
<hero-list-enter-leave [heroes]=heroes></hero-list-enter-leave>
<hero-list-enter-leave [heroes]="heroes"></hero-list-enter-leave>
</div>
</div>
<div class="columns">
@ -44,27 +45,27 @@ import { Heroes } from './hero.service';
Enter and leave animations combined with active/inactive state animations.
Different enter and leave transitions depending on state.
</p>
<hero-list-enter-leave-states [heroes]=heroes></hero-list-enter-leave-states>
<hero-list-enter-leave-states [heroes]="heroes"></hero-list-enter-leave-states>
</div>
<div class="column">
<h4>Auto Style Calc</h4>
<p>Leave animation from the current computed height using the auto-style value *.</p>
<hero-list-auto [heroes]=heroes></hero-list-auto>
<hero-list-auto [heroes]="heroes"></hero-list-auto>
</div>
<div class="column">
<h4>Different Timings</h4>
<p>Enter and leave animations with different easings, ease-in for enter, ease-out for leave.</p>
<hero-list-timings [heroes]=heroes></hero-list-timings>
<hero-list-timings [heroes]="heroes"></hero-list-timings>
</div>
<div class="column">
<h4>Multiple Keyframes</h4>
<p>Enter and leave animations with three keyframes in each, to give the transition some bounce.</p>
<hero-list-multistep [heroes]=heroes></hero-list-multistep>
<hero-list-multistep [heroes]="heroes"></hero-list-multistep>
</div>
<div class="column">
<h4>Parallel Groups</h4>
<p>Enter and leave animations with multiple properties animated in parallel with different timings.</p>
<hero-list-groups [heroes]=heroes></hero-list-groups>
<hero-list-groups [heroes]="heroes"></hero-list-groups>
</div>
</div>
`,
@ -87,8 +88,12 @@ import { Heroes } from './hero.service';
min-height: 6em;
}
`],
providers: [Heroes]
providers: [HeroService]
})
export class HeroTeamBuilderComponent {
constructor(private heroes: Heroes) { }
heroes: Hero[];
constructor(private heroService: HeroService) {
this.heroes = heroService.heroes;
}
}

View File

@ -1,16 +1,16 @@
import { Injectable } from '@angular/core';
class Hero {
constructor(public name: string,
public state = 'inactive') {
}
// #docregion hero
export class Hero {
constructor(public name: string, public state = 'inactive') { }
toggleState() {
this.state = (this.state === 'active' ? 'inactive' : 'active');
this.state = this.state === 'active' ? 'inactive' : 'active';
}
}
// #enddocregion hero
let ALL_HEROES = [
const ALL_HEROES = [
'Windstorm',
'RubberMan',
'Bombasto',
@ -25,36 +25,30 @@ let ALL_HEROES = [
].map(name => new Hero(name));
@Injectable()
export class Heroes implements Iterable<Hero> {
export class HeroService {
currentHeroes: Hero[] = [];
[Symbol.iterator]() {
return this.currentHeroes.values();
}
heroes: Hero[] = [];
canAdd() {
return this.currentHeroes.length < ALL_HEROES.length;
return this.heroes.length < ALL_HEROES.length;
}
canRemove() {
return this.currentHeroes.length > 0;
return this.heroes.length > 0;
}
addActive() {
let hero = ALL_HEROES[this.currentHeroes.length];
hero.state = 'active';
this.currentHeroes.push(hero);
addActive(active = true) {
let hero = ALL_HEROES[this.heroes.length];
hero.state = active ? 'active' : 'inactive';
this.heroes.push(hero);
}
addInactive() {
let hero = ALL_HEROES[this.currentHeroes.length];
hero.state = 'inactive';
this.currentHeroes.push(hero);
this.addActive(false);
}
remove() {
this.currentHeroes.splice(this.currentHeroes.length - 1, 1);
this.heroes.length -= 1;
}
}

View File

@ -12,8 +12,10 @@ describe('Component Style Tests', function () {
let componentH1 = element(by.css('hero-app > h1'));
let externalH1 = element(by.css('body > h1'));
expect(componentH1.getCssValue('fontWeight')).toEqual('normal');
expect(externalH1.getCssValue('fontWeight')).not.toEqual('normal');
// Note: sometimes webdriver returns the fontWeight as "normal",
// othertimes as "400", both of which are equal in CSS terms.
expect(componentH1.getCssValue('fontWeight')).toMatch(/normal|400/);
expect(externalH1.getCssValue('fontWeight')).not.toMatch(/normal|400/);
});

View File

@ -7,3 +7,4 @@
<p>We're sorry. The page you are looking for cannot be found.</p>
</div>
</div>
<aio-file-not-found-search></aio-file-not-found-search>

View File

@ -150,11 +150,6 @@ The following table lists some of the key AngularJS template features with their
</table>
[Back to top](guide/ajs-quick-reference#top)
## Template directives
AngularJS provides more than seventy built-in directives for templates.
Many of them aren't needed in Angular because of its more capable and expressive binding system.
@ -721,10 +716,6 @@ The following are some of the key AngularJS built-in directives and their equiva
</table>
[Back to top](guide/ajs-quick-reference#top)
{@a filters-pipes}
@ -992,9 +983,6 @@ For more information on pipes, see [Pipes](guide/pipes).
[Back to top](guide/ajs-quick-reference#top)
{@a controllers-components}
@ -1215,11 +1203,6 @@ The Angular code is shown using TypeScript.
</table>
[Back to top](guide/ajs-quick-reference#top)
{@a style-sheets}
@ -1297,6 +1280,3 @@ also encapsulate a style sheet within a specific component.
</table>
[Back to top](guide/ajs-quick-reference#top)

View File

@ -28,19 +28,37 @@ The examples in this page are available as a <live-example></live-example>.
</div>
## Setup
Before you can add animations to your application, you need
to import a few animation-specific modules and functions to the root application module.
<code-example path="animations/src/app/app.module.ts" region="animations-module" title="app.module.ts (animation module import excerpt)" linenums="false"></code-example>
#### Example basics
The animations examples in this guide animate a list of heroes.
A `Hero` class has a `name` property, a `state` property that indicates if the hero is active or not,
and a `toggleState()` method to switch between the states.
<code-example path="animations/src/app/hero.service.ts" region="hero" title="hero.service.ts (Hero class)" linenums="false"></code-example>
Across the top of the screen (`app.hero-team-builder.component.ts`)
are a series of buttons that add and remove heroes from the list (via the `HeroService`).
The buttons trigger changes to the list that all of the example components see at the same time.
{@a example-transitioning-between-states}
## Quickstart example: Transitioning between two states
## Transitioning between two states
<img src="generated/images/guide/animations/animation_basic_click.gif" alt="A simple transition animation" class="right">
You can build a simple animation that transitions an element between two states
driven by a model attribute.
Animations are defined inside `@Component` metadata. Before you can add animations, you need
to import a few animation-specific imports and functions:
<code-example path="animations/src/app/app.module.ts" region="animations-module" title="app.module.ts (@NgModule imports excerpt)" linenums="false"></code-example>
Animations can be defined inside `@Component` metadata.
<code-example path="animations/src/app/hero-list-basic.component.ts" region="imports" title="hero-list-basic.component.ts" linenums="false"></code-example>

View File

@ -2,13 +2,13 @@
This page presents design and layout guidelines for Angular documentation pages. These guidelines should be followed by all guide page authors. Deviations must be approved by the documentation editor.
Most guide pages should have [accompanying sample code](#from-code-samples) with
[special markup](#source-code-markup) for the code snippets on the page.
Code samples should adhere to the
[style guide for Angular applications](guide/styleguide "Application Code Style Guide")
Most guide pages should have [accompanying sample code](#from-code-samples) with
[special markup](#source-code-markup) for the code snippets on the page.
Code samples should adhere to the
[style guide for Angular applications](guide/styleguide "Application Code Style Guide")
because readers expect consistency.
For clarity and precision, every guideline on _this_ page is illustrated with a working example,
For clarity and precision, every guideline on _this_ page is illustrated with a working example,
followed by the page markup for that example ... as shown here.
```html
@ -19,13 +19,13 @@ followed by the page markup for that example ... as shown here.
To make changes to the documentation pages and sample code, clone the [Angular github repository](https://github.com/angular/angular "Angular repo") and go to the `aio/` folder.
The [aio/README.md](https://github.com/angular/angular/blob/master/aio/README.md "AIO ReadMe") explains how to install and use the tools to edit and test your changes.
The [aio/README.md](https://github.com/angular/angular/blob/master/aio/README.md "AIO ReadMe") explains how to install and use the tools to edit and test your changes.
Here are a few essential commands for guide page authors.
1. `yarn setup` &mdash; installs packages; builds docs, plunkers, and zips.
1. `yarn docs-watch -- --watch-only` &mdash; watches for saved content changes and refreshes the browser. The (optional) `--watch-only` flag skips the initial docs rebuild.
1. `yarn docs-watch --watch-only` &mdash; watches for saved content changes and refreshes the browser. The (optional) `--watch-only` flag skips the initial docs rebuild.
1. `yarn start` &mdash; starts the doc viewer application so you can see your local changes in the browser.
@ -33,9 +33,9 @@ Here are a few essential commands for guide page authors.
## Guide pages
All but a few guide pages are [markdown](https://daringfireball.net/projects/markdown/syntax "markdown") files with an `.md` extension.
All but a few guide pages are [markdown](https://daringfireball.net/projects/markdown/syntax "markdown") files with an `.md` extension.
Every guide page file is stored in the `content/guide` directory. Although the [side navigation](#navigation) panel displays as a hierarchy, the directory is flat with no sub-folders.
Every guide page file is stored in the `content/guide` directory. Although the [side navigation](#navigation) panel displays as a hierarchy, the directory is flat with no sub-folders.
The flat folder approach allows us to shuffle the apparent navigation structure without moving page files or redirecting old page URLs.
The doc generation process consumes the markdown files in the `content/guide` directory and produces JSON files in the `src/generated/docs/guide` directory, which is also flat. Those JSON files contain a combination of document metadata and HTML content.
@ -52,7 +52,7 @@ _Tutorial_ pages are exactly like guide pages. The only difference is that they
_API_ pages are generated from Angular source code into the `src/generated/docs/api` directory.
The doc viewer translates URLs that begin `api/` into requests for document JSON files in that directory. This style guide does not discuss creation or maintenance of API pages.
_Marketing_ pages are similar to guide pages. They're located in the `content/marketing` directory. While they can be markdown files, they may be static HTML pages or dynamic HTML pages that render with JSON data.
_Marketing_ pages are similar to guide pages. They're located in the `content/marketing` directory. While they can be markdown files, they may be static HTML pages or dynamic HTML pages that render with JSON data.
Only a few people are authorized to write marketing pages. This style guide does not discuss creation or maintenance of marketing pages.
@ -70,14 +70,14 @@ Standard markdown processors don't allow you to put markdown _within_ HTML tags.
<div class="alert is-critical">
**Always** follow every opening and closing HTML tag with _a blank line_.
**Always** follow every opening and closing HTML tag with _a blank line_.
</div>
```html
<div class="alert is-critical">
**Always** follow every opening and closing HTML tag with _a blank line_.
**Always** follow every opening and closing HTML tag with _a blank line_.
</div>
```
@ -111,7 +111,7 @@ Title text should be in "Title Case", which means that you use capital letters t
## Sections
A typical document is divided into sections.
A typical document is divided into sections.
All section heading text should be in "Sentence case", which means the first word is capitalized and all other words are lower case.
@ -122,7 +122,7 @@ Main section heading
</h2>
There are usually one or more main sections that may be further divided into secondary sections.
Begin a main section heading with the markdown `##` characters. Alternatively, you can write the equivalent `<h2>` HTML tag.
Begin a main section heading with the markdown `##` characters. Alternatively, you can write the equivalent `<h2>` HTML tag.
The main section heading should be followed by a blank line and then the content for that heading.
@ -164,7 +164,7 @@ Try to minimize the heading depth, preferably only two. But more headings, such
Subsections typically present extra detail and references to other pages.
Use subsections for commentary that _enriches_ the reader's understanding of the text that precedes it.
Use subsections for commentary that _enriches_ the reader's understanding of the text that precedes it.
A subsection _must not_ contain anything _essential_ to that understanding. Don't put a critical instruction or a tutorial step in a subsection.
@ -192,7 +192,7 @@ Note that at least one blank line must follow the opening `<div>`. A blank line
Most pages display a table of contents (TOC). The TOC appears in the right panel when the viewport is wide. When narrow, the TOC appears in an expandable/collapsible region near the top of the page.
You should not create your own TOC by hand. The TOC is generated automatically from the page's main and secondary section headers.
You should not create your own TOC by hand. The TOC is generated automatically from the page's main and secondary section headers.
To exclude a heading from the TOC, create the heading as an `<h2>` or `<h3>` element with a class called 'no-toc'. You can't do this with markdown.
@ -214,7 +214,7 @@ A guide without a TOC
The navigation links at the top, left, and bottom of the screen are generated from the JSON configuration file, `content/navigation.json`.
The authority to change the `navigation.json` file is limited to a few core team members.
The authority to change the `navigation.json` file is limited to a few core team members.
But for a new guide page, you should suggest a navigation title and position in the left-side navigation panel called the "side nav".
Look for the `SideNav` node in `navigation.json`. The `SideNav` node is an array of navigation nodes. Each node is either an _item_ node for a single document or a _header_ node with child nodes.
@ -302,7 +302,7 @@ For terminal input and output, put the content between `<code-example>` tags, se
Inline, hand-coded snippets like this one are _not_ testable and, therefore, are intrinsically unreliable.
This example belongs to the small set of pre-approved, inline snippets that includes
user input in a command shell or the _output_ of some process.
user input in a command shell or the _output_ of some process.
**Do not write inline code snippets** unless you have a good reason and the editor's permission to do so.
In all other cases, code snippets should be generated automatically from tested code samples.
@ -341,26 +341,43 @@ The following _code-example_ displays the sample's `app.module.ts`.
Here's the brief markup that produced that lengthy snippet:
```html
<code-example
path="docs-style-guide/src/app/app.module.ts"
<code-example
path="docs-style-guide/src/app/app.module.ts"
title="src/app/app.module.ts">
</code-example>
```
You identified the snippet's source file by setting the `path` attribute to sample folder's location _within_ `content/examples`.
In this example, that path is `docs-style-guide/src/app/app.module.ts`.
In this example, that path is `docs-style-guide/src/app/app.module.ts`.
You added a header to tell the reader where to find the file by setting the `title` attribute.
Following convention, you set the `title` attribute to the file's location within the sample's root folder.
<div class="l-sub-section">
Unless otherwise noted, all code snippets in this page are derived from sample source code
Unless otherwise noted, all code snippets in this page are derived from sample source code
located in the `content/examples/docs-style-guide` directory.
</div>
#### Code-example attributes
<div class="alert is-important">
The doc tooling reports an error if the file identified in the path does not exist **or is _git_-ignored**.
Most `.js` files are _git_-ignored.
If you want to include an ignored code file in your project and display it in a guide you must _un-ignore_ it.
The preferred way to un-ignore a file is to update the `content/examples/.gitignore` like this:
<code-example title="content/examples/.gitignore">
# my-guide
!my-guide/src/something.js
!my-guide/more-javascript*.js
</code-example>
</div>
#### Code-example attributes
You control the _code-example_ output by setting one or more of its attributes:
@ -384,8 +401,8 @@ You control the _code-example_ output by setting one or more of its attributes:
Often you want to focus on a fragment of code within a sample code file. In this example, you focus on the `AppModule` class and its `NgModule` metadata.
<code-example
path="docs-style-guide/src/app/app.module.ts"
<code-example
path="docs-style-guide/src/app/app.module.ts"
region="class">
</code-example>
@ -394,8 +411,8 @@ Then you reference that _docregion_ in the `region` attribute of the `<code-exam
```html
<code-example
path="docs-style-guide/src/app/app.module.ts"
<code-example
path="docs-style-guide/src/app/app.module.ts"
region="class">
</code-example>
```
@ -413,20 +430,20 @@ There's no need to repeat the header.
Sometimes you want to display an example of bad code or bad design.
You should be careful. Readers don't always read carefully and are likely to copy and paste your example of bad code in their own applications. So don't display bad code often.
You should be careful. Readers don't always read carefully and are likely to copy and paste your example of bad code in their own applications. So don't display bad code often.
When you do, set the `class` to `avoid`. The code snippet will be framed in bright red to grab the reader's attention.
Here's the markup for an "avoid" example in the
Here's the markup for an "avoid" example in the
[_Angular Style Guide_](guide/styleguide#style-05-03 "Style 05-03: components as elements").
```html
<code-example
path="styleguide/src/05-03/app/heroes/shared/hero-button/hero-button.component.avoid.ts"
region="example"
<code-example
path="styleguide/src/05-03/app/heroes/shared/hero-button/hero-button.component.avoid.ts"
region="example"
title="app/heroes/hero-button/hero-button.component.ts">
</code-example>
```
```
<code-example path="styleguide/src/05-03/app/heroes/shared/hero-button/hero-button.component.avoid.ts" region="example" title="app/heroes/hero-button/hero-button.component.ts">
</code-example>
@ -450,22 +467,22 @@ The next example displays multiple code tabs, each with its own title.
It demonstrates control over display of line numbers at both the `<code-tabs>` and `<code-pane>` levels.
<code-tabs linenums="false">
<code-pane
title="app.component.html"
<code-pane
title="app.component.html"
path="docs-style-guide/src/app/app.component.html">
</code-pane>
<code-pane
title="app.component.ts"
<code-pane
title="app.component.ts"
path="docs-style-guide/src/app/app.component.ts"
linenums="true">
</code-pane>
<code-pane
title="app.component.css (heroes)"
path="docs-style-guide/src/app/app.component.css"
<code-pane
title="app.component.css (heroes)"
path="docs-style-guide/src/app/app.component.css"
region="heroes">
</code-pane>
<code-pane
title="package.json (scripts)"
<code-pane
title="package.json (scripts)"
path="docs-style-guide/package.1.json">
</code-pane>
</code-tabs>
@ -477,22 +494,22 @@ The `linenums` attribute in the second pane restores line numbering for _itself
```html
<code-tabs linenums="false">
<code-pane
title="app.component.html"
<code-pane
title="app.component.html"
path="docs-style-guide/src/app/app.component.html">
</code-pane>
<code-pane
title="app.component.ts"
<code-pane
title="app.component.ts"
path="docs-style-guide/src/app/app.component.ts"
linenums="true">
</code-pane>
<code-pane
title="app.component.css (heroes)"
path="docs-style-guide/src/app/app.component.css"
<code-pane
title="app.component.css (heroes)"
path="docs-style-guide/src/app/app.component.css"
region="heroes">
</code-pane>
<code-pane
title="package.json (scripts)"
<code-pane
title="package.json (scripts)"
path="docs-style-guide/package.1.json">
</code-pane>
</code-tabs>
@ -555,8 +572,8 @@ Every line of code _after_ that comment belongs in the region _until_ the code f
The `src/main.ts` is a simple example of a file with a single _#docregion_ at the top of the file.
<code-example
path="docs-style-guide/src/main.ts"
<code-example
path="docs-style-guide/src/main.ts"
title="src/main.ts"></code-example>
</div>
@ -575,8 +592,8 @@ You distinguish among them by giving each fragment its own _#docregion name_ as
Remember to refer to this region by name in the `region` attribute of the `<code-example>` or `<code-pane>` as you did in an example above like this:
```html
<code-example
path="docs-style-guide/src/app/app.module.ts"
<code-example
path="docs-style-guide/src/app/app.module.ts"
region="class"></code-example>
```
@ -586,7 +603,7 @@ The _#docregion_ with no name is the _default region_. Do _not_ set the `region`
You can nest _#docregions_ within _#docregions_
```
// #docregion
// #docregion
... some code ...
// #docregion inner-region
... more code ...
@ -630,12 +647,12 @@ export class AppComponent {
Here's are the two corresponding code snippets displayed side-by-side.
<code-tabs>
<code-pane
title="app.component.ts (class)"
<code-pane
title="app.component.ts (class)"
path="docs-style-guide/src/app/app.component.ts"
region="class">
</code-pane>
<code-pane
<code-pane
title="app.component.ts (class-skeleton)"
path="docs-style-guide/src/app/app.component.ts"
region="class-skeleton">
@ -644,7 +661,7 @@ Here's are the two corresponding code snippets displayed side-by-side.
Some observations:
* The `#docplaster` at the top is another bit of code snippet markup. It tells the processor how to join the fragments into a single snippet.
* The `#docplaster` at the top is another bit of code snippet markup. It tells the processor how to join the fragments into a single snippet.
In this example, we tell the processor to put the fragments together without anything in between - without any "plaster". Most sample files define this _empty plaster_.
@ -656,7 +673,7 @@ Some observations:
Code snippet markup is not supported for JSON files because comments are forbidden in JSON files.
You can display an entire JSON file by referring to it in the `src` attribute.
You can display an entire JSON file by referring to it in the `src` attribute.
But you can't display JSON fragments because you can't add `#docregion` tags to the file.
If the JSON file is too big, you could copy the nodes-of-interest into markdown backticks.
@ -667,12 +684,12 @@ You can't test this partial file and you'll never use it in the application. But
Here's an example that excerpts certain scripts from `package.json` into a partial file named `package.1.json`.
<code-example
<code-example
path="docs-style-guide/package.1.json"
title="package.json (selected scripts)"></code-example>
```html
<code-example
<code-example
path="docs-style-guide/package.1.json"
title="package.json (selected scripts)"></code-example>
```
@ -696,7 +713,7 @@ You'll find many such files among the samples in the Angular documentation.
Remember to exclude these files from plunkers by listing them in the `plnkr.json` as illustrated here.
<code-example
<code-example
path="docs-style-guide/plnkr.json"
title="plnkr.json"></code-example>
@ -705,7 +722,7 @@ Remember to exclude these files from plunkers by listing them in the `plnkr.json
By adding `<live-example>` to the page you generate links that run sample code in the Plunker live coding environment and download that code to the reader's file system.
Live examples (AKA "plunkers") are defined by one or more `plnkr.json` files in the root of a code sample folder. Each sample folder usually has a single unnamed definition file, the default `plnkr.json`.
Live examples (AKA "plunkers") are defined by one or more `plnkr.json` files in the root of a code sample folder. Each sample folder usually has a single unnamed definition file, the default `plnkr.json`.
<div class="l-sub-section">
@ -728,7 +745,7 @@ Clicking the first link opens the code sample in a new browser tab in the "embed
You can change the appearance and behavior of the live example with attributes and classes.
<h3 class="no-toc">Custom label and tooltip</h3>
<h3 class="no-toc">Custom label and tooltip</h3>
Give the live example anchor a custom label and tooltip by setting the `title` attribute.
@ -746,7 +763,7 @@ You can achieve the same effect by putting the label between the `<live-example>
<live-example>Live example with content label</live-example>
```
<h3 class="no-toc">Live example from another guide</h3>
<h3 class="no-toc">Live example from another guide</h3>
To link to a plunker in a folder whose name is not the same as the current guide page, set the `name` attribute to the name of that folder.
@ -756,7 +773,7 @@ To link to a plunker in a folder whose name is not the same as the current guide
<live-example name="router">Live Example from the Router guide</live-example>
```
<h3 class="no-toc">Live Example for named plunker</h3>
<h3 class="no-toc">Live Example for named plunker</h3>
To link to a plunker defined by a named `plnkr.json` file, set the `plnkr` attribute. The following example links to the plunker defined by `second.plnkr.json` in the current guide's directory.
@ -766,7 +783,7 @@ To link to a plunker defined by a named `plnkr.json` file, set the `plnkr` attri
<live-example plnkr="second"></live-example>
```
<h3 class="no-toc">Live Example without download</h3>
<h3 class="no-toc">Live Example without download</h3>
To skip the download link, add the `noDownload` attribute.
@ -776,7 +793,7 @@ To skip the download link, add the `noDownload` attribute.
<live-example noDownload>Just the plunker</live-example>
```
<h3 class="no-toc">Live Example with download-only</h3>
<h3 class="no-toc">Live Example with download-only</h3>
To skip the live plunker link and only link to the download, add the `downloadOnly` attribute.
@ -786,9 +803,9 @@ To skip the live plunker link and only link to the download, add the `downloadOn
<live-example downloadOnly>Download only</live-example>
```
<h3 class="no-toc">Embedded live example</h3>
<h3 class="no-toc">Embedded live example</h3>
By default, a live example link opens a plunker in a separate browser tab.
By default, a live example link opens a plunker in a separate browser tab.
You can embed the plunker within the guide page itself by adding the `embedded` attribute.
For performance reasons, the plunker does not start right away. The reader sees an image instead. Clicking the image starts the sometimes-slow process of launching the embedded plunker within an iframe on the page.
@ -836,10 +853,10 @@ When navigating within the page, you can omit the page URL when specifying the l
It is often a good idea to *lock-in* a good anchor name.
Sometimes the section header text makes for an unattractive anchor. [This one](#ugly-long-section-header-anchors) is pretty bad.
Sometimes the section header text makes for an unattractive anchor. [This one](#ugly-long-section-header-anchors) is pretty bad.
```html
[This one](#ugly-long-section-header-anchors) is pretty bad.
[This one](#ugly-long-section-header-anchors) is pretty bad.
```
The greater danger is that **a future rewording of the header text would break** a link to this section.
@ -905,7 +922,7 @@ A helpful, informational alert.
```
Alerts are meant to grab the user's attention and should be used sparingly.
They are not for casual asides or commentary. Use [subsections](#subsections "subsections") for commentary.
They are not for casual asides or commentary. Use [subsections](#subsections "subsections") for commentary.
## Callouts
@ -1146,9 +1163,9 @@ src="generated/images/guide/docs-style-guide/flying-hero.png"
**Do not use the markdown image syntax, \!\[\.\.\.\]\(\.\.\.\).**
Images should be specified in an `<img>` tag.
Images should be specified in an `<img>` tag.
For accessibility, always set the `alt` attribute with a meaningful description of the image.
For accessibility, always set the `alt` attribute with a meaningful description of the image.
You should nest the `<img>` tag within a `<figure>` tag, which styles the image within a drop-shadow frame. You'll need the editor's permission to skip the `<figure>` tag.
@ -1160,7 +1177,7 @@ Here's a conforming example
```html
<figure>
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
alt="flying hero">
</figure>
```
@ -1180,13 +1197,13 @@ Here's the "flying hero" at a more reasonable scale.
```html
<figure>
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
alt="flying Angular hero"
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
alt="flying Angular hero"
width="200">
</figure>
```
Wide images can be a problem. Most browsers try to rescale the image but wide images may overflow the document in certain viewports.
Wide images can be a problem. Most browsers try to rescale the image but wide images may overflow the document in certain viewports.
**Do not set a width greater than 700px**. If you wish to display a larger image, provide a link to the actual image that the user can click on to see the full size image separately as in this example of `source-map-explorer` output from the "Ahead-of-time Compilation" guide:
@ -1205,11 +1222,11 @@ Consider using an image compression web site such as [tinypng](https://tinypng.c
You can float the image to the left or right of text by applying the class="left" or class="right" attributes respectively.
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
alt="flying Angular hero"
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
alt="flying Angular hero"
width="200"
class="left">
This text wraps around to the right of the floating "flying hero" image.
Headings and code-examples automatically clear a floating image. If you need to force a piece of text to clear a floating image, add `<br class="clear">` where the text should break.
@ -1219,11 +1236,11 @@ Headings and code-examples automatically clear a floating image. If you need to
The markup for the above example is:
```html
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
alt="flying Angular hero"
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
alt="flying Angular hero"
width="200"
class="left">
This text wraps around to the right of the floating "flying hero" image.
Headings and code-examples automatically clear a floating image. If you need to force a piece of text to clear a floating image, add `<br class="clear">` where the text should break.
@ -1239,8 +1256,8 @@ If you have a floating image inside an alert, callout, or a subsection, it is a
<div class="l-sub-section clear-fix">
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
alt="flying Angular hero"
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
alt="flying Angular hero"
width="100"
class="right">
@ -1251,12 +1268,12 @@ If you have a floating image inside an alert, callout, or a subsection, it is a
```html
<div class="l-sub-section clear-fix">
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
alt="flying Angular hero"
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
alt="flying Angular hero"
width="100"
class="right">
A subsection with **markdown** formatted text.
</div>
```
```

View File

@ -139,6 +139,10 @@ null if the control value is valid _or_ a validation error object.
The validation error object typically has a property whose name is the validation key, `'forbiddenName'`,
and whose value is an arbitrary dictionary of values that you could insert into an error message, `{name}`.
Custom async validators are similar to sync validators, but they must instead return a Promise or Observable
that later emits null or a validation error object. In the case of an Observable, the Observable must complete,
at which point the form uses the last value emitted for validation.
### Adding to reactive forms
In reactive forms, custom validators are fairly simple to add. All you have to do is pass the function directly

View File

@ -117,7 +117,7 @@ Now the extractor tool and compiler will generate a translation unit with _your
<code-example path="i18n/src/locale/messages.es.xlf.html" region="custom-id" linenums="false">
</code-example>
Here is the `i18n` attribute with a _definition_, followed by the custom `id`:
Here is the `i18n` attribute with a _description_, followed by the custom `id`:
<code-example path='i18n/src/app/app.component.1.html' region='i18n-attribute-id' title='app/app.component.html' linenums="false">
</code-example>

View File

@ -83,4 +83,3 @@ you'll have to provide a different `Title` service that understands
the concept of a "document title" for that specific platform.
Ideally, the application itself neither knows nor cares about the runtime environment.
[Back to top](guide/set-document-title#top)

View File

@ -316,10 +316,13 @@ which `NgFor` has initialized with the hero for the current iteration.
* The [API guide](api/common/NgFor "API: NgFor")
describes additional `NgFor` directive properties and context properties.
* `NgFor` is implemented by the `NgForOf` directive. Read more about additional `NgForOf` directive properties and context properties [NgForOf API reference](api/common/NgForOf).
These microsyntax mechanisms are available to you when you write your own structural directives.
Studying the
[source code for `NgIf`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_if.ts "Source: NgIf")
and [`NgFor`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts "Source: NgFor")
and [`NgForOf`](https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts "Source: NgForOf")
is a great way to learn more.

View File

@ -4502,8 +4502,8 @@ helps instantly identify which members of the component serve which purpose.
**Why?** The property associated with `@HostBinding` or the method associated with `@HostListener`
can be modified only in a single place&mdash;in the directive's class.
If you use the `host` metadata property, you must modify both the property declaration inside the controller,
and the metadata associated with the directive.
If you use the `host` metadata property, you must modify both the property/method declaration in the
directive's class and the metadata in the decorator associated with the directive.
</div>

View File

@ -37,7 +37,6 @@ You can extend the HTML vocabulary of your templates with components and directi
In the following sections, you'll learn how to get and set DOM (Document Object Model) values dynamically through data binding.
Begin with the first form of data binding&mdash;interpolation&mdash;to see how much richer template HTML can be.
<a href="#top-of-page">back to top</a>
<hr/>
@ -81,7 +80,7 @@ Though this is not exactly true. Interpolation is a special syntax that Angular
But first, let's take a closer look at template expressions and statements.
<a href="#top-of-page">back to top</a>
<hr/>
@ -143,11 +142,10 @@ The `hero` in `{{hero.name}}`
refers to the template input variable, not the component's property.
Template expressions cannot refer to anything in
the global namespace. They can't refer to `window` or `document`. They
the global namespace (except `undefined`). They can't refer to `window` or `document`. They
can't call `console.log` or `Math.max`. They are restricted to referencing
members of the expression context.
<a href="#top-of-page">back to top</a>
{@a no-side-effects}
@ -204,7 +202,7 @@ Dependent values should not change during a single turn of the event loop.
If an idempotent expression returns a string or a number, it returns the same string or number
when called twice in a row. If the expression returns an object (including an `array`),
it returns the same object *reference* when called twice in a row.
<a href="#top-of-page">back to top</a>
<hr/>
@ -276,7 +274,6 @@ A method call or simple property assignment should be the norm.
Now that you have a feel for template expressions and statements,
you're ready to learn about the varieties of data binding syntax beyond interpolation.
<a href="#top-of-page">back to top</a>
<hr/>
@ -585,7 +582,6 @@ The following table summarizes:
</table>
With this broad view in mind, you're ready to look at binding types in detail.
<a href="#top-of-page">back to top</a>
<hr/>
@ -773,7 +769,6 @@ content harmlessly.
<img src='generated/images/guide/template-syntax/evil-title.png' alt="evil title made safe">
</figure>
<a href="#top-of-page">back to top</a>
<hr/>
@ -847,7 +842,6 @@ is to set ARIA attributes, as in this example:
<code-example path="template-syntax/src/app/app.component.html" region="attrib-binding-aria" title="src/app/app.component.html" linenums="false">
</code-example>
<a href="#top-of-page">back to top</a>
<hr/>
@ -885,7 +879,6 @@ the [NgClass directive](guide/template-syntax#ngClass) is usually preferred when
</div>
<a href="#top-of-page">back to top</a>
<hr/>
@ -921,8 +914,6 @@ Note that a _style property_ name can be written in either
</div>
<a href="#top-of-page">back to top</a>
<hr/>
{@a event-binding}
@ -1053,7 +1044,6 @@ Deleting the hero updates the model, perhaps triggering other changes
including queries and saves to a remote server.
These changes percolate through the system and are ultimately displayed in this and other views.
<a href="#top-of-page">back to top</a>
<hr/>
@ -1118,7 +1108,6 @@ However, no native HTML element follows the `x` value and `xChange` event patter
Fortunately, the Angular [_NgModel_](guide/template-syntax#ngModel) directive is a bridge that enables two-way binding to form elements.
<a href="#top-of-page">back to top</a>
<hr/>
@ -1162,7 +1151,7 @@ This section is an introduction to the most commonly used attribute directives:
* [`NgClass`](guide/template-syntax#ngClass) - add and remove a set of CSS classes
* [`NgStyle`](guide/template-syntax#ngStyle) - add and remove a set of HTML styles
* [`NgModel`](guide/template-syntax#ngModel) - two-way data binding to an HTML form element
<a href="#top-of-page">back to top</a>
<hr/>
@ -1203,8 +1192,6 @@ It's up to you to call `setCurrentClassess()`, both initially and when the depen
</div>
<a href="#top-of-page">back to top</a>
<hr/>
{@a ngStyle}
@ -1241,7 +1228,6 @@ It's up to you to call `setCurrentStyles()`, both initially and when the depende
</div>
<a href="#top-of-page">back to top</a>
<hr/>
@ -1335,8 +1321,6 @@ Here are all variations in action, including the uppercase version:
<img src='generated/images/guide/template-syntax/ng-model-anim.gif' alt="NgModel variations">
</figure>
<a href="#top-of-page">back to top</a>
<hr/>
{@a structural-directives}
@ -1431,7 +1415,6 @@ described below.
</div>
<a href="#top-of-page">back to top</a>
<hr/>
@ -1511,8 +1494,8 @@ The next example captures the `index` in a variable named `i` and displays it wi
<div class="l-sub-section">
Learn about the other `NgFor` context values such as `last`, `even`,
and `odd` in the [NgFor API reference](api/common/NgFor).
`NgFor` is implemented by the `NgForOf` directive. Read more about the other `NgForOf` context values such as `last`, `even`,
and `odd` in the [NgForOf API reference](api/common/NgForOf).
</div>
@ -1553,7 +1536,6 @@ Here is an illustration of the _trackBy_ effect.
<img src="generated/images/guide/template-syntax/ng-for-track-by-anim.gif" alt="trackBy">
</figure>
<a href="#top-of-page">back to top</a>
<hr/>
@ -1601,8 +1583,6 @@ For example, you could replace the `<confused-hero>` switch case with the follow
<code-example path="template-syntax/src/app/app.component.html" region="NgSwitch-div" title="src/app/app.component.html" linenums="false">
</code-example>
<a href="#top-of-page">back to top</a>
<hr/>
{@a template-reference-variable}
@ -1673,7 +1653,6 @@ This example declares the `fax` variable as `ref-fax` instead of `#fax`.
<code-example path="template-syntax/src/app/app.component.html" region="ref-fax" title="src/app/app.component.html" linenums="false">
</code-example>
<a href="#top-of-page">back to top</a>
<hr/>
@ -1810,7 +1789,6 @@ the directive property name on the *left* and the public alias on the *right*:
</div>
<a href="#top-of-page">back to top</a>
<hr/>
@ -1861,7 +1839,6 @@ The generated output would look something like this
"rate": 325 }
</code-example>
<a href="#top-of-page">back to top</a>
<hr/>
@ -1935,7 +1912,6 @@ The display is blank, but the app keeps rolling without errors.
It works perfectly with long property paths such as `a?.b?.c?.d`.
<a href="#top-of-page">back to top</a>
<hr/>

View File

@ -18,7 +18,7 @@ The sample application and all tests in this guide are available as live example
* <live-example name="setup" plnkr="quickstart-specs" embedded-style>The QuickStart seed's AppComponent spec</live-example>.
* <live-example embedded-style>The sample application to be tested</live-example>.
* <live-example plnkr="app-specs" embedded-style>All specs that test the sample application</live-example>.
* <live-example plnkr="bag-specs" embedded-style>A grab bag of additional specs</live-example>.<a href="#top" class='to-top'>Back to top</a>
* <live-example plnkr="bag-specs" embedded-style>A grab bag of additional specs</live-example>.
<hr/>
@ -199,7 +199,6 @@ A comprehensive review of the Angular testing utilities appears [later in this g
But first you should write a dummy test to verify that your test environment is set up properly
and to lock in a few basic testing skills.
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -365,7 +364,6 @@ Debug specs in the browser in the same way that you debug an application.
You can also try this test as a <live-example plnkr="1st-specs" title="First spec" embedded-style></live-example> in plunker.
All of the tests in this guide are available as [live examples](guide/testing#live-examples "Live examples of these tests").
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -631,7 +629,7 @@ There is no harm in calling `detectChanges()` more often than is strictly necess
</div>
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -800,7 +798,7 @@ The tests in this guide only call `compileComponents` when necessary.
</div>
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -966,7 +964,6 @@ In a spec with multiple expectations, it can help clarify what went wrong and wh
The remaining tests confirm the logic of the component when the service returns different values.
The second test validates the effect of changing the user name.
The third test checks that the component displays the proper message when there is no logged-in user.
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -1202,7 +1199,7 @@ and `fakeAsync`, is a viable and occasionally necessary technique.
For example, you can't call `async` or `fakeAsync` when testing
code that involves the `intervalTimer`, as is common when
testing async `Observable` methods.
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -1482,7 +1479,7 @@ The tests themselves are almost identical to the stand-alone version:
Only the selected event test differs. It confirms that the selected `DashboardHeroComponent` hero
really does find its way up through the event binding to the host component.
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -1782,7 +1779,6 @@ Inspect and download _all_ of the guide's application test code with this <live-
</div>
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -1848,7 +1844,7 @@ Here are a few more `HeroDetailComponent` tests to drive the point home.
</code-example>
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -1945,8 +1941,6 @@ especially when the feature module is small and mostly self-contained, as featur
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -2104,7 +2098,6 @@ The `TestBed` offers similar `overrideDirective`, `overrideModule`, and `overrid
for digging into and replacing parts of these other classes.
Explore the options and combinations on your own.
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -2272,8 +2265,6 @@ tests with the `RouterTestingModule`.
</div>
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -2327,7 +2318,6 @@ such as misspelled or misused components and directives.
</div>
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -2427,7 +2417,7 @@ The test for the default color uses the injector of the second `<h2>` to get its
and its `defaultColor`.
* `DebugElement.properties` affords access to the artificial custom property that is set by the directive.
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -2655,7 +2645,6 @@ may require meticulous preparation with the Angular testing utilities.
On the other hand, isolated unit tests can't confirm that the `ButtonComp` is
properly bound to its template or even data bound at all.
Use Angular tests for that.
<a href="#top" class='to-top'>Back to top</a>
<hr/>
@ -3642,8 +3631,6 @@ The Angular `By` class has three static methods for common predicates:
</code-example>
<a href="#top" class='to-top'>Back to top</a>
<div class='l' class='hr'>
</div>
@ -3792,7 +3779,6 @@ The sample tests are written to run in Jasmine and karma.
The two "fast path" setups added the appropriate Jasmine and karma npm packages to the
`devDependencies` section of the `package.json`.
They're installed when you run `npm install`.
<a href="#top" class='to-top'>Back to top</a>
<div class='l' class='hr'>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -13,18 +13,6 @@
</tr>
</thead>
<tbody>
<!-- ngJapan -->
<tr>
<th><a href="http://ngjapan.org/" title="ng-Japan">ng-Japan</a></th>
<td>Tokyo, Japan</td>
<td>June 17, 2017</td>
</tr>
<!-- AngularMix -->
<tr>
<th><a href="https://angularmix.com/" title="AngularMix">AngularMix</a></th>
<td>Universal Studios, Orlando, Florida</td>
<td>October 8, 2017</td>
</tr>
<!-- ReactiveConf -->
<tr>
<th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th>
@ -35,7 +23,7 @@
<tr>
<th><a href="http://angularconnect.com" title="AngularConnect">AngularConnect</a></th>
<td>London, United Kingdom</td>
<td>November 07, 2017</td>
<td>November 7-8, 2017</td>
</tr>
<!-- ngAtlanta-->
<tr>
@ -43,6 +31,30 @@
<td>Atlanta, Georgia</td>
<td>January 30, 2018</td>
</tr>
<!-- ngVikings-->
<tr>
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
<td>Helsinki, Finland</td>
<td>March 1-2, 2018</td>
</tr>
<!-- ngconf 2018-->
<tr>
<th><a href="https://www.ng-conf.org/" title="ng-conf">ng-conf</a></th>
<td>Salt Lake City, UT</td>
<td>April 18-20, 2018</td>
</tr>
<!-- WeRDevs-->
<tr>
<th><a href="https://www.wearedevelopers.com/" title="WeAreDevs">WeAreDevelopers</a></th>
<td>Vienna</td>
<td>May 16-18, 2018</td>
</tr>
<!-- AngularConnect-->
<tr>
<th><a href="http://angularconnect.com" title="AngularConnect">AngularConnect</a></th>
<td>London, United Kingdom</td>
<td>November 5-7, 2018</td>
</tr>
</tbody>
</table>
</article>

View File

@ -30,10 +30,10 @@
<!--Announcement Bar-->
<div class="homepage-container">
<div class="announcement-bar">
<img src="generated/images/marketing/home/angular-mix.png" height="40" width="151">
<p>Join us at our newest event, October 2017</p>
<a class="button" href="https://angularmix.com/">Learn More</a>
</div>
<img src="generated/images/marketing/home/angular-connect.png">
<p>Join us in London for AngularConnect<br>November 7-8, 2017</p>
<a class="button" href="https://angularconnect.com/">Learn More</a>
</div>
</div>
<!-- Group 1-->

View File

@ -317,7 +317,7 @@
"desc": "Material Design components for Angular",
"logo": "",
"rev": true,
"title": "Angular Material 2",
"title": "Angular Material",
"url": "https://github.com/angular/material2"
},
"aggrid": {

View File

@ -87,7 +87,7 @@ Here's the app in action:
## Up next
## Next step
You'll build the Tour of Heroes app, step by step.
Each step is motivated with a requirement that you've likely

View File

@ -263,7 +263,7 @@ You can edit the hero's name and see the changes reflected immediately in the `<
## The road you've travelled
## Summary
Take stock of what you've built.
@ -286,7 +286,7 @@ Here's the complete `app.component.ts` as it stands now:
## The road ahead
## Next step
In the [next tutorial page](tutorial/toh-pt2 "Master/Detail"), you'll build on the Tour of Heroes app to display a list of heroes.
You'll also allow the user to select heroes and display their details.
You'll learn more about how to retrieve lists and bind them to the template.

View File

@ -450,7 +450,7 @@ Here's the complete `app.component.ts` as of now:
## The road you've travelled
## Summary
Here's what you achieved in this page:
@ -460,7 +460,7 @@ Here's what you achieved in this page:
Your app should look like this <live-example></live-example>.
## The road ahead
## Next step
You've expanded the Tour of Heroes app, but it's far from complete.
An app shouldn't be one monolithic component.
In the [next page](tutorial/toh-pt3 "Multiple Components"), you'll split the app into subcomponents and make them work together.

View File

@ -443,7 +443,7 @@ Here are the code files discussed in this page.
## The road youve travelled
## Summary
Here's what you achieved in this page:
@ -457,7 +457,7 @@ Your app should look like this <live-example></live-example>.
## The road ahead
## Next step
The Tour of Heroes app is more reusable with shared components,
but its (mock) data is still hard coded within the `AppComponent`.
That's not sustainable.

View File

@ -593,7 +593,7 @@ Here are the code files discussed in this page.
## The road you've travelled
## Summary
Here's what you achieved in this page:
* You created a service class that can be shared by many components.
@ -604,7 +604,7 @@ Here's what you achieved in this page:
Your app should look like this <live-example></live-example>.
## The road ahead
## Next step
The Tour of Heroes has become more reusable using shared components and services.
The next goal is to create a dashboard, add menu links that route between the views, and format data in a template.
As the app evolves, you'll discover how to design it to make it easier to grow and maintain.

View File

@ -1207,7 +1207,7 @@ Here's an excerpt:
Create the file <code>styles.css</code>.
Ensure that the file contains the [master styles provided here](https://raw.githubusercontent.com/angular/angular/master/aio/tools/examples/shared/boilerplate/src/styles.css).
Ensure that the file contains the [master styles provided here](https://raw.githubusercontent.com/angular/angular/master/aio/tools/examples/shared/boilerplate/common/src/styles.css).
Also edit <code>index.html</code> to refer to this stylesheet.
@ -1356,7 +1356,7 @@ Verify that you have the following structure:
## The road youve travelled
## Summary
Here's what you achieved in this page:
* You added the Angular router to navigate among different components.
@ -1368,7 +1368,7 @@ Here's what you achieved in this page:
Your app should look like this <live-example></live-example>.
### The road ahead
### Next step
You have much of the foundation you need to build an app.
You're still missing a key piece: remote data access.

View File

@ -518,7 +518,7 @@ Verify that you have the following structure:
</div>
</div>
## Home Stretch
## Summary
You're at the end of your journey, and you've accomplished a lot.
* You added the necessary dependencies to use HTTP in the app.

44
aio/e2e/api.e2e-spec.ts Normal file
View File

@ -0,0 +1,44 @@
import { ApiPage } from './api.po';
describe('Api pages', function() {
it('should show direct subclasses of a class', () => {
const page = new ApiPage('api/forms/AbstractControlDirective');
// We must use `as any` (here and below) because of broken typings for jasmine
expect(page.getDescendants('class', true)).toEqual(['ControlContainer', 'NgControl'] as any);
});
it('should show direct and indirect subclasses of a class', () => {
const page = new ApiPage('api/forms/AbstractControlDirective');
expect(page.getDescendants('class')).toEqual(['ControlContainer', 'AbstractFormGroupDirective', 'NgControl'] as any);
});
it('should show child interfaces that extend an interface', () => {
const page = new ApiPage('api/forms/Validator');
expect(page.getDescendants('interface')).toEqual(['AsyncValidator'] as any);
});
it('should show classes that implement an interface', () => {
const page = new ApiPage('api/animations/AnimationPlayer');
expect(page.getDescendants('class')).toEqual(['NoopAnimationPlayer', 'MockAnimationPlayer'] as any);
});
it('should show type params of type-aliases', () => {
const page = new ApiPage('api/common/http/HttpEvent');
expect(page.getOverview('type-alias').getText()).toContain('type HttpEvent<T>');
});
it('should show readonly properties as getters', () => {
const page = new ApiPage('api/common/http/HttpRequest');
expect(page.getOverview('class').getText()).toContain('get body: T|null');
});
it('should not show parenthesis for getters', () => {
const page = new ApiPage('api/core/NgModuleRef');
expect(page.getOverview('class').getText()).toContain('get injector: Injector');
});
it('should show both type and initializer if set', () => {
const page = new ApiPage('api/common/HashLocationStrategy');
expect(page.getOverview('class').getText()).toContain('path(includeHash: boolean = false): string');
});
});

34
aio/e2e/api.po.ts Normal file
View File

@ -0,0 +1,34 @@
import { element, by } from 'protractor';
import { SitePage } from './app.po';
export class ApiPage extends SitePage {
constructor(url: string) {
super();
this.navigateTo(url);
}
getDescendants(docType: string, onlyDirect = false) {
// This selector is horrible because we have potentially recursive HTML lists
//
// ul
// li
// code
// ul
// li
// code
// ul
// li
// code
// li
// code
//
// 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`;
return element.all(by.css(selector)).map<string>(item => item.getText());
}
getOverview(docType) {
return element(by.css(`.${docType}-overview`));
}
}

View File

@ -1,4 +1,4 @@
import { browser, element, by, promise } from 'protractor';
import { element, by } from 'protractor';
import { SitePage } from './app.po';
describe('site App', function() {
@ -41,20 +41,20 @@ describe('site App', function() {
describe('scrolling to the top', () => {
it('should scroll to the top when navigating to another page', () => {
page.navigateTo('guide/docs');
page.navigateTo('guide/security');
page.scrollToBottom();
page.getScrollTop().then(scrollTop => expect(scrollTop).toBeGreaterThan(0));
page.navigateTo('guide/api');
page.navigateTo('api');
page.getScrollTop().then(scrollTop => expect(scrollTop).toBe(0));
});
it('should scroll to the top when navigating to the same page', () => {
page.navigateTo('guide/docs');
page.navigateTo('guide/security');
page.scrollToBottom();
page.getScrollTop().then(scrollTop => expect(scrollTop).toBeGreaterThan(0));
page.navigateTo('guide/docs');
page.navigateTo('guide/security');
page.getScrollTop().then(scrollTop => expect(scrollTop).toBe(0));
});
});
@ -66,7 +66,9 @@ describe('site App', function() {
});
});
describe('google analytics', () => {
// TODO(https://github.com/angular/angular/issues/19785): Activate this again
// once it is no more flaky.
xdescribe('google analytics', () => {
beforeEach(done => page.gaReady.then(done));
it('should call ga', done => {
@ -100,4 +102,12 @@ describe('site App', function() {
expect(page.getSearchResults().map(link => link.getText())).toContain('ControlValueAccessor');
});
});
describe('404 page', () => {
it('should search the index for words found in the url', () => {
page.navigateTo('http/router');
expect(page.getSearchResults().map(link => link.getText())).toContain('Http');
expect(page.getSearchResults().map(link => link.getText())).toContain('Router');
});
});
});

View File

@ -30,8 +30,14 @@ module.exports = function (config) {
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
browsers: ['CustomChrome'],
browserNoActivityTimeout: 60000,
singleRun: false
singleRun: false,
customLaunchers: {
CustomChrome: {
base: 'Chrome',
flags: process.env.TRAVIS && ['--no-sandbox']
}
}
});
};

View File

@ -6,33 +6,45 @@
"author": "Angular",
"license": "MIT",
"scripts": {
"aio-use-local": "node tools/ng-packages-installer overwrite . --debug --ignore-packages @angular/service-worker",
"aio-use-npm": "node tools/ng-packages-installer restore .",
"aio-check-local": "node tools/ng-packages-installer check .",
"ng": "yarn check-env && ng",
"start": "yarn check-env && ng serve",
"prebuild": "yarn check-env && yarn setup",
"build": "ng build --target=production --environment=stable -sm --build-optimizer",
"postbuild": "yarn sw-manifest && yarn sw-copy",
"prebuild": "yarn setup",
"build": "yarn ~~build",
"prebuild-local": "yarn setup-local",
"build-local": "yarn ~~build",
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint",
"test": "yarn check-env && ng test",
"pree2e": "yarn check-env && yarn ~~update-webdriver",
"e2e": "ng e2e --no-webdriver-update",
"setup": "yarn && yarn build-ie-polyfills && yarn boilerplate:remove && yarn boilerplate:add && yarn generate-plunkers && yarn generate-zips && yarn docs",
"pretest-pwa-score-local": "yarn build",
"test-pwa-score-local": "concurrently --kill-others --success first \"http-server dist -p 4200 --silent\" \"yarn test-pwa-score -- http://localhost:4200 90\"",
"presetup": "yarn install --freeze-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-plunkers && yarn generate-zips && yarn docs",
"presetup-local": "yarn presetup",
"setup-local": "yarn aio-use-local && yarn example-use-local",
"postsetup-local": "yarn postsetup",
"pretest-pwa-score-localhost": "yarn build",
"test-pwa-score-localhost": "concurrently --kill-others --success first \"http-server dist -p 4200 --silent\" \"yarn test-pwa-score http://localhost:4200 90\"",
"test-pwa-score": "node scripts/test-pwa-score",
"example-e2e": "node ./tools/examples/run-example-e2e",
"example-e2e": "yarn example-check-local && node ./tools/examples/run-example-e2e",
"example-lint": "tslint -c \"content/examples/tslint.json\" \"content/examples/**/*.ts\" -e \"content/examples/styleguide/**/*.avoid.ts\"",
"example-use-local": "node tools/ng-packages-installer overwrite ./tools/examples/shared",
"example-use-npm": "node tools/ng-packages-installer restore ./tools/examples/shared",
"example-check-local": "node tools/ng-packages-installer check ./tools/examples/shared",
"deploy-preview": "scripts/deploy-preview.sh",
"deploy-production": "scripts/deploy-to-firebase.sh",
"check-env": "node scripts/check-environment",
"check-env": "yarn ~~check-env",
"postcheck-env": "yarn aio-check-local",
"payload-size": "scripts/payload.sh",
"predocs": "rimraf src/generated/{docs,*.json}",
"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",
"tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test && yarn boilerplate:test && jasmine tools/ng-packages-installer/index.spec.js",
"serve-and-sync": "concurrently --kill-others \"yarn docs-watch\" \"yarn start\"",
"~~update-webdriver": "webdriver-manager update --standalone false --gecko false",
"boilerplate:add": "node ./tools/examples/example-boilerplate add",
"boilerplate:remove": "node ./tools/examples/example-boilerplate remove",
"boilerplate:test": "node tools/examples/test.js",
@ -41,11 +53,15 @@
"sw-manifest": "ngu-sw-manifest --dist dist --in ngsw-manifest.json --out dist/ngsw-manifest.json",
"sw-copy": "cp node_modules/@angular/service-worker/bundles/worker-basic.min.js dist/",
"postinstall": "uglifyjs node_modules/lunr/lunr.js -c -m -o src/assets/js/lunr.min.js --source-map",
"build-ie-polyfills": "node node_modules/webpack/bin/webpack.js -p src/ie-polyfills.js src/generated/ie-polyfills.min.js"
"build-ie-polyfills": "node node_modules/webpack/bin/webpack.js -p src/ie-polyfills.js src/generated/ie-polyfills.min.js",
"~~check-env": "node scripts/check-environment",
"~~build": "ng build --target=production --environment=stable -sm --build-optimizer",
"post~~build": "yarn sw-manifest && yarn sw-copy",
"~~update-webdriver": "webdriver-manager update --standalone false --gecko false $CHROMEDRIVER_VERSION_ARG"
},
"engines": {
"node": ">=6.9.5 <7.0.0",
"yarn": ">=0.21.3 <1.0.0"
"yarn": ">=1.0.2 <2.0.0"
},
"private": true,
"dependencies": {
@ -66,7 +82,7 @@
"core-js": "^2.4.1",
"jasmine": "^2.6.0",
"ng-pwa-tools": "^0.0.10",
"rxjs": "^5.2.0",
"rxjs": "^5.4.3",
"tslib": "^1.7.1",
"web-animations-js": "^2.2.5",
"zone.js": "^0.8.16"
@ -78,11 +94,12 @@
"@types/node": "~6.0.60",
"archiver": "^1.3.0",
"canonical-path": "^0.0.2",
"chalk": "^2.1.0",
"codelyzer": "~2.0.0",
"concurrently": "^3.4.0",
"cross-spawn": "^5.1.0",
"dgeni": "^0.4.7",
"dgeni-packages": "^0.21.2",
"dgeni-packages": "0.22.0",
"entities": "^1.1.1",
"eslint": "^3.19.0",
"eslint-plugin-jasmine": "^2.2.0",
@ -117,7 +134,7 @@
"semver": "^5.3.0",
"shelljs": "^0.7.7",
"tree-kill": "^1.1.0",
"ts-node": "~2.0.0",
"ts-node": "^3.3.0",
"tslint": "~4.5.0",
"typescript": "2.3.2",
"uglify-js": "^3.0.15",

View File

@ -12,7 +12,8 @@ exports.config = {
browserName: 'chrome',
// For Travis
chromeOptions: {
binary: process.env.CHROME_BIN
binary: process.env.CHROME_BIN,
args: ['--no-sandbox']
}
},
directConnect: true,

View File

@ -3,7 +3,7 @@
set -u -e -o pipefail
declare -A limitUncompressed
limitUncompressed=(["inline"]=1600 ["main"]=525000 ["polyfills"]=38000)
limitUncompressed=(["inline"]=1600 ["main"]=525500 ["polyfills"]=38000)
declare -A limitGzip7
limitGzip7=(["inline"]=1000 ["main"]=127000 ["polyfills"]=12500)
declare -A limitGzip9

View File

@ -51,6 +51,6 @@ readonly relevantChangedFilesCount=$(git diff --name-only $TRAVIS_COMMIT_RANGE |
# Run PWA-score tests (unless the deployment is not public yet;
# i.e. it could not be automatically verified).
if [[ $httpCode -ne 202 ]] && [[ "$isHidden" != "true" ]]; then
yarn test-pwa-score -- "$DEPLOYED_URL" "$MIN_PWA_SCORE"
yarn test-pwa-score "$DEPLOYED_URL" "$MIN_PWA_SCORE"
fi
)

View File

@ -67,7 +67,7 @@ case $deployEnv in
readonly firebaseToken=$FIREBASE_TOKEN
;;
archive)
readonly projectId=angular-io-${majorVersion}
readonly projectId=v${majorVersion}-angular-io
readonly deployedUrl=https://v${majorVersion}.angular.io/
readonly firebaseToken=$FIREBASE_TOKEN
;;
@ -87,7 +87,7 @@ fi
cd "`dirname $0`/.."
# Build the app
yarn build -- --env=$deployEnv
yarn build --env=$deployEnv
# Include any mode-specific files
cp -rf src/extra-files/$deployEnv/. dist/
@ -100,5 +100,5 @@ fi
firebase deploy --message "Commit: $TRAVIS_COMMIT" --non-interactive --token "$firebaseToken"
# Run PWA-score tests
yarn test-pwa-score -- "$deployedUrl" "$MIN_PWA_SCORE"
yarn test-pwa-score "$deployedUrl" "$MIN_PWA_SCORE"
)

View File

@ -94,7 +94,7 @@ Deployment URL : https://angular.io/"
)
expected="Git branch : 2.4.x
Build/deploy mode : archive
Firebase project : angular-io-2
Firebase project : v2-angular-io
Deployment URL : https://v2.angular.io/"
check "$actual" "$expected"
)

View File

@ -61,7 +61,7 @@ else
# Nothing changed in aio/
exit 0
fi
message=$(echo $TRAVIS_COMMIT_MESSAGE | sed 's/"/\\"/g' | sed 's/\\/\\\\/g')
message=$(echo $TRAVIS_COMMIT_MESSAGE | sed 's/\\/\\\\/g' | sed 's/"/\\"/g')
payloadData="$payloadData\"change\": \"$change\", \"message\": \"$message\""
payloadData="{${payloadData}}"

View File

@ -17,8 +17,16 @@ const printer = require('lighthouse/lighthouse-cli/printer');
const config = require('lighthouse/lighthouse-core/config/default.js');
// Constants
const CHROME_LAUNCH_OPTS = {};
const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer/';
// Specify the path and flags for Chrome on Travis
if (process.env.TRAVIS) {
process.env.LIGHTHOUSE_CHROMIUM_PATH = process.env.CHROME_BIN;
CHROME_LAUNCH_OPTS.chromeFlags = ['--no-sandbox'];
}
// Run
_main(process.argv.slice(2));
@ -66,7 +74,7 @@ function ignoreHttpsAudits(config) {
}
function launchChromeAndRunLighthouse(url, flags, config) {
return chromeLauncher.launch().then(chrome => {
return chromeLauncher.launch(CHROME_LAUNCH_OPTS).then(chrome => {
flags.port = chrome.port;
return lighthouse(url, flags, config).
then(results => chrome.kill().then(() => results)).

View File

@ -13,7 +13,7 @@
<aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes"></aio-top-menu>
<aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box>
</md-toolbar>
<aio-search-results #searchResults *ngIf="showSearchResults" (resultSelected)="hideSearchResults()"></aio-search-results>
<aio-search-results #searchResultsView *ngIf="showSearchResults" [searchResults]="searchResults | async" (resultSelected)="hideSearchResults()"></aio-search-results>
<md-sidenav-container class="sidenav-container" [class.starting]="isStarting" [class.has-floating-toc]="hasFloatingToc" role="main">

View File

@ -1,12 +1,11 @@
import { NO_ERRORS_SCHEMA, DebugElement } from '@angular/core';
import { async, inject, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { inject, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { Title } from '@angular/platform-browser';
import { APP_BASE_HREF } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { MdProgressBar, MdSidenav } from '@angular/material';
import { By } from '@angular/platform-browser';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { of } from 'rxjs/observable/of';
import { AppComponent } from './app.component';
@ -22,9 +21,9 @@ import { MockSearchService } from 'testing/search.service';
import { NavigationNode } from 'app/navigation/navigation.service';
import { ScrollService } from 'app/shared/scroll.service';
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
import { SearchResultsComponent } from 'app/search/search-results/search-results.component';
import { SearchResultsComponent } from 'app/shared/search-results/search-results.component';
import { SearchService } from 'app/search/search.service';
import { SelectComponent, Option } from 'app/shared/select/select.component';
import { SelectComponent } from 'app/shared/select/select.component';
import { TocComponent } from 'app/embedded/toc/toc.component';
import { TocItem, TocService } from 'app/shared/toc.service';
@ -1054,11 +1053,6 @@ class TestGaService {
locationChanged = jasmine.createSpy('locationChanged');
}
class TestSearchService {
initWorker = jasmine.createSpy('initWorker');
loadIndex = jasmine.createSpy('loadIndex');
}
class TestHttpClient {
static versionInfo = {

View File

@ -2,18 +2,18 @@ import { Component, ElementRef, HostBinding, HostListener, OnInit,
QueryList, ViewChild, ViewChildren } from '@angular/core';
import { MdSidenav } from '@angular/material';
import { CurrentNodes, NavigationService, NavigationViews, NavigationNode, VersionInfo } from 'app/navigation/navigation.service';
import { CurrentNodes, NavigationService, NavigationNode, VersionInfo } from 'app/navigation/navigation.service';
import { DocumentService, DocumentContents } from 'app/documents/document.service';
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
import { Deployment } from 'app/shared/deployment.service';
import { LocationService } from 'app/shared/location.service';
import { NavMenuComponent } from 'app/layout/nav-menu/nav-menu.component';
import { ScrollService } from 'app/shared/scroll.service';
import { SearchResultsComponent } from 'app/search/search-results/search-results.component';
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
import { SearchResults } from 'app/search/interfaces';
import { SearchService } from 'app/search/search.service';
import { TocService } from 'app/shared/toc.service';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { combineLatest } from 'rxjs/observable/combineLatest';
@ -89,10 +89,9 @@ export class AppComponent implements OnInit {
// Search related properties
showSearchResults = false;
@ViewChildren('searchBox, searchResults', { read: ElementRef })
searchResults: Observable<SearchResults>;
@ViewChildren('searchBox, searchResultsView', { read: ElementRef })
searchElements: QueryList<ElementRef>;
@ViewChild(SearchResultsComponent)
searchResults: SearchResultsComponent;
@ViewChild(SearchBoxComponent)
searchBox: SearchBoxComponent;
@ -332,7 +331,7 @@ export class AppComponent implements OnInit {
}
doSearch(query) {
this.searchService.search(query);
this.searchResults = this.searchService.search(query);
this.showSearchResults = !!query;
}

View File

@ -43,7 +43,6 @@ import { NavMenuComponent } from 'app/layout/nav-menu/nav-menu.component';
import { NavItemComponent } from 'app/layout/nav-item/nav-item.component';
import { ScrollService } from 'app/shared/scroll.service';
import { ScrollSpyService } from 'app/shared/scroll-spy.service';
import { SearchResultsComponent } from './search/search-results/search-results.component';
import { SearchBoxComponent } from './search/search-box/search-box.component';
import { TocService } from 'app/shared/toc.service';
@ -95,7 +94,6 @@ export const svgIconProviders = [
ModeBannerComponent,
NavMenuComponent,
NavItemComponent,
SearchResultsComponent,
SearchBoxComponent,
TopMenuComponent,
],

View File

@ -1,10 +1,7 @@
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { Injector } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { LocationService } from 'app/shared/location.service';
import { MockLocationService } from 'testing/location.service';

View File

@ -1,4 +1,4 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { ApiListComponent } from './api-list.component';

View File

@ -13,7 +13,7 @@ import { ReplaySubject } from 'rxjs/ReplaySubject';
import { combineLatest } from 'rxjs/observable/combineLatest';
import { LocationService } from 'app/shared/location.service';
import { ApiItem, ApiSection, ApiService } from './api.service';
import { ApiSection, ApiService } from './api.service';
import { Option } from 'app/shared/select/select.component';

View File

@ -1,6 +1,6 @@
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { Injector } from '@angular/core';
import { TestBed, inject } from '@angular/core/testing';
import { TestBed } from '@angular/core/testing';
import { Logger } from 'app/shared/logger.service';

View File

@ -1,11 +1,9 @@
import { Component, ElementRef, ViewChild, OnChanges, OnDestroy, Input } from '@angular/core';
import { Component, ElementRef, ViewChild, OnChanges, Input } from '@angular/core';
import { Logger } from 'app/shared/logger.service';
import { PrettyPrinter } from './pretty-printer.service';
import { CopierService } from 'app/shared/copier.service';
import { MdSnackBar } from '@angular/material';
const originalLabel = 'Copy Code';
const copiedLabel = 'Copied!';
const defaultLineNumsCount = 10; // by default, show linenums over this number
/**

View File

@ -3,7 +3,7 @@ import { Injector } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { ContributorService } from './contributor.service';
import { Contributor, ContributorGroup } from './contributors.model';
import { ContributorGroup } from './contributors.model';
describe('ContributorService', () => {

View File

@ -1,4 +1,4 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LocationService } from 'app/shared/location.service';
import { MockLocationService } from 'testing/location.service';
import { CurrentLocationComponent } from './current-location.component';

View File

@ -20,6 +20,7 @@ import { CodeTabsComponent } from './code/code-tabs.component';
import { ContributorListComponent } from './contributor/contributor-list.component';
import { ContributorComponent } from './contributor/contributor.component';
import { CurrentLocationComponent } from './current-location.component';
import { FileNotFoundSearchComponent } from './search/file-not-found-search.component';
import { LiveExampleComponent, EmbeddedPlunkerComponent } from './live-example/live-example.component';
import { ResourceListComponent } from './resource/resource-list.component';
import { ResourceService } from './resource/resource.service';
@ -30,7 +31,8 @@ import { TocComponent } from './toc/toc.component';
*/
export const embeddedComponents: any[] = [
ApiListComponent, CodeExampleComponent, CodeTabsComponent, ContributorListComponent,
CurrentLocationComponent, LiveExampleComponent, ResourceListComponent, TocComponent
CurrentLocationComponent, FileNotFoundSearchComponent, LiveExampleComponent, ResourceListComponent,
TocComponent
];
/** Injectable class w/ property returning components that can be embedded in docs */

View File

@ -1,6 +1,6 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { Component, DebugElement, ElementRef } from '@angular/core';
import { Component, DebugElement } from '@angular/core';
import { Location } from '@angular/common';
import { LiveExampleComponent, EmbeddedPlunkerComponent } from './live-example.component';
@ -71,7 +71,6 @@ describe('LiveExampleComponent', () => {
describe('when not embedded', () => {
function getLiveExampleAnchor() { return getAnchors()[0]; }
function getDownloadAnchor() { return getAnchors()[1]; }
it('should create LiveExampleComponent', () => {
testComponent(() => {

View File

@ -1,7 +1,6 @@
import { ReflectiveInjector } from '@angular/core';
import { PlatformLocation } from '@angular/common';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { ResourceListComponent } from './resource-list.component';

View File

@ -3,7 +3,7 @@ import { Injector } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { ResourceService } from './resource.service';
import { Category, SubCategory, Resource } from './resource.model';
import { Category } from './resource.model';
describe('ResourceService', () => {

View File

@ -0,0 +1,49 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { Subject } from 'rxjs/Subject';
import { LocationService } from 'app/shared/location.service';
import { MockLocationService } from 'testing/location.service';
import { SearchResults } from 'app/search/interfaces';
import { SearchResultsComponent } from 'app/shared/search-results/search-results.component';
import { SearchService } from 'app/search/search.service';
import { FileNotFoundSearchComponent } from './file-not-found-search.component';
describe('FileNotFoundSearchComponent', () => {
let element: HTMLElement;
let fixture: ComponentFixture<FileNotFoundSearchComponent>;
let searchService: SearchService;
let searchResultSubject: Subject<SearchResults>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ FileNotFoundSearchComponent, SearchResultsComponent ],
providers: [
{ provide: LocationService, useValue: new MockLocationService('base/initial-url?some-query') },
SearchService
]
});
fixture = TestBed.createComponent(FileNotFoundSearchComponent);
searchService = TestBed.get(SearchService);
searchResultSubject = new Subject<SearchResults>();
spyOn(searchService, 'search').and.callFake(() => searchResultSubject.asObservable());
fixture.detectChanges();
element = fixture.nativeElement;
});
it('should run a search with a query built from the current url', () => {
expect(searchService.search).toHaveBeenCalledWith('base initial url');
});
it('should pass through any results to the `aio-search-results` component', () => {
const searchResultsComponent = fixture.debugElement.query(By.directive(SearchResultsComponent)).componentInstance;
expect(searchResultsComponent.searchResults).toBe(null);
const results = { query: 'base initial url', results: []};
searchResultSubject.next(results);
fixture.detectChanges();
expect(searchResultsComponent.searchResults).toEqual(results);
});
});

View File

@ -0,0 +1,23 @@
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { LocationService } from 'app/shared/location.service';
import { SearchResults } from 'app/search/interfaces';
import { SearchService } from 'app/search/search.service';
@Component({
selector: 'aio-file-not-found-search',
template:
`<p>Let's see if any of these search results help...</p>
<aio-search-results class="embedded" [searchResults]="searchResults | async"></aio-search-results>`
})
export class FileNotFoundSearchComponent implements OnInit {
searchResults: Observable<SearchResults>;
constructor(private location: LocationService, private search: SearchService) {}
ngOnInit() {
this.searchResults = this.location.currentPath.switchMap(path => {
const query = path.split(/\W+/).join(' ');
return this.search.search(query);
});
}
}

View File

@ -1,6 +1,6 @@
import { Component, CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By, DOCUMENT } from '@angular/platform-browser';
import { By } from '@angular/platform-browser';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { asap } from 'rxjs/scheduler/asap';

View File

@ -1,11 +1,8 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import {
Component, ComponentFactoryResolver, DebugElement,
ElementRef, Injector, NgModule, OnInit, ViewChild } from '@angular/core';
import { By } from '@angular/platform-browser';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Component, DebugElement, ElementRef, NgModule, OnInit, ViewChild } from '@angular/core';
import { DocViewerComponent } from './doc-viewer.component';
import { DocumentContents } from 'app/documents/document.service';
import { EmbeddedModule, embeddedComponents, EmbeddedComponents } from 'app/embedded/embedded.module';
import { EmbeddedModule, EmbeddedComponents } from 'app/embedded/embedded.module';
import { Title } from '@angular/platform-browser';
import { TocService } from 'app/shared/toc.service';

View File

@ -1,7 +1,7 @@
import {
Component, ComponentFactory, ComponentFactoryResolver, ComponentRef,
DoCheck, ElementRef, EventEmitter, Injector, Input, OnDestroy,
Output, ViewEncapsulation
Output
} from '@angular/core';
import { EmbeddedComponents } from 'app/embedded/embedded.module';

View File

@ -1,6 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { SimpleChange, SimpleChanges, NO_ERRORS_SCHEMA } from '@angular/core';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { NavItemComponent } from './nav-item.component';
import { NavigationNode } from 'app/navigation/navigation.model';

View File

@ -1,9 +1,9 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { TopMenuComponent } from './top-menu.component';
import { NavigationService, NavigationViews, NavigationNode } from 'app/navigation/navigation.service';
import { NavigationService, NavigationViews } from 'app/navigation/navigation.service';
describe('TopMenuComponent', () => {
let component: TopMenuComponent;

View File

@ -2,7 +2,6 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { AsyncSubject } from 'rxjs/AsyncSubject';
import { combineLatest } from 'rxjs/observable/combineLatest';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/publishLast';

View File

@ -0,0 +1,19 @@
export interface SearchResults {
query: string;
results: SearchResult[];
}
export interface SearchResult {
path: string;
title: string;
type: string;
titleWords: string;
keywords: string;
}
export interface SearchArea {
name: string;
pages: SearchResult[];
priorityPages: SearchResult[];
}

View File

@ -2,7 +2,6 @@ import { Component } from '@angular/core';
import { ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { SearchBoxComponent } from './search-box.component';
import { MockSearchService } from 'testing/search.service';
import { LocationService } from 'app/shared/location.service';
import { MockLocationService } from 'testing/location.service';

View File

@ -1,6 +1,7 @@
import { ReflectiveInjector, NgZone } from '@angular/core';
import { fakeAsync, tick } from '@angular/core/testing';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { SearchService } from './search.service';
import { WebWorkerClient } from 'app/shared/web-worker';
@ -36,27 +37,29 @@ describe('SearchService', () => {
describe('search', () => {
beforeEach(() => {
// We must initialize the service before calling search
service.initWorker('some/url', 100);
// We must initialize the service before calling connectSearches
service.initWorker('some/url', 1000);
// Simulate the index being ready so that searches get sent to the worker
(service as any).ready = Observable.of(true);
});
it('should trigger a `loadIndex` synchronously', () => {
service.search('some query');
it('should trigger a `loadIndex` synchronously (not waiting for the delay)', () => {
expect(mockWorker.sendMessage).not.toHaveBeenCalled();
service.search('some query').subscribe();
expect(mockWorker.sendMessage).toHaveBeenCalledWith('load-index');
});
it('should send a "query-index" message to the worker', () => {
service.search('some query');
service.search('some query').subscribe();
expect(mockWorker.sendMessage).toHaveBeenCalledWith('query-index', 'some query');
});
it('should push the response to the `searchResults` observable', () => {
it('should push the response to the returned observable', () => {
const mockSearchResults = { results: ['a', 'b'] };
let actualSearchResults;
(mockWorker.sendMessage as jasmine.Spy).and.returnValue(Observable.of(mockSearchResults));
let searchResults: any;
service.searchResults.subscribe(results => searchResults = results);
service.search('some query');
expect(searchResults).toEqual(mockSearchResults);
service.search('some query').subscribe(results => actualSearchResults = results);
expect(actualSearchResults).toEqual(mockSearchResults);
});
});
});

View File

@ -4,34 +4,21 @@ Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { NgZone, Injectable, Type } from '@angular/core';
import { NgZone, Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { ReplaySubject } from 'rxjs/ReplaySubject';
import 'rxjs/add/observable/race';
import 'rxjs/add/observable/timer';
import 'rxjs/add/operator/concatMap';
import 'rxjs/add/operator/publish';
import 'rxjs/add/operator/publishReplay';
import { WebWorkerClient } from 'app/shared/web-worker';
export interface SearchResults {
query: string;
results: SearchResult[];
}
export interface SearchResult {
path: string;
title: string;
type: string;
titleWords: string;
keywords: string;
}
import { SearchResults } from 'app/search/interfaces';
@Injectable()
export class SearchService {
private ready: Observable<boolean>;
private searchesSubject = new ReplaySubject<string>(1);
searchResults: Observable<SearchResults>;
private worker: WebWorkerClient;
constructor(private zone: NgZone) {}
/**
@ -43,36 +30,32 @@ export class SearchService {
* @param initDelay the number of milliseconds to wait before we load the WebWorker and generate the search index
*/
initWorker(workerUrl: string, initDelay: number) {
const searchResults = Observable
const ready = this.ready = Observable
// Wait for the initDelay or the first search
.race(
.race<any>(
Observable.timer(initDelay),
this.searchesSubject.first()
(this.searchesSubject as Observable<string>).first()
)
.concatMap(() => {
// Create the worker and load the index
const worker = WebWorkerClient.create(workerUrl, this.zone);
return worker.sendMessage('load-index').concatMap(() =>
// Once the index has loaded, switch to listening to the searches coming in
this.searchesSubject.switchMap((query) =>
// Each search gets switched to a web worker message, whose results are returned via an observable
worker.sendMessage<SearchResults>('query-index', query)
)
);
}).publish();
this.worker = WebWorkerClient.create(workerUrl, this.zone);
return this.worker.sendMessage<boolean>('load-index');
}).publishReplay(1);
// Connect to the observable to kick off the timer
searchResults.connect();
// Expose the connected observable to the rest of the world
this.searchResults = searchResults;
// Connect to the observable to kick off the timer
ready.connect();
return ready;
}
/**
* Send a search query to the index.
* The results will appear on the `searchResults` observable.
* Search the index using the given query and emit results on the observable that is returned.
* @param query The query to run against the index.
* @returns an observable collection of search results
*/
search(query: string) {
search(query: string): Observable<SearchResults> {
// Trigger the searches subject to override the init delay timer
this.searchesSubject.next(query);
// Once the index has loaded, switch to listening to the searches coming in.
return this.ready.concatMap(() => this.worker.sendMessage<SearchResults>('query-index', query));
}
}

Some files were not shown because too many files have changed in this diff Show More