Compare commits

..

468 Commits
5.0.4 ... 5.2.0

Author SHA1 Message Date
04dd9713f0 docs: add changelog for 5.2.0 2018-01-09 17:00:17 -08:00
48d579b2ae release: cut the 5.2.0 release 2018-01-09 16:55:58 -08:00
3db91ffd96 feat(core): add ngAfterViewInit and ngAfterViewChecked support to render3 (#21266)
PR Close #21266
2018-01-09 14:18:17 -08:00
229b76cfde ci: temporary work-around for Travis issue (#21422)
See
https://github.com/travis-ci/travis-ci/issues/8836#issuecomment-356362524
for more info.

PR Close #21422
2018-01-09 12:25:18 -08:00
80a5b91575 build(aio): upgrade to zone.js@0.8.19 (#21349)
This causes a 3.4kb size regressions for polyfills.js. :-(

I filed an issue for this: https://github.com/angular/zone.js/issues/989

-rw-r--r--  1 iminar  eng   73998 Jan  5 17:51 dist/0.5fb611ef423970fd3ba1.chunk.js
-rw-r--r--  1 iminar  eng   14880 Jan  5 17:51 dist/4.c719ac5645940382cdce.chunk.js
-rw-r--r--  1 iminar  eng    1558 Jan  5 17:51 dist/inline.797233300016416206fc.bundle.js
-rw-r--r--  1 iminar  eng  457592 Jan  5 17:51 dist/main.5870135237d5187f1ab6.bundle.js
-rw-r--r--  1 iminar  eng   40684 Jan  5 17:51 dist/polyfills.88f0257676f76560da16.bundle.js
-rw-r--r--  1 iminar  eng   54001 Jan  5 17:51 dist/worker-basic.min.js

PR Close #21349
2018-01-09 10:33:50 -08:00
9728dce048 fix(bazel): Give correct module names for ES6 output (#21320)
Fixes #21022

PR Close #21320
2018-01-09 10:30:44 -08:00
463e2872a6 build: upgrade Bazel to 0.9.0 (#21335)
Also install the skylark linter for .bzl files.

PR Close #21335
2018-01-09 10:28:46 -08:00
fa03ae14b0 fix(benchpress): work around missing events from Chrome 63 (#21396)
Chrome 63 can cause the navigationStart event for the first
run to arrive with a different pid than the start of the
benchpress run. This makes the first collected result invalid.

This workaround causes the sampler to ignore runs that have this
condition.

PR Close #21396
2018-01-08 16:14:34 -08:00
27196b676b docs(aio): fix punctuation for clarity (#21325)
PR Close #21325
2018-01-08 16:12:24 -08:00
6040ee39eb fix(benchpress): forward compat with selenium_webdriver 3.6.0 (#21399)
This was a local mod in google3 introduced by cl 174212464

PR Close #21399
2018-01-08 13:09:26 -08:00
74dbf7bad5 build(core): reduce payload limit back to normal (#21394)
PR Close #21394
2018-01-08 12:02:30 -08:00
7269fe5203 ci: reformat itegration/_payload-limits.json to be easier to read (#21344)
PR Close #21344
2018-01-08 11:12:54 -08:00
8e80db4982 build: upgrade to @angular/cli@1.6.3 (#21344)
PR Close #21344
2018-01-08 11:12:54 -08:00
e99475260f docs(aio): document the template compiler options (#21333)
PR Close #21333
2018-01-08 13:23:16 -05:00
b15d50af9b build(core): improve payload size failure message (#21346)
PR Close #21346
2018-01-08 13:13:52 -05:00
db55e86e91 fix(core): make all render3 tests pass in IE9 (#21279)
PR Close #21279
2018-01-05 14:25:40 -08:00
a2f3f4550d test(core): properly stringify HTML elements in render3 tests (#21279)
PR Close #21279
2018-01-05 14:25:40 -08:00
d2cfc6a719 build: activate render3 tests in CI (#21279)
PR Close #21279
2018-01-05 14:25:40 -08:00
fecf768f43 fix(common): export currencies via getCurrencySymbol (#20983)
PR Close #20983
2018-01-05 14:24:26 -08:00
2fc4cf67be build(core): update payload size of hello world (#21340)
PR Close #21340
2018-01-05 14:23:28 -08:00
f9436f7a5b docs(aio): delete duplicated word (#21129)
PR Close #21129
2018-01-05 16:22:28 -05:00
e726d81822 build: force fetch PR in merge script (#21295)
Use `-f` when doing `git fetch` for the PR. Without 
it the `git fetch` will not overwrite what is currently 
fetched locally, in essence doing fast-forward only.
PR Close #21295
2018-01-05 15:01:55 -05:00
6cd4b1b41c build(aio): add @nodoc alias to the @internal tag (#21024)
The `@internal` tag prevents code items from appearing in the docs and the
typings files. You can now use `@nodoc` if you only want it to be excluded
from the docs and not the typings files.

Closes #20990

PR Close #21024
2018-01-05 14:59:59 -05:00
24df08efc7 build(aio): fix plunker for custom plunkers (#21291)
PR Close #21291
2018-01-05 14:55:19 -05:00
4f1a571ba6 docs: remove broken issuestats.com badges (#21334)
PR Close #21334
2018-01-05 14:43:18 -05:00
c8d2e0a3bc test(aio): wait for Angular to avoid flake in e2e test (#21047)
PR Close #21047
2018-01-05 14:08:01 -05:00
b5934fc582 docs(router): fix missing export in docs for UrlMatcher (#21095)
Fixes #21042

PR Close #21095
2018-01-04 17:48:32 -05:00
2402224b4e docs(common): update copyright years (#21232)
PR Close #21232
2018-01-04 17:45:36 -05:00
03d16fcd24 docs(aio): add missing imports to interceptor example (#21259)
PR Close #21259
2018-01-04 17:40:34 -05:00
dcc3eba962 fix(aio): fix test message's mistake. (#21254)
duplicated message for 1st link. /heroes is 2nd link.

PR Close #21254
2018-01-04 17:38:25 -05:00
6588ca8a6c fix(aio): do not redirect pages on "next" deployment (#21027)
We redirect non-docs pages in the "archive" deployment back to the stable
deployment. We should not redirect pages in the "next" deployment.

Closes #19505

PR Close #21027
2018-01-04 17:13:52 -05:00
81823e89cd build(aio): fix example protractor config for Travis (#21309)
Without setting the CHROME_BIN Travis will not use the correct version
of Chrome for running e2e tests.

Closes #20159

PR Close #21309
2018-01-04 17:09:56 -05:00
ef78538a06 fix(aio): don't use Object.keys on NamedNodeMap to prevent SEO errors (#21305)
Apparently Object.keys on NamedNodeMap work differently with googlebot :-(

There are not tests since we don't have a way to write tests for googlebot,
but I did manually verify that after this fix googlebot correctly renders
several of the previously broken pages.

Fixes #21272

PR Close #21305
2018-01-04 14:25:09 -05:00
ccea37256e refactor(aio): use one argument for DocViewer error reporting (#21293)
Pass one argument to `logger.error()` to improve error reporting in
environments that do not handle more than one arguments well (e.g.
Googlebot's web rendering service).

Related to #21272.

PR Close #21293
2018-01-04 13:32:50 -05:00
cdc66f6164 docs: add changelog for 5.2.0-rc.0 2018-01-03 16:12:46 -08:00
5bceb89a7f release: cut the 5.2.0-rc.0 release 2018-01-03 16:09:47 -08:00
2582eca265 docs: add changelog for 5.1.3 2018-01-03 16:07:35 -08:00
3e47ea27f5 fix(language-service): ignore null metadatas (#20557)
There can be null metadatas in certain cases, for example with locales.

Fixes #20260

PR Close #20557
2018-01-02 10:29:44 -06:00
d365077dfa docs(aio): HttpClientXsrfModule withConfig => withOptions (#21185)
Docummentation suggests use of HttpClientXsrfModule#withConfig but this method looks like it's renamed to #withConfig.
https://angular.io/guide/http#configuring-custom-cookieheader-names
PR Close #21185
2018-01-02 10:29:07 -06:00
bc7a6d7b00 build: force upstream fetch before merge (#21192)
PR Close #21192
2017-12-27 17:42:03 -06:00
9f538a6cac build: add karma tests for render3 (#21188)
PR Close #21188
2017-12-27 16:46:56 -06:00
3750ea9dff feat(core): final adjustements to ngIvy read option for queries (#21187)
PR Close #21187
2017-12-27 16:46:56 -06:00
afd89ed8d9 fix(core): support read option when querying for types (#21187)
PR Close #21187
2017-12-27 16:46:56 -06:00
a62371c0eb feat(core): more read options for ngIvy queries (#21187)
PR Close #21187
2017-12-27 16:46:56 -06:00
c516bc3b35 feat(core): add ngOnInit and ngDoCheck support in render3 (#21156)
PR Close #21156
2017-12-27 16:46:56 -06:00
8bf1305490 refactor(core): avoid object creation in bind (#21155)
PR Close #21155
2017-12-27 16:46:56 -06:00
d8abf70f1f aio: Sort in the api type dropdown (#21176)
Change the order of elements in the api type dropdown to be alphabetical order

PR Close #21030
PR Close #21176
2017-12-27 11:12:54 -08:00
efd9c09456 docs(changelog): fix typo in 5.1.1 (#21007)
PR Close #21007
2017-12-22 21:39:22 -08:00
a66cd526c3 docs(forms): add text about min() and max() as functions (#21110)
PR Close #21110
2017-12-22 21:36:46 -08:00
a0ffdf1ef2 build(common): generate ts declarations for i18n locale files (#21127)
Fixes #21120
PR Close #21127
2017-12-22 21:34:03 -08:00
83d207d0a7 build: upgrade to TypeScript 2.6 (#21144)
Fixes #20653

PR Close #21144
2017-12-22 20:15:47 -08:00
83c1383701 build: upgrade circle/build into large class (more ram) (#21053)
PR Close #21053
2017-12-22 13:10:51 -08:00
c66283ad66 build: add istruction for running and debugging tests to BAZEL.md (#21053)
PR Close #21053
2017-12-22 13:10:51 -08:00
ae97920fe2 build: move _testing_init into tools; limit web_test concurrency (#21053)
PR Close #21053
2017-12-22 13:10:51 -08:00
533a010b28 build(platform-browser): exclude node incompatible tests from :test target. (#21053)
PR Close #21053
2017-12-22 13:10:51 -08:00
6b81d1c9b9 build(compiler-cli): exclude command line scripts from compilation (#21053)
PR Close #21053
2017-12-22 13:10:51 -08:00
fefc081e1b build(service-worker): enable karma bazel test for service-worker (#21053)
Corrected the environment detection code which was incorretly throwing exception in the browser if `require` function was found.

PR Close #21053
2017-12-22 13:10:51 -08:00
bdee1f4a25 build(upgrade): enable bazel tests for upgrade (#21053)
Added missing angular-mocks dependency.

PR Close #21053
2017-12-22 13:10:51 -08:00
40dfe39e64 build(router): enable bazel tests for router (#21053)
Bazel runs on newer version of RxJs than is installed in Yarn. The never version subclasses `EmptyError` in a different way which fails the `instanceof` check. This change makes the `instanceof` check more robust with respect to `EmptyError`.

PR Close #21053
2017-12-22 13:10:51 -08:00
3d50fd7cac build: add bazel test rules for remainder of packages (#21053)
PR Close #21053
2017-12-22 13:10:51 -08:00
cc1058f6e1 build: add bazel test rules for more packages (#21053)
PR Close #21053
2017-12-22 13:10:51 -08:00
47e251a80a build: remove main() from specs (#21053)
PR Close #21053
2017-12-22 13:10:51 -08:00
47bcb5bc35 build(core): add bazel test targets for core (#21053)
- Add tests target for `test`, `test_node_only` and `test_web` in `core` package.
- Created a `_testing_init` pseudo package where bootstrap code for tests is kept.
- Moved `source_map_util` from `test` to `testing` so to prevent circular dependency.
- Removed `visibility:public` for testing `BUILD` packages.

PR Close #21053
2017-12-22 13:10:51 -08:00
f3fc74ab67 build(core): remove main() from specs (#21053)
`main()` function used to be needed to support dart, since dart
Does not allow top level statements. Since we no longer use dart
The need for `main()` has been removed.

In preparation for `Basel` and standardized way of running tests
we are removing `main()`

PR Close #21053
2017-12-22 13:10:51 -08:00
86a36eaadd fix(animations): avoid infinite loop with multiple blocked sub triggers (#21119)
This patch fixes animations so that if multiple sub @triggers are used
and are blocked by a parent animation then the engine will not lead
itself into an infinite loop.

PR Close #21119
2017-12-22 09:23:28 -08:00
5ba1cf1063 fix(router): fix wildcard route with lazy loaded module (again) (#18139)
Closes #13848

Description:
We doesn't handle children of wildcard route properly link. It's always an empty array.

Created from #13851

PR Close #18139
2017-12-22 09:20:11 -08:00
07b81ae741 fix(common): handle JS floating point errors in percent pipe (#20329)
Fixes #20136
PR Close #20329
2017-12-22 09:02:49 -08:00
5a7bf36723 build: fix circular dep between interface and l_node by merging (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
66528a21f6 build: fix benchmarks for render3 (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
a77757277b build: yarn buildifier (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
4f05d022c1 feat(core): support 'read' option for ngIvy queries (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
5df343169e docs(core): add missing docs to component and fix formatting (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
764fea1344 test(core): animation renderer factory in render3 (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
bbdea96a66 refactor: remove import circular dependencies (#20855)
This PR fixes a circular dependency among those files in Renderer3:
`query` -> `di` -> `instructions` -> `query` -> ...

Looking at the above dependencies the `di` -> `instructions` import is
a problematic one. Previously `di` had an import from `instructions`
since we can known about "current node" only in `instructions`
(and we need "current node" to create node injector instances).

This commit refactors the code in the way that functions in the
`di` file don't depend on any info stored module-global variables
in `instructions`.

PR Close #20855
2017-12-21 21:40:58 -08:00
d1de587ce0 feat(core): add renderer factory in render3 (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
147aec43bd feat: support queries for elements with local names (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
1f5049f30c style: fix formatting errors (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
09e3839994 perf: use style.setProperty instead of setAttribute in render3 and iv benchmarks (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
19eeba2281 refactor(core): rename instructions for consistency (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
6cc8f2298e docs(core): add docs to instructions, minor renames (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
83b27bac17 style: fix formatting errors (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
b462f49ce7 refactor(core): renamed and split out interfaces (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
1a9064ba2b docs(core): add more comments to di and fix formatting (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
5bc869cb24 docs(core): document di and minor renames (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
8fdb1e09c1 refactor(core): store directive defs in static data (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
f3d38ce053 docs(core): add comments to node_manipulation functions (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
05953b3b83 docs(core): add comments to assert functions (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
f75296e04e perf: add benchmarks for IV and render3 with functions (tree) (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
0867e85163 perf: add large table and deep tree benchmarks for render3 (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
93b00cceb6 refactor(core): store locals in main array in rederer3 (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
0fa818b318 feat(core): Moving Renderer3 into @angular/core (#20855)
PR Close #20855
2017-12-21 21:40:58 -08:00
bc66d27938 docs(service-worker): fix word wrap (#21114)
The fix removes space between 'c' and 'aches' in docs

PR Close #21114
2017-12-21 20:12:18 -08:00
6a5818454f build: make umd.min.js source map paths relative (#21147)
I'm not quite sure how to test this since we don't have any infrastructure for these kinds of tests.
I did verify the fix manually though.

Fixes #15740

PR Close #21147
2017-12-21 20:11:20 -08:00
27fc458ef6 build: fix pullapprove (#21140)
Currently it gives a green status if I edit a file I'm an owner of,
even without anyone else's approval.

PR Close #21140
2017-12-21 14:04:20 -08:00
0487a9f140 build: update pullapprove: (#21117)
- remove ex-team members
- allow author to approve their own change
- move more bazel files under the bazel group

PR Close #21117
2017-12-21 13:17:00 -08:00
871ece6123 fix(animations): renaming issue with DOMAnimation. (#21125)
Closure Compiler renames all properties that are "internal" to the
program. `DOMAnimation` however is external, it is a browser API, so its
fields must not be renamed.

This change marks `DOMAnimation` as external using `declare interface`,
which will cause Closure Compiler to back off and prevent renaming of
any of its fields.

PR Close #21125
2017-12-21 09:44:37 -08:00
abca7c0243 docs: add changelog for 5.2.0-beta.1 2017-12-20 13:42:24 -08:00
1f5256e745 release: cut the 5.1.0-beta.1 release 2017-12-20 13:39:58 -08:00
6990354047 docs: add changelog for 5.1.2 2017-12-20 13:39:37 -08:00
135ead6c97 ci(router): update the public API guard for the router
This fixes a badly applied revert earlier.
2017-12-20 11:54:22 -08:00
33c0ee3441 fix(compiler): report an error for recursive module references
Modules that directly or indirectly export or imported themselves
reports an error instead of generating a stack fault.

Fixes: #19979
2017-12-20 10:54:45 -08:00
5efea2f6a0 feat(router): add "paramsInheritanceStrategy" router configuration option
Previously, the router would merge path and matrix params, as well as
data/resolve, with special rules (only merging down when the route has
an empty path, or is component-less). This change adds an extra option
"paramsInheritanceStrategy" which, when set to 'always', makes child
routes unconditionally inherit params from parent routes.

Closes #20572.
2017-12-20 10:06:24 -08:00
5f23a1223f fix(compiler-cli): do not force type checking on .js files
The compiler host would force any file that is in node_modules
into the list of files that needed to be type checked which
captures .js files if `allowJs` is set to `true`. This should
have only forced .d.ts files into the project to enable
generation of factories.

Fixes: #19757
2017-12-20 10:01:10 -08:00
30208759cd fix(compiler-cli): do not emit invalid .metadata.json files
If no metadata is collected the `ngc` would generate file
that contained `[null]` instead of eliding the `.metadata.json`
file.

Fixes: #20479
2017-12-20 09:58:36 -08:00
e4c53f8529 Revert "feat(router): add a function set up router sync when used with downgradeModule"
This reverts commit f5bb999319. The commit
does not include proper tests.
2017-12-20 09:15:15 -08:00
b61e3e9d20 Revert "fix(router): replaceURL when reacting to a change coming from angularjs"
This reverts commit 0b2d636b75. The commit
does not include proper tests.
2017-12-20 09:14:27 -08:00
f593552cfe docs(aio): Rename service worker files, update examples, move service worker under Techniques 2017-12-19 11:07:57 -08:00
8458647232 docs(aio): fix inconsistency in lifecycle hooks table 2017-12-19 10:48:29 -08:00
a693c5614c ci: add router/testing to public API guard 2017-12-19 10:45:33 -08:00
8ceffd8b48 fix(aio): improve transitions between pages
- Avoid unnecessary animations, style transitions, repositioning on
  initial rendering.
- Better handle transitioning from/to Home page (which is the only page
  with transparent top-menu).
- Better coordinate sidenav and hamburger animations with page
  transitions.
- Improve fade-in/out animations.

Fixes #20996
2017-12-19 10:45:19 -08:00
2986e25abb refactor(aio): clean up top-menu CSS
- Clean-up and re-organize top-menu styles.
- Clean-up and merge hamburger styles into top-menu styles.
2017-12-19 10:45:19 -08:00
f8fe53aeb0 feat(aio): support disabling DocViewer animations via class 2017-12-19 10:45:19 -08:00
74e3115686 build(aio): make zipper work correctly with CLI projects 2017-12-19 10:44:56 -08:00
3846f19f22 docs(core): move core examples into examples/core/ directory
This allows examples to be found during aio's `yarn serve-and-sync`, which only
looks for examples in `packages/examples/<packageName>/**/*`, where
`packageName` is the name of the package that the modified file belonged to;
e.g. `core`, `common`, etc.).
2017-12-18 12:10:29 -08:00
057513536b fix(upgrade): replaces get/setAngularLib with get/setAngularJSGlobal
The current names are confusing because Angular should refer to the latest version of the framework.
2017-12-18 12:10:01 -08:00
82bcd83566 feat(compiler): allow ngIf to use the ngIf expression directly as a guard
Allows a directive to use the expression passed directly to a property
as a guard instead of filtering the type through a type expression.

This more accurately matches the intent of the ngIf usage of its template
enabling better type inference.

Moved NgIf to using this type of guard instead of a function guard.

Closes: #20967
2017-12-18 12:09:21 -08:00
e48f477477 ci: temporarily remove wardbell from pullapprove (2FA) 2017-12-18 08:41:44 -08:00
20e1cc049f fix(service-worker): check for updates on navigation
Currently the Service Worker checks for updates only on SW startup,
an event which happens frequently but also nondeterministically. This
makes it hard for developers to observe the update process or reason
about how updates will be delivered to users. This problem is
exacerbated by the DevTools behavior of keeping the SW alive
indefinitely while opened, effectively preventing the page from
updating at all.

This change causes the SW to additionally check for updates on
navigation requests (app page reloads). This creates deterministic
update behavior, and is much easier for developers to reason about.
It does leave the old update-on-SW-startup behavior in place, as
removing that would be a breaking change.

Fixes #20877
2017-12-15 15:19:20 -08:00
0b2d636b75 fix(router): replaceURL when reacting to a change coming from angularjs
Closes: #20549
2017-12-15 07:59:56 -08:00
f5bb999319 feat(router): add a function set up router sync when used with downgradeModule 2017-12-15 07:59:56 -08:00
a4742763b9 test(aio): correct usage of fakeAsync and inject together in test
Corrects a test which wrapped the fakeAsync call in an inject call.  This caused
the test to not actually run.
2017-12-15 07:55:09 -08:00
05ff6c09ca fix(compiler): make tsx file aot compatible
fixes #20555
2017-12-15 07:53:46 -08:00
d91ff17adc fix(compiler): generate the correct imports for summary type-check
Summaries should be ignored when importing the types used in a
type-check block.
2017-12-15 07:53:11 -08:00
d213a20dfc fix(forms): avoid producing an error with hostBindingTypeCheck
Using the default value accessor no longer produces errors when
used in combination with fullTemplateTypeCheck and hostBindingTypeCheck.

Fixes: #19905
2017-12-15 07:52:52 -08:00
2e7e935b02 fix(common): fix a Closure compilation issue.
Closure Compiler cannot infer that the swtich statement is exhaustive,
which causes it to complain that the method does not always return a
value.

Work around the problem by throwing an exception in the default case,
and using the `: never` type to ensure the code is unreachable.
2017-12-15 07:51:30 -08:00
b89e7c2cb7 ci(aio): move e2e tests to non-optional job
This essentially reverts #20178, since the flakes should be gone after
pinning ChromeDriver and Chrome versions to 2.32 and 59 respectively.
2017-12-14 08:48:52 -08:00
b4db2e25d6 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
2017-12-14 08:48:52 -08:00
a33eaf6e07 test(aio): disable DocViewer animations during e2e tests 2017-12-14 08:48:52 -08:00
0d47c39609 test(aio): fix and clean up e2e tests 2017-12-14 08:48:52 -08:00
cbe7e39bbe build(common): don't generate .d.ts & .metadata.json files for i18n locales
Fixes #20880
2017-12-14 08:29:36 -08:00
6d57cb04f6 ci: parallelize bazel build and test
The current setup can cause a de-optimization where unit tests can't start running until the slowest build target completes.
2017-12-14 08:28:52 -08:00
6e2a8a2ba4 docs: add changelog for 5.2.0-beta.0 2017-12-13 11:43:12 -08:00
7874697b6c release: cut the 5.2.0-beta.0 release 2017-12-13 11:39:44 -08:00
767141761a docs: add changelog for 5.1.1 2017-12-13 11:36:25 -08:00
b3eb1db6dd build: update node version number in .nvmrc (#20832)
PR Close #20832
2017-12-12 11:56:18 -08:00
ee0dab025b docs: update DEVELOPER.md with the node and yarn info (#20832)
I intentionally removed version numbers so that we don't need to update them in this file -
because we usually forget.

PR Close #20832
2017-12-12 11:56:18 -08:00
b7738e1fe5 feat(core): add source to StaticInjectorError message (#20817)
Closes #19302
PR Close #20817
2017-12-12 11:56:06 -08:00
634d33f5dd fix(compiler): support referencing enums in namespaces (#20947)
Due to an overly agressive assert the compiler would generate
an internal error when referencing an enum declared in
namspace.

Fixes #18170

PR Close #20947
2017-12-12 11:55:55 -08:00
3401283399 ci: clean up circleci/config.yml (#20954)
PR Close #20954
2017-12-12 11:55:44 -08:00
981947d104 ci: allow me to approve circleCI changes (#20957)
Removes the root group from the pullapprove settings for .circleci/*
PR Close #20957
2017-12-12 11:55:35 -08:00
8c52088346 fix(compiler-cli): merge @fileoverview comments. (#20870)
Previously, this code would unconditionally add a @fileoverview
comment to generated files, and only if the contained any code at all.

However often existing fileoverview comments should be copied from the
file the generated file was originally based off of. This allows users
to e.g. include Closure Compiler directives in their original
`component.ts` file, which will then automaticallly also apply to code
generated from it.

This special cases `@license` comments, as Closure disregards directives
in comments containing `@license`.

PR Close #20870
2017-12-12 11:37:55 -08:00
add3589451 ci: use container version in cache key (#20952)
PR Close #20952
2017-12-11 16:07:28 -08:00
81d497ce1f 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
2017-12-11 15:53:04 -08:00
70cd124ede feat(compiler): add a pseudo $any() function to disable type checking (#20876)
`$any()` can now be used in a binding expression to disable type
checking for the rest of the expression. This similar to `as any` in
TypeScript and allows expression that work at runtime but do not
type-check.

PR Close #20876
2017-12-11 14:34:38 -08:00
7363b3d4b5 build: remove bazel option --noshow_results (#20943)
I originally added this when I was trying to build `//packages/core`, which is not what users will do often.
This makes it harder for team members to understand what Bazel is doing. I find myself suggesting to turn it off, so it's better to just remove it.
PR Close #20943
2017-12-11 11:16:59 -08:00
f05937db4d fix(bazel): don't equate moduleName with fileName (#20895)
Fixes broken material build.
/cc @jelbourn
PR Close #20895
2017-12-11 11:16:49 -08:00
d684f55423 build: require bazel 0.8 (#20897)
Users should get an error if they are running an older version of Bazel than we have on CI.
PR Close #20897
2017-12-11 11:16:41 -08:00
db06cb170f build: new docker image, faster to boot on circleci 2017-12-11 11:16:29 -08:00
77a1f9f2e8 ci: print the buildifier command when BUILD lint fails (#20882)
PR Close #20882
2017-12-11 11:15:47 -08:00
13e663c232 fix(animations): ensure multi-level route leave animations are queryable (#20787)
Closes #19807

PR Close #20787
2017-12-08 13:44:01 -08:00
d098cf5a8b ci: remove obsolete chromedriverpatch (#18515)
Dart(ium) is not used anymore in Angular so this file should be obsolete

PR Close #18515
2017-12-08 13:43:55 -08:00
3ce3b4d2af refactor(common): update i18n locale data to CLDR v32 (#20830)
List of changes between v31.0.1 and v32: http://cldr.unicode.org/index/downloads/cldr-32
PR Close #20830
2017-12-08 10:24:33 -08:00
e7d9cb3e4c feat(compiler): narrow types of expressions used in *ngIf (#20702)
Structural directives can now specify a type guard that describes
what types can be inferred for an input expression inside the
directive's template.

NgIf was modified to declare an input guard on ngIf.

After this change, `fullTemplateTypeCheck` will infer that
usage of `ngIf` expression inside it's template is truthy.

For example, if a component has a property `person?: Person`
and a template of `<div *ngIf="person"> {{person.name}} </div>`
the compiler will no longer report that `person` might be null or
undefined.

The template compiler will generate code similar to,

```
  if (NgIf.ngIfTypeGuard(instance.person)) {
    instance.person.name
  }
```

to validate the template's use of the interpolation expression.
Calling the type guard in this fashion allows TypeScript to infer
that `person` is non-null.

Fixes: #19756?

PR Close #20702
2017-12-08 10:24:26 -08:00
e544742156 refactor(core): Removed readonly getters and changed to readonly (#19842)
variables

PR Close #19842
2017-12-08 10:24:19 -08:00
c9ad529afc docs(common): fix mistakes in number pipe example (#20788)
PR Close #20788
2017-12-08 10:24:10 -08:00
75e468494c ci: add IgorMinar to bazel pullapprove group (#20843)
PR Close #20843
2017-12-08 10:24:00 -08:00
ddada6e2be build(aio): upgrade to latest @angular/cli (#18428)
PR Close #18428
2017-12-08 10:11:16 -08:00
22ae17bb0b build(aio): upgrade to latest @angular/material and @angular/cdk (#18428)
PR Close #18428
2017-12-08 10:11:15 -08:00
d546be48e1 build(aio): upgrade to latest @angular/* (#18428)
PR Close #18428
2017-12-08 10:11:15 -08:00
753a130aaa build(aio): upgrade to latest rxjs (#18428)
PR Close #18428
2017-12-08 10:11:15 -08:00
94e2ea7361 fix(aio): fix embedded ToC and improve ToC, destroying components and scroll timing (#18428)
- Fix embedded ToC:
  Previously, the element was added too late and was never instantiated.

- Improve ToC update timing:
  Previously, the ToC was updated after the entering animation was over, which
  resulted in the ToC being outdated for the duration of the animation.

- Improve destroying components timing:
  Previously, the old embedded components were destroyed as soon as a
  new document was requested. Even if the transition ended up never
  happening (e.g. due to error while preparing the new document), the
  embedded components would have been destroyed and the displayed
  document would not work as expected.
  Now the old embedded components are destroyed only after the new
  document has been fully prepared.

- Improve scroll-to-top timing:
  Previously, the page was scrolled to top after the entering animation was
  over, which resulted in "jumpi-ness". Now the scrolling happens after the
  leaving document has been removed and before the entering document has been
  inserted.

PR Close #18428
2017-12-08 10:11:15 -08:00
1539cd8819 feat(aio): animate the leaving/entering documents (#18428)
This commit adds a simple fade-in/out animation.

Fixes #15629

PR Close #18428
2017-12-08 10:11:15 -08:00
131c8ab6be fix(aio): do not show new document until embedded components are ready (#18428)
Previously, the document was shown as soon as the HTML was received, but before
the embedded components were ready (e.g. downloaded and instantiated). This
caused FOUC (Flash Of Uninstantiated Components).
This commit fixes it by preparing the new document in an off-DOM node and
swapping the nodes when the embedded components are ready.

PR Close #18428
2017-12-08 10:11:15 -08:00
7d81309e11 feat(aio): lazy-load embedded components (#18428)
Fixes #16127

PR Close #18428
2017-12-08 10:11:15 -08:00
225baf4686 docs(aio): fix typo for missing quote (#20888)
PR Close #20888
2017-12-08 10:06:41 -08:00
70b061be2e fix(aio): tsconfig.app.json excludes all testing files (#20779)
Fixes app build error in testing guide which has testing folder at multiple levels,
with files in them referring to files in the root `testing` folder.
Also removed the exclusion of files with `.1` in the name because
all app `.ts` files must be buildable per aio policy.
must build

PR Close #20779
2017-12-08 10:02:43 -08:00
46aa0a1cf6 fix(animations): properly recover and cleanup DOM when CD failures occur (#20719)
Closes #19093

PR Close #20719
2017-12-07 17:16:27 -08:00
661fdcd3e2 refactor(animations): instantiate Set-matching code with values in constructor (#20725)
For some reason, prior to this fix, the boolean set matching
code (within `animation_transition_expr.ts`) failed to remain
the same when compiled with closure. This refactor makes sure
that the code stays in tact.

Reproduction Details:
Passes without `ng build --prod`: https://burger.stackblitz.io/
Fails with `ng build --prod`: http://burger.fxck.cz/

Closes #20374

PR Close #20725
2017-12-07 17:16:21 -08:00
590d93b30d feat(animations): re-introduce support for transition matching functions (#20723)
Closes #18959

PR Close #20723
2017-12-07 17:16:09 -08:00
c26e1bba1d fix(animations): ensure the web-animations driver properly handles empty keyframes (#20648)
Closes #15858

PR Close #20648
2017-12-07 17:16:02 -08:00
10771d0bd8 fix(animations): support webkit-based vendor prefixes for prop validations (#19055)
Closes #18921

PR Close #19055
2017-12-07 17:15:53 -08:00
d8cc09b76c fix(router): NavigatonError and NavigationCancel should be emitted after resetting the URL (#20803)
PR Close #20803
2017-12-07 13:34:20 -08:00
d41d2c460a feat(forms): allow nulls on setAsyncValidators (#20327)
closes #20296

PR Close #20327
2017-12-07 13:34:12 -08:00
4efc32dabf fix(compiler-cli): disable checkTypes in emit. (#20828)
Closure Compiler by default will report diagnostics from type checks in
any JavaScript code, including code emitted by the Angular compiler.
Disabling `checkTypes` substantially reduces warning spam for users, and
allows them to run with stricter compiler flags (e.g. treating actual
diagnostics from user code as errors).

Closure Compiler will still type check the code and use types (where
found and correct) for optimizations.

PR Close #20828
2017-12-07 13:34:05 -08:00
ef534c0cc1 build: upgrade bazel rules to latest (#20768)
Add enough BUILD files to make it possible to
`bazel build packages/core/test`

Also re-format BUILD.bazel files with Buildifier.
Add a CI lint check that they stay formatted.

PR Close #20768
2017-12-07 11:27:50 -08:00
073f485c72 fix(compiler-cli): Fix swallowed Error messages (#20846)
This commit fixes a bug in which non-formatted errors are silently
dropped.

Internal issue: b/67739418

PR Close #20846
2017-12-06 16:49:22 -08:00
c2dbc55f11 release: cut the 5.1.0 release 2017-12-06 12:17:04 -08:00
9ee2703824 docs: add changelog for 5.1.0 2017-12-06 12:17:04 -08:00
b78ada198a fix(animations): ensure DOM is cleaned up after multiple @trigger leave animations finish (#20740)
Closes #20541

PR Close #20740
2017-12-06 07:02:42 -08:00
6790e02a13 ci: upgrade to node 8 and Bazel 0.8 (#20807)
Closes #19648

PR Close #20807
2017-12-06 06:58:33 -08:00
7cabaa0ae7 fix(service-worker): ensure initialised in browser only (#20782)
closes #20360

PR Close #20782
2017-12-06 06:55:33 -08:00
da3563ce19 fix(service-worker): esm2015 points to wrong path (#20800)
The package.json esm2015 points to the wrong path.
"esm15" should be "esm2015"

Service Worker can't be compiled with use of Closure Compiler
PR Close #20800
2017-12-06 06:53:30 -08:00
9bbec42a6c docs(aio): add service worker guide content and update nav (#20736)
PR Close #20736
2017-12-06 06:52:20 -08:00
be994496cd docs(core): add docs for i18n tokens (#17920)
PR Close #17920
2017-12-05 21:56:43 -08:00
77ef527993 docs(aio): update myUnless references to appUnless (#20658)
fixes Issue Number: #20447

PR Close #20658
2017-12-05 21:54:39 -08:00
f092a7c824 docs(aio): fix numbering of the three ways to access hero details (#20647)
Fixes #20628

PR Close #20647
2017-12-05 21:54:34 -08:00
5e25d3986d docs: component-styles guide - inline styles must be CSS (#20701)
Cannot write them in less, sass, or stylus
See CLI issue https://github.com/angular/angular-cli/issues/8472

PR Close #20701
2017-12-05 21:54:23 -08:00
35977e3830 fix(forms): Broken link NG_VALIDATORS replace by an example (#15480)
PR Close #15480
2017-12-05 21:53:12 -08:00
f7328c69b3 docs(aio): architecture review for a11y (#17848)
PR Close #17848
2017-12-05 21:49:27 -08:00
25f2211726 docs: add We Are One Sàrl as onsite training (#20714)
Adding We Are One Sàrl as an onsite training company in Switzerland.
PR Close #20714
2017-12-05 21:48:41 -08:00
18793c896b docs(aio): Fix typo (#20732)
Remove duplicate word 'to'

PR Close #20732
2017-12-05 21:46:56 -08:00
e7cdb9f660 ci(core): Improve the payload size message (#20786)
PR Close #20786
2017-12-05 21:46:10 -08:00
6911a250ef build: set preserveWhitespaces to false by default on Bazel (#20783)
`preserveWhitespaces: false` will be the default in Angular 6+

You can opt-out at component or element level.

Docs: https://angular.io/api/core/Component#preserveWhitespaces

PR Close #20783
2017-12-04 16:16:01 -08:00
7e7ff2e0aa ci: fix the payload-size checking scripts (#20683)
The scripts were accidentally broken in #20524. More specifically, when a limit
was exceeded the script would break while trying to log an error message due to
a missing `commit` variable.
This commit fixes it and also does some minor clean-up (improve docs, use more
descriptive variable names, remove dead code, etc).

PR Close #20683
2017-12-04 14:52:15 -08:00
d34f0bf573 docs: add changelog for 5.1.0-rc.1 2017-12-01 14:55:51 -08:00
1f5fa25583 release: cut the 5.1.0-rc.1 release 2017-12-01 14:55:39 -08:00
d507057476 docs: add changelog for 5.0.5 2017-12-01 14:41:48 -08:00
f582620d5b fix(service-worker): use relative path for ngsw.json
Not every application is served from the domain root. The Service
Worker made a bad assumption that it would be, and so requested
/ngsw.json from the domain root.

This change corrects this assumption, and requests ngsw.json without
the leading slash. This causes the request to be interpreted
relative to the SW origin, which will be the application root.
2017-12-01 14:21:07 -08:00
3fbcde9048 fix(service-worker): send initialization signal from the application
The Service Worker contains a mechanism by which it will postMessage
itself a signal to initialize its caches. Through this mechanism,
initialization happens asynchronously while keeping the SW process
alive.

Unfortunately in Firefox, the SW does not have the ability to
postMessage itself during the activation event. This prevents the
above mechanism from working, and the SW initializes on the next
fetch event, which is often too late.

Therefore, this change has the application wait for SW changes and
tells each new SW to initialize itself. This happens in addition to
the self-signal that the SW attempts to send (as self-signaling is
more reliable). That way even on browsers such as Firefox,
initialization happens eagerly.
2017-12-01 14:21:07 -08:00
f841fbe60f fix(compiler-cli): propagate ts.SourceFile moduleName into metadata 2017-12-01 14:19:06 -08:00
b9a91a5e74 fix(service-worker): don't crash if SW not supported
Currently a bug exists where attempting to inject SwPush crashes the
application if Service Workers are unsupported. This happens because
SwPush doesn't properly detect that navigator.serviceWorker isn't
set.

This change ensures that all passive observation of SwPush and
SwUpdate doesn't cause crashes, and that calling methods to perform
actions on them results in rejected Promises. It's up to applications
to detect when those services are not available, and refrain from
attempting to use them.

To that end, this change also adds an `isSupported` getter to both
services, so users don't have to rely on feature detection directly
with browser APIs. Currently this simply detects whether the SW API
is present, but in the future it will be expanded to detect whether
a particular browser supports specific APIs (such as push
notifications, for example).
2017-12-01 14:18:16 -08:00
65f4fad801 fix(service-worker): allow disabling SW while still using services
Currently, the way to not use the SW is to not install its module.
However, this means that you can't inject any of its services.

This change adds a ServiceWorkerModule.disabled() MWP, that still
registers all of the right providers but acts as if the browser does
not support Service Workers.
2017-12-01 14:18:16 -08:00
60a30818ef docs: add changelog for 5.1.0-rc.0 2017-11-30 21:38:52 -08:00
b967cbfc66 release: cut the 5.1.0-rc.0 release 2017-11-30 21:37:42 -08:00
8826a8235b docs: add changelog for 5.0.4 2017-11-30 21:34:44 -08:00
47addd169d revert: docs(aio): add service worker guide content and update nav (#20021) (#20716)
* revert: style: broken build due to missing new lines

This reverts commit ba6af2a6dd.
The commit that introduced these files (48300067f) will also get
reverted.

* revert: docs(aio): add service worker guide content and update nav (#20021)

This reverts commit 48300067fb.
This commit has some issues (e.g. breaks some e2e tests, adds images
to the wrong directories, breaks linting, etc).
Reverting in order to investigate and fix.
2017-11-30 09:53:07 -08:00
ba6af2a6dd style: broken build due to missing new lines 2017-11-29 20:27:25 -08:00
b9e4d62d5a docs(aio): remove services plurality (#20696)
remove services plurality for the sentence formation to be proper

PR Close #20696
2017-11-29 16:53:49 -06:00
71e5de646b fix(compiler-cli): fix memory leak in program creation (#20692)
Saving `oldProgram` in `AngularCompilerProgram` instances is causing a memory leak for unemitted programs.

It's not actually used so simply not saving it fixes the memory leak.

Fix #20691

PR Close #20692
2017-11-29 16:53:11 -06:00
3def2cc552 test(aio): cleaner approach to reliable Google Analytics e2e tests (#20661)
PR Close #20661
2017-11-29 16:53:04 -06:00
4ec4a99f16 test(aio): fix e2e API test due to #20607 (#20661)
PR Close #20661
2017-11-29 16:53:04 -06:00
3203069d6c fix(language-service): Allow empty templates (#20651)
Fixes the bug where templates with empty strings show up as error in the editor.

PR Close #19406

PR Close #20651
2017-11-29 16:52:55 -06:00
54bfe14313 fix(language-service): Fix crash when no script files are found (#20550)
Fixes the crash in typescript host when getScriptFileNames() returns an
empty array.

PR Close #19325

PR Close #20550
2017-11-29 16:52:48 -06:00
ba850b36de Revert "fix(core): should use native addEventListener in ngZone (#20672)"
This reverts commit 65a2cb8307.
2017-11-29 14:56:29 -06:00
f3c5481181 docs(aio): Updating with Ignite UI for Angular (#20663)
PR Close #20663
2017-11-29 12:15:34 -06:00
48300067fb docs(aio): add service worker guide content and update nav (#20021)
PR Close #20021
2017-11-29 12:15:27 -06:00
b841e0d530 docs(aio): add class attribute to example referenced in Structural Directive guide (#19446)
See https://github.com/angular/angular/blob/master/aio/content/guide/structural-directives.md

From the structural-directives.md:
The rest of the <div>, including its class attribute, moved inside the <ng-template> element.

Maybe this made sense at one time but it has become out of sync.

PR Close #19446
2017-11-29 12:15:21 -06:00
65a2cb8307 fix(core): should use native addEventListener in ngZone (#20672)
PR Close #20672
2017-11-28 22:27:25 -06:00
0bef021321 build(aio): upgrade codelyzer to 4.0.x and angular/cli to 1.5.4 (#20392)
PR Close #20392
2017-11-28 22:27:18 -06:00
aafa75da84 fix(common): don't strip XSSI prefix for if error isn't JSON (#19958)
This changes XhrBackend to not strip the XSSI prefix from error text
if such a prefix is present but the remaining body does not parse as
JSON.

PR Close #19958
2017-11-28 22:27:10 -06:00
503be69af6 fix(common): treat an empty body as null when parsing JSON in HttpClient (#19958)
Previously, XhrBackend would call JSON.parse('') if the response body was
empty (a 200 status code with content-length 0). This changes the XhrBackend
to attempt the JSON parse only if the response body is non-empty. Otherwise,
the body is left as null.

Fixes #18680.
Fixes #19413.
Fixes #19502.
Fixes #19555.

PR Close #19958
2017-11-28 22:27:10 -06:00
eb01ad583f fix(common): remove useless guard in HttpClient (#19958)
An invalid "if" condition is always true, and is thus useless. This
change removes it. No behavior changes.

Fixes #19223.

PR Close #19958
2017-11-28 22:27:10 -06:00
15a54df7d3 fix(common): accept falsy values as HTTP bodies (#19958)
Previously, HttpClient used the overly clever test "body || null"
to determine when a body parameter was provided. This breaks when
the valid bodies '0' or 'false' are provided.

This change tests directly against 'undefined' to detect the presence
of the body parameter, and thus correctly allows falsy values through.

Fixes #19825.
Fixes #19195.

PR Close #19958
2017-11-28 22:27:10 -06:00
eaaae2edf4 docs: fix grammar and wording (#18530)
PR Close #18530
2017-11-28 22:27:02 -06:00
c2b3792a3b fix(animations): ensure multi-level leave animations work (#19455)
PR Close #19455
2017-11-28 18:24:41 -06:00
b2a586cee1 fix(animations): ensure multi-level enter animations work (#19455)
PR Close #19455
2017-11-28 18:24:40 -06:00
8bb42df47e fix(compiler): correctly detect when to serialze summary metadata (#20668)
The change to improve error messages broke the summary serialization
of summaries.

PR Close #20668
2017-11-28 16:43:35 -06:00
add5953aa1 Revert "fix(animations): ensure multi-level enter animations work (#19455)"
This reverts commit dd6237ecd9.
2017-11-28 15:08:44 -06:00
6b4c24020d Revert "fix(animations): ensure multi-level leave animations work (#19455)"
This reverts commit 1366762d12.
2017-11-28 15:08:31 -06:00
24bf3e2a25 feat(common): add locale id parameter to registerLocaleData (#20623)
PR Close #20623
2017-11-27 17:00:06 -06:00
8ecda94899 feat(compiler-cli): improve error messages produced during structural errors (#20459)
The errors produced when error were encountered while interpreting the
content of a directive was often incomprehencible. With this change
these kind of error messages should be easier to understand and diagnose.

PR Close #20459
2017-11-27 16:59:57 -06:00
1366762d12 fix(animations): ensure multi-level leave animations work (#19455)
PR Close #19455
2017-11-27 16:59:47 -06:00
dd6237ecd9 fix(animations): ensure multi-level enter animations work (#19455)
PR Close #19455
2017-11-27 16:59:46 -06:00
6e83204238 fix(bazel): produce named AMD modules for codegen (#20547)
fixes #19422

Signed-off-by: Alex Eagle <alexeagle@google.com>

PR Close #20547
2017-11-27 13:44:41 -06:00
a53a040071 build(aio): prevent comments in code from leaking into doc-gen code snippets (#20607)
The new version of dgeni-packages (0.22.1) does a better job of rendering
code nodes, which do not include comments.

Fixes #19751

PR Close #20607
2017-11-27 12:16:28 -06:00
5bef070e16 docs(aio): update homepage tooling image (#20593)
Fix #19831

PR Close #20593
2017-11-27 12:16:20 -06:00
89de98b25e build(aio): ensure downloadable zip filenames are unique (#20586)
Fixes #16227

PR Close #20586
2017-11-27 12:16:14 -06:00
de78307928 fix(compiler-cli): normalize sourcepaths for i18n extracted files (#20417)
Fixes #20416
PR Close #20417
2017-11-27 12:16:07 -06:00
6293ca23c3 docs(core): fix broken NgZone code example (#19291)
The current code example was broken as there were a couple of syntax errors. This commit fixes the demo.

PR Close #19291
2017-11-27 12:14:47 -06:00
330bb2a360 release: cut the 5.1.0-beta.2 release 2017-11-22 13:34:53 -08:00
59455ea8d1 release: cut the 5.1.0-beta.2 release 2017-11-22 13:25:40 -08:00
e9e4ffd6e1 docs: add changelog for 5.0.3 2017-11-22 13:25:40 -08:00
0444e13efb docs(aio): add ngATL header (#20590)
Fix #20568

PR Close #20590
2017-11-22 13:49:59 -06:00
ffb6dbeefe fix(animations): always fire start and done callbacks in order for noop animations (#20570)
PR Close #20570
2017-11-22 12:36:28 -06:00
0e012c9669 fix(animations): always fire inner trigger callbacks even if blocked by parent animations (#19753)
Closes #19100

PR Close #19753
2017-11-22 10:38:02 -06:00
a0819d3af1 docs(aio): add a French onsite training ressource (#19889)
PR Close #19889
2017-11-22 08:58:02 -06:00
ffd8c361eb docs(aio): add a French onsite training ressource (#19889)
PR Close #19889
2017-11-22 08:58:02 -06:00
1594f8c09e docs(aio): fix filename (#20569)
PR Close #20569
2017-11-22 08:57:37 -06:00
d7a727cc07 feat(platform-browser-dynamic): export JitCompilerFactory (#20478)
Fixes #20125
PR Close #20478
2017-11-22 08:56:11 -06:00
437a0446e2 fix(common): return ISubscription from Location.subscribe() (#20429)
Fix #20406

PR Close #20429
2017-11-22 08:55:10 -06:00
ac93f1235e ci: Update 1% payload size test (#20524)
PR Close #20524
2017-11-21 22:28:28 -06:00
e2b76bb386 build(aio): filter out ambiguous directives from auto code linking (#20512)
Closes #20466

PR Close #20512
2017-11-21 22:28:16 -06:00
ae0275e2dd build(aio): do not store duplicate metadata aliases (#20512)
Having duplicates was causing the code auto-linking
to ignore `ngForm` directives.

PR Close #20512
2017-11-21 22:28:16 -06:00
ecce90718b build(aio): better parsing of selectors as aliases for directives/components (#20512)
PR Close #20512
2017-11-21 22:28:16 -06:00
c2cb475a2c docs(aio): add angularfirebase.com to education resources (#20302)
PR Close #20302
2017-11-21 22:28:00 -06:00
7cf5e95ac9 ci: update pullapprove (#20540)
PR Close #20540
2017-11-21 11:49:52 -06:00
a740e4f00a fix(core): fix #20532, should be able to cancel listener from mixed zone (#20538)
PR Close #20538
2017-11-21 11:49:36 -06:00
2a9d2bacd5 docs: update changelog with missing feature in 5.1.0-beta.1 (#20508)
PR Close #20508
2017-11-20 18:39:48 -06:00
4ed04392d3 fix(compiler): support event bindings in fullTemplateTypeCheck (#20490)
The type-check block now disables type checking event access instead
of generating a reference to an undefined variable.

PR Close #20490
2017-11-20 18:39:42 -06:00
23ab83b504 docs: update the triaging doc with the latest process (#20128)
PR Close #20128
2017-11-20 18:39:36 -06:00
9332161e01 docs: add description for target labels + add LTS target label (#20128)
PR Close #20128
2017-11-20 18:39:36 -06:00
816d5ba3fd docs: NgModule guide prose for CLI (partial) (#19776)
Also replaces “Angular Module” with “NgModule” wherever that is clarifying.
Continue using “module” when qualified as in “feature module”, “root module”, “routing module”, etc.

PR Close #19776
2017-11-20 18:39:25 -06:00
69c53c3e03 fix(forms): updateOn should check if change occurred (#20358)
Fixes #20259.

PR Close #20358
2017-11-17 16:28:46 -06:00
365712e2f0 docs(aio): fix typo in Attribute Directives documentation (#20143)
changed "appHightlight" to "appHighlight"

PR Close #20143
2017-11-17 12:53:10 -06:00
997336b790 fix(core): should support event.stopImmediatePropagation (#20469)
PR Close #20469
2017-11-16 22:43:53 -06:00
1861e416a1 fix(animations): validate against trigger() names that use @ symbols (#20326)
PR Close #20326
2017-11-16 16:20:09 -06:00
7c44637fbf build(aio): tighten up code autolinking (#20468)
Do not match code "words" that are part of a hyphenated
string of characters: e.g. `platform-browser-dynamic` should
not auto-link `browser`.

Do not match code "words" that correspond to pipe names
but are not preceded by a pipe `|` character. E.g. `package.json` should
not auto link `json` to the `JsonPipe`.

Closes #20187

PR Close #20468
2017-11-16 16:20:03 -06:00
68b53c07fd fix(compiler): emit correct type-check-blocks with TemplateRef's (#20463)
The type-check block generated with `"fullTemplateTypeCheck"` was
invalid if the it contained a template ref as would be generated
using the `else` micro-syntax of `NgIf`.

Fixes: #19485

PR Close #20463
2017-11-16 16:19:54 -06:00
368cb5ad4e docs(aio): fix a typo to improve readability (#20435)
Remove a comma and space

PR Close #20435
2017-11-16 16:19:48 -06:00
949836d003 docs(aio): Clearing array with [] (#20369) (#20395)
Convert remaining references to directly use LoggerServices logs.

PR Close #20395
2017-11-16 16:19:35 -06:00
697fb76960 docs(aio): Removing reference to LoggerService property (#20369) (#20395)
The reference removed so that calling LoggerService clear() method
behaves as intended in SpyComponent.

PR Close #20395
2017-11-16 16:19:35 -06:00
0fedb57cb0 docs(aio): Clearing array with [] (#20369) (#20395)
Clearing array with setting length to 0 replaced with [] for being short
and marginally efficient. For reference: [] is turned into a sequence of
around 15 machine instructions on x64 (if bump pointer allocation succeeds),
whereas a.length=0 is a C++ runtime function call, which requires 10-100x as
many instructions.
Benedikt Meurer

PR Close #20395
2017-11-16 16:19:35 -06:00
717ac5ac4d fix(benchpress): Allow ignoring navigationStart events in perflog metric. (#20312)
PR Close #20312
2017-11-16 16:18:38 -06:00
4064cbe945 docs: add changelog for 5.1.0-beta.1 2017-11-16 14:04:25 -08:00
a88306d671 release: cut the 5.1.0-beta.1 release 2017-11-16 14:03:03 -08:00
c702ffc471 docs: add changelog for 5.0.2 2017-11-16 13:41:55 -08:00
dcfffbf828 build: fix build failures (#20470) 2017-11-15 22:33:30 -08:00
336041aac9 docs(aio): Add instructions to fork angular/angular (#20462)
PR Close #20462
2017-11-15 18:26:27 -06:00
7e38f4fd1f fix(aio): fix window title on Home page (#20440)
Using `display: none` on the `<h1>` causes `innerText` to not work as expected
and include the icon ligature (`link`) in the title. This caused the window
title on the angular.io Home page to appear as "Angular - link".
This commit fixes it by not generating anchors at all for headings with the
`no-anchor` class.

Fixes #20427

PR Close #20440
2017-11-15 18:25:58 -06:00
c28b52187a docs(aio): add missing closing backtick (#20446)
PR Close #20446
2017-11-15 18:25:49 -06:00
5ec1717c58 fix: Update test code to type-check under TS 2.5 (#20175)
PR Close #20175
2017-11-15 18:12:16 -06:00
c2a24b4241 fix(bazel): adjust mock of tsconfig for ng_module rule unit test (#20175)
Due to 7f33f01b72

PR Close #20175
2017-11-15 18:12:16 -06:00
a9f3e2bd95 build: Upgrade to TypeScript 2.5 (#20175)
- update to TypeScript 2.5
- point the 2.4 typings test at the previous typescript version, so we
  don't break it accidentally
- widen the peerDeps from Angular packages that depend on TypeScript
- update to latest TypeScript 2.5 compatible Bazel rules
- move .bazelrc to tools/bazel.rc per https://docs.bazel.build/versions/master/best-practices.html#bazelrc

PR Close #20175
2017-11-15 18:12:16 -06:00
f8658cdc38 Revert "fix(animations): always fire inner trigger callbacks even if blocked by parent animations (#19753)"
This reverts commit d47b2a6f70.
2017-11-15 17:04:22 -06:00
043e408805 style(core): fix comment format for linter 2017-11-15 11:53:11 -08:00
c004d483ab style(core): fix max line length to pass linting (#20441)
Accidentally introduced in #19920, where other linting errors (unrelated to the
PR) prevented proper linting.

PR Close #20441
2017-11-15 11:44:52 -08:00
2586846ee2 Revert "fix(core): should support event.stopImmediatePropagation"
This reverts commit 5e0eb5e3d94bd7077c4d6657b89bfc8d900f2bc6.
2017-11-15 11:35:21 -08:00
d47b2a6f70 fix(animations): always fire inner trigger callbacks even if blocked by parent animations (#19753)
Closes #19100

PR Close #19753
2017-11-14 15:59:47 -08:00
1adbcda12e docs(aio): fix wrong copy (#20431)
PR Close #20431
2017-11-14 15:58:25 -08:00
12af6d356e docs(core): change from deprecated renderer to renderer2 (#19920)
We now show the proper class instead of the deprecated Renderer

Fixes #19806


PR Close #19920
2017-11-14 13:44:56 -08:00
20aafff092 fix(animations): ensure final state() styles are applied within @.disabled animations (#20267)
Closes #20266

PR Close #20267
2017-11-14 11:01:11 -08:00
a622e19df6 fix(router): 'merge' queryParamHandling strategy should be able to remove query params (#19733)
Closes #18463, #17202

PR Close #19733
2017-11-14 11:01:04 -08:00
1db7c0d139 ci: tighten package limits (#20364)
PR Close #20364
2017-11-14 11:00:58 -08:00
b9bd3204f2 docs(aio): fix broken link in guide/component-interaction (#20411)
add a blank line before line with markdown link
PR Close #20411
2017-11-14 10:00:46 -08:00
bf651a504f docs(aio): make it clear we are talking about <a> tags (#20410)
As is, it could be seen as a typo at first glance. Wrapping the "a" in carets and backticks for formatting adds some clarity.

PR Close #20410
2017-11-14 10:00:42 -08:00
e5c4371d72 docs(aio): fix typo in filename (packages.json --> package.json) (#20377)
PR Close #20377
2017-11-14 10:00:39 -08:00
3caae94261 docs(aio): fix not found schema (#20347)
Fixes #20338

PR Close #20347
2017-11-14 10:00:35 -08:00
e7a2b31472 docs(aio): fix rxjs import (#20350)
closes #20349

PR Close #20350
2017-11-14 10:00:31 -08:00
c03186013c docs: Dependency Injection guides for CLI (#19892)
PR Close #19892
2017-11-14 10:00:28 -08:00
5a2531ee45 docs(aio): update template forms to CLI (#20014)
PR Close #20014
2017-11-14 10:00:23 -08:00
6ca780178c docs(aio): update reactive-forms to CLI (#20019)
PR Close #20019
2017-11-14 10:00:17 -08:00
9608b0636d docs(aio): update displaying-data for CLI (#19574)
PR Close #19574
2017-11-14 10:00:13 -08:00
89187d9b6b docs(aio): update glossary for CLI (#20017)
PR Close #20017
2017-11-14 10:00:10 -08:00
335b72f301 docs(aio): update ajs-quick-reference for CLI (#19552)
PR Close #19552
2017-11-14 10:00:02 -08:00
74071210eb docs(aio): remove trailing underscore from provide_ (#20356)
_provide_ was already emphasized in the previous paragraph

PR Close #20356
2017-11-14 09:59:56 -08:00
fde966832b docs(aio): fix missed grave accent (#20379)
PR Close #20379
2017-11-14 09:59:51 -08:00
75d474e1d3 docs: AttributeDirectives guide for CLI (#19771)
PR Close #19771
2017-11-14 09:59:43 -08:00
24cf8b3269 fix(core): ensure init lifecycle events are called (#20258)
Throwing an exception in a lifecycle event will delay but not
prevent an Init method, such as `ngOnInit`, `ngAfterContentInit`,
or `ngAfterViewInit`, from being called. Also, calling `detectChanges()`
in a way that causes duplicate change detection (such as a
child component causing a parent to call `detectChanges()` on its
own `ChangeDetectorRef`, will no longer prevent change `ngOnInit`,
`ngAfterContentInit` and `ngAfterViewInit` from being called.

With this change lifecycle methods are still not guarenteed to be
called but the Init methods will be called if at least one change
detection pass on its view is completed.

Fixes: #17035

PR Close #20258
2017-11-10 13:47:39 -08:00
743651f5e8 refactor(animations): uses a loop instead Array.map() which creates and (#19910)
returns a new array that is discarded.

This pattern will become a compilation error in google3.

PR Close #19910
2017-11-10 13:47:02 -08:00
161f88fe6f build: update to google-closure-compiler@20171023.0.1 (#20321)
added externs

remove externs

PR Close #20321
2017-11-10 11:51:57 -08:00
c33a57666b fix(compiler): recognize @NgModule with a redundant @Injectable (#20320)
The compiler now, again, recognizes `@NgModule()` decorators on
classes with a redundant `@Injectable()` decorator.

Fixes: #19544

PR Close #20320
2017-11-10 11:51:52 -08:00
cf618c564c fix(aio): markdown typo in Tour of Heroes tutorial (#20288)
PR Close #20288
2017-11-10 11:51:18 -08:00
401ead07b8 docs(common): update default display value for CurrencyPipe (#20246)
PR Close #20246
2017-11-10 11:51:17 -08:00
b55c2ba342 refactor(core): remove prolyfill from error message (#20121)
PR Close #20121
2017-11-10 11:51:15 -08:00
d8db0f12a2 docs(changelog): remove unnecessary revert item (#20303)
PR Close #20303
2017-11-10 11:51:13 -08:00
eb8013e853 fix(language-service): pass compilerOptions.paths to ReflectorHost (#20222)
This commit fixes the options passed to ReflectorHost to include 'paths'
if it's specified in compiler options, so that dependency modules can
be loaded.

PR Close #20222
2017-11-10 11:51:11 -08:00
fb4b90a564 docs(aio): typo fix (#20318)
tutorial part 0 app component template file extention fix

PR Close #20318
2017-11-10 11:12:07 -08:00
7830d74615 docs(aio): empty line between HTML tag and content (#20341)
PR Close #20341
2017-11-10 10:59:46 -08:00
8e24c0fff4 docs(aio): fix typo (#20103)
PR Close #20103
2017-11-10 10:59:35 -08:00
cf0444b731 docs(aio): show correct path for mock-heroes code (#20323)
PR Close #20323
2017-11-10 10:59:25 -08:00
a7bbe9a1ff docs(aio): Fix typo in tutorial (#20295)
PR Close #20295
2017-11-10 10:59:10 -08:00
ffe323036e docs(aio): fix toh-pt3 typos (#20307)
PR Close #20307
2017-11-10 10:59:01 -08:00
b4a39f9c30 docs(aio): fix -mm- to -MM- for month in DatePipe (#20315)
PR Close #20315
2017-11-10 10:58:45 -08:00
3257fcdcee fix(compiler): show explanatory text in template errors (#20313)
Fixes: #20076

PR Close #20313
2017-11-09 15:46:30 -08:00
9bcd7097d0 test(compiler): do not use a as a content selector
As it might trigger false positive in the RegExp from `_makeScopeMatcher`

closes #20256
2017-11-08 15:52:21 -08:00
c32f5fd393 fix(compiler): fix corner cases in shadow CSS
`cmp:host {}` and `cmp:host some-other-selector {}` were not handled
consistently.

Note those should not match anything but are made equivalent to respectively
`:host(cmp)` and `:host(cmp) some-other-selector` to avoid breaking legacy apps.
2017-11-08 15:20:19 -08:00
78ba39bfe2 docs: add changelog for 5.1.0-beta.0 2017-11-08 11:32:48 -08:00
119034c642 docs: add changelog for 5.0.1 2017-11-08 10:30:17 -08:00
6e8e3bd248 refactor(core): misc changes and integrate review feedback on #19996
closes #20224
2017-11-06 14:13:02 -08:00
a460066972 feat(compiler): introduce TestBed.overrideTemplateUsingTestingModule
This allows to overwrite templates for JIT and AOT components alike.

In contrast to `TestBed.overrideTemplate`, the template is compiled
in the context of the testing module, allowing to use other testing
directives.

Closes #19815
2017-11-06 14:12:30 -08:00
05d96dc507 feat(core): allow to pass in aot summaries also to TestBed.configureTestingModule
Also adds caching for summaries.

Closes #19817.
2017-11-06 14:12:30 -08:00
b489259a34 docs(common): fix mis-ordered lines (#20221) 2017-11-06 11:28:39 -08:00
6b748835be build(aio): upgrade to @anglar/core@5.0.0 and cli@1.5.0
-rw-r--r--  1 iminar  eng   14880 Nov  4 11:54
dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1533 Nov  4 11:54
dist/inline.9183bfe0d60f43b6a772.bundle.js
-rw-r--r--  1 iminar  eng  486476 Nov  4 11:54
dist/main.f5445f99490330207c9c.bundle.js
-rw-r--r--  1 iminar  eng   37070 Nov  4 11:54
dist/polyfills.0dfca732c5a075c110d0.bundle.js

Closes #20184
2017-11-06 10:04:43 -08:00
d30ea61f0d build(aio): remove the build-optimizer flag
it's on by default now.
2017-11-06 10:04:02 -08:00
0c47ea704e build(aio): use aot compiler for development
since it was not turned on by default in the cli we
have to opt in.
2017-11-06 10:04:02 -08:00
049c89645b docs(aio): update ToH for CLI (#19811) 2017-11-06 10:02:17 -08:00
bf22f2df88 fix(compiler): report a reasonable error with invalid metadata (#20062)
The compiler would throw an internal exception if an import using
the `ngModule` syntax and the module as not a resolvable symbol.

Fixes: #20049
2017-11-06 10:01:27 -08:00
880201681f fix(aio): style correctly on Safari (#20157) 2017-11-06 10:00:37 -08:00
63d26a1777 ci(aio): move e2e tests to optional job (#20178) 2017-11-06 09:59:59 -08:00
8b50ed083c docs(aio): fixed case-typo for code (#20196) 2017-11-06 09:59:17 -08:00
3997d97806 revert: feat(elements): implement @angular/elements #19469 (#20152)
This PR was merged without API docs and general rollout plan.

We can't release this as is in 5.1 without a plan for documentation, cli integration, etc.
2017-11-03 15:54:54 -07:00
200d92d030 fix(core): should support event.stopImmediatePropagation (#19222) 2017-11-03 15:22:05 -07:00
dbec3ca716 docs(aio): update pipes for CLI (#19553) 2017-11-03 15:21:14 -07:00
f7c9b941cb ci(aio): Add payload size percentage changes check to payload size task (#19908) 2017-11-03 15:20:09 -07:00
f0764016f9 revert: feat(core): add source to StaticInjectorError message (#19482) (#20171)
This reverts commit faa621218e.
2017-11-03 14:51:06 -07:00
a99eb16320 test(aio): make e2e tests less flakey
closes #20117
2017-11-03 11:22:57 -07:00
e36bac9e90 build(aio): upgrade protractor to v5.2.0 2017-11-03 11:22:41 -07:00
196ce6d475 build(aio): use correct types for e2e tests 2017-11-03 11:22:41 -07:00
faa621218e feat(core): add source to StaticInjectorError message (#19482) 2017-11-03 11:14:05 -07:00
169cedd43b docs: Bootstrapping guide prose for CLI (#19777) 2017-11-03 11:13:24 -07:00
567cc26b8e docs: Component Styles guide for CLI (#19791) 2017-11-03 11:12:34 -07:00
1d19d61970 fix(platform-browser): support Symbols in custom jasmineToString() method (#19794)
It's illegal to coerce a Symbol to a string, and results in a TypeError:

TypeError: Cannot convert a Symbol value to a string

Previously, the custom jasmineToString() method monkey-patched onto Maps
in platform-browser/testing/src/matchers.ts would coerce keys and values
to strings. A change in a newer version of Jasmine calls this method more
often, resulting in calls against Maps which contain Symbols in some
applications, which causes crashes.

The fix is to explicitly convert keys and values to strings, which does
work on Symbols.
2017-11-03 11:11:47 -07:00
03f080b7da docs: Deployment guide for CLI (#19839) 2017-11-03 11:10:53 -07:00
26f82995f6 fix(compiler): don't overwrite missingTranslation's value in JIT (#19952) 2017-11-03 11:08:39 -07:00
f1da1419fa docs(aio): Fix typo in Architecture Overview. (#20073) 2017-11-03 11:00:22 -07:00
5079d2d37c docs(aio): Add v4 doc link to navigation (#20089)
Fixes #20090
2017-11-03 10:59:27 -07:00
c7fd172ba7 build(aio): make plunker works with rxjs operators (#20124) 2017-11-03 10:47:23 -07:00
dcf8840831 feat(elements): implement registerAsCustomElements()
closes #19469
2017-11-02 16:09:30 -07:00
60c0b178af feat(elements): implement NgElements 2017-11-02 16:09:09 -07:00
0899f4f8fc feat(elements): implement NgElementConstructor 2017-11-02 16:09:09 -07:00
aed4a11d01 feat(elements): implement NgElement 2017-11-02 16:09:09 -07:00
75cf70ae04 feat(elements): implement NgElementApplicationContext 2017-11-02 16:09:09 -07:00
6b30fbf94e feat(elements): implement extractProjectableNodes() 2017-11-02 16:09:09 -07:00
24f17f913a feat(elements): implement utils 2017-11-02 16:09:09 -07:00
ebfa204af0 feat(elements): set up the elements package 2017-11-02 16:09:09 -07:00
a28d616e10 fix(core): __symbol__ should return __zone_symbol__ without zone.js loaded (#19541) 2017-11-02 16:06:06 -07:00
613a9e3672 build: update Windows symlink scripts (#16761)
Fixes packages\upgrade\static\src symlink.

Closes #16760
2017-11-02 16:05:13 -07:00
65d57a07e0 build(aio): avoid building before running the local PWA tests
When this command is run on CI, `yarn build` has already been run, so
this was unnecessarily building angular.io again (adding ~4mins to the
`aio` job).
When this command is run locally, it is most often about testing a new
`lighthouse` version/config, so you don't need to build angular.io over
and over (and if necessary, one can always run `yarn build` manually).

Closes #19633
2017-11-02 16:02:52 -07:00
22946cfd40 build(aio): upgrade lighthouse to v2.5.0 2017-11-02 16:02:25 -07:00
9975486954 build: remove local yarn (#19981)
We use the globally installed yarn now. The local yarn was used in
`check-environment.js` only, which results in the `--integrity` check
always failing (if dependencies were installed with the global yarn).
2017-11-02 15:11:34 -07:00
068348e9b1 build(aio): do not fail the initial doc-gen when running docs-watch
Checks that cause the doc-gen to fail were already disabled in `docs-watch`
for the doc-gen that runs when a file is changed.
Now these checks are also disabled for the initial doc-gen run.
closes #20038
2017-11-02 15:03:59 -07:00
1beab0da6a build(aio): do not fail on bad examples when running docs-watch 2017-11-02 15:00:22 -07:00
3a03ff6b2d build(aio): allow render-examples to complete even if examples are broken
By setting `renderExamples.ignoreBrokenExamples = true` the doc-gen will
not fail if there is something wrong with an example. Instead it will
just log a warning.
2017-11-02 15:00:22 -07:00
feae55b264 docs(aio): fix badly formatted code-examples
closes #19970
2017-11-02 14:59:05 -07:00
0355142737 build(aio): fail doc-gen if a code-example is badly formatted.
This will catch the problem that was missed in
https://github.com/angular/angular/pull/19845#issuecomment-338626662
2017-11-02 14:58:38 -07:00
5b16ce9302 fix(compiler-cli): don't report emit diagnostics when --noEmitOnError is off (#20063) 2017-11-02 14:49:38 -07:00
17ed14faea docs: fix changelog for v5 (#20097) 2017-11-02 14:37:39 -07:00
d156e72ad7 docs: Add 0 in padding of 'dd' (#20107) 2017-11-02 14:36:22 -07:00
7186c9c839 docs: AOT guide for CLI #19510 (#19818) 2017-11-02 14:34:21 -07:00
a41558eb30 docs: BrowserSupport guide for CLI (#19843) 2017-11-02 14:29:59 -07:00
a91252a90c docs: Npm Packages guide for CLI (#19850) 2017-11-02 14:27:53 -07:00
132c0719dc docs(aio): updated i18n guide and example (#19975) 2017-11-02 14:22:09 -07:00
3db7112b89 docs(aio): fix linting for universal (#20086)
PR Close #20086
2017-11-02 00:51:29 +01:00
2ea76cce31 build: change the version in package.json to 5.1.0-beta
this is needed to reflect that the current branch will be yielding 5.1.0 releases

some of our scripts (e.g. aio deployment) relies on this identifier.
2017-11-02 00:17:55 +01:00
b8bb2dd0f5 build(aio): update lockfile for examples
Installing dependencies for the docs examples fails, because the
lockfile is out-of-sync with the corresponding `package.json`.
This commit brings the lockfile in sync with `package.json`.

(For reference, this was accidentally broken in #20039.)
2017-11-02 00:15:59 +01:00
27ae0f9475 build(aio): upgrade to @angular/material@2.0.0-beta.12 (#19702)
-rw-r--r--  1 iminar  eng   14880 Nov  1 12:25 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1533 Nov  1 12:25 dist/inline.2826385ad3e299c6d1c1.bundle.js
-rw-r--r--  1 iminar  eng  486476 Nov  1 12:25 dist/main.f0610805f4aad19da4be.bundle.js
-rw-r--r--  1 iminar  eng   37070 Nov  1 12:25 dist/polyfills.0dfca732c5a075c110d0.bundle.js

PR Close #19702
2017-11-01 15:24:48 -07:00
171dceb010 build(aio): upgrade to @angular/core@5.0.0-rc.9 (#19702)
build fails - material upgrade required

PR Close #19702
2017-11-01 15:24:48 -07:00
1ebc0d1e33 revert: build(aio): remove cli patches (#19702)
This reverts commit f0d530b4de38f71c759e42afc8f3d7531eb1b1fb.

cli rc.8 reintroduces the node polyfill which causes size regression,
so I'm putting the patch back in.

-rw-r--r--  1 iminar  eng   14880 Nov  1 12:11 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1533 Nov  1 12:11 dist/inline.25600c3b48de18b97581.bundle.js
-rw-r--r--  1 iminar  eng  486476 Nov  1 12:11 dist/main.d1292a34401056535884.bundle.js
-rw-r--r--  1 iminar  eng   37070 Nov  1 12:11 dist/polyfills.0dfca732c5a075c110d0.bundle.js

PR Close #19702
2017-11-01 15:24:47 -07:00
1d8e0758fa build(aio): upgrade to @angular/cli@1.5.0-rc.8 (#19702)
-rw-r--r--  1 iminar  eng   14880 Nov  1 11:57 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1533 Nov  1 11:57 dist/inline.3574d1d784c09c507dbd.bundle.js
-rw-r--r--  1 iminar  eng  497812 Nov  1 11:57 dist/main.76bbb69df79eaefef54c.bundle.js
-rw-r--r--  1 iminar  eng   37259 Nov  1 11:57 dist/polyfills.fdb71956ccd13330fb47.bundle.js

PR Close #19702
2017-11-01 15:24:47 -07:00
cd55643f85 build(aio): upgrade to @angular/cli@1.5.0-rc.6 (#19702)
-rw-r--r--  1 iminar  eng   14880 Oct 30 11:29 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1533 Oct 30 11:29 dist/inline.25600c3b48de18b97581.bundle.js
-rw-r--r--  1 iminar  eng  486476 Oct 30 11:29 dist/main.d1292a34401056535884.bundle.js
-rw-r--r--  1 iminar  eng   37070 Oct 30 11:29 dist/polyfills.0dfca732c5a075c110d0.bundle.js

PR Close #19702
2017-11-01 15:24:47 -07:00
486b8e6f69 ci(aio): decrease payload size limit for main file (#19702)
PR Close #19702
2017-11-01 15:24:47 -07:00
215d373ebd refactor(aio): rename CustomMdIconRegistry to CustomIconRegistry (#19702)
The change of Angular Material version means that the `md` prefix is
no longer appropriate.

PR Close #19702
2017-11-01 15:24:47 -07:00
4038b42396 build(aio): lock zone.js to 0.8.16 (#19702)
Later versions (before 0.8.19) had a size increase.

PR Close #19702
2017-11-01 15:24:46 -07:00
9ab1f4a9c9 style(aio): fix docs linting issues (#19702)
These issues appeared after upgrade of eslint jasmine plugin

PR Close #19702
2017-11-01 15:24:46 -07:00
e9afc59a81 ci(aio): increase payload size limit for polyfills.ts (#19702)
The latest builds have added ~2kB to the size of this file (500 bytes zipped).

PR Close #19702
2017-11-01 15:24:46 -07:00
83c826c3f9 build(aio): remove hack to modify CLI version (#19702)
PR Close #19702
2017-11-01 15:24:46 -07:00
abfc41d661 build(aio): upgrade to Angular@rc.5 and CLI@rc.3 (#19702)
PR Close #19702
2017-11-01 15:24:45 -07:00
11fd7eaf63 build(aio): revert temporary increase in size limit (#19702)
PR Close #19702
2017-11-01 15:24:45 -07:00
54eba606cb build(aio): fix tests to work with @angular/{material,cdk}@2.0.0-beta.12 (#19702)
PR Close #19702
2017-11-01 15:24:45 -07:00
3337865913 build(aio): remove cli patches (#19702)
PR Close #19702
2017-11-01 15:24:45 -07:00
f24397c5d0 build(aio): revert to clean CLI test.ts file (#19702)
The use of `System.import()` in test.ts was causing the webpack build to fail
with a mysterious "Module build failed: Error: TypeScript compilation failed" error,
when running `yarn test`.

PR Close #19702
2017-11-01 15:24:44 -07:00
9d52bf27de build(aio): temporarily increaze the size limit until the regressions are fixed (#19702)
related issues:
https://github.com/angular/angular/issues/19857
https://github.com/angular/devkit/pull/231

PR Close #19702
2017-11-01 15:24:44 -07:00
19fbfbc371 build(aio): disable 'global' support in webpack (#19702)
This will be fixed in CLI once https://github.com/angular/angular-cli/pull/8130 lands.

-rw-r--r--  1 iminar  eng   14942 Oct 20 22:23 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 20 22:23 dist/inline.5d66b81ec9e01af9d28d.bundle.js
-rw-r--r--  1 iminar  eng  528395 Oct 20 22:23 dist/main.e36bb99245ca52ae546f.bundle.js
-rw-r--r--  1 iminar  eng   37205 Oct 20 22:23 dist/polyfills.0dfca732c5a075c110d0.bundle.js

PR Close #19702
2017-11-01 15:24:44 -07:00
6578b30b77 build(aio): upgrade to build-optimizer@0.0.29 (#19702)
-rw-r--r--  1 iminar  eng   14942 Oct 20 22:16 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 20 22:16 dist/inline.68ebcf831dc9c905804f.bundle.js
-rw-r--r--  1 iminar  eng  541291 Oct 20 22:16 dist/main.5ec6fb5f95fc0433d822.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 20 22:16 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:44 -07:00
10c1c2ba5a build(aio): upgrade to @angular/cli@1.5.0-rc.2 (#19702)
-rw-r--r--  1 iminar  eng   84219 Oct 19 21:37 dist/0.0f327734d18211139822.chunk.js
-rw-r--r--  1 iminar  eng   14942 Oct 19 21:37 dist/4.c719ac5645940382cdce.chunk.js
-rw-r--r--  1 iminar  eng    1560 Oct 19 21:37 dist/inline.887757679ff553e20b54.bundle.js
-rw-r--r--  1 iminar  eng  492354 Oct 19 21:37 dist/main.5e5dc9ed980c9f5dc2bd.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 19 21:37 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:44 -07:00
e18bfd1f3d build(aio): upgrade to @angular/cli@1.5.0-rc.1 (#19702)
-rw-r--r--  1 iminar  eng   84219 Oct 18 21:05 dist/0.0f327734d18211139822.chunk.js
-rw-r--r--  1 iminar  eng   14942 Oct 18 21:05 dist/4.c719ac5645940382cdce.chunk.js
-rw-r--r--  1 iminar  eng    1560 Oct 18 21:05 dist/inline.887757679ff553e20b54.bundle.js
-rw-r--r--  1 iminar  eng  492354 Oct 18 21:05 dist/main.5e5dc9ed980c9f5dc2bd.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 18 21:05 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:43 -07:00
3c2ddbe9ab build(aio): upgrade to rxjs@5.5.0 (#19702)
-rw-r--r--  1 iminar  eng   84219 Oct 18 09:13 dist/0.8ef208c27531d5c6af63.chunk.js
-rw-r--r--  1 iminar  eng   14942 Oct 18 09:13 dist/4.c719ac5645940382cdce.chunk.js
-rw-r--r--  1 iminar  eng    1560 Oct 18 09:13 dist/inline.adc367eb50c706f3fd04.bundle.js
-rw-r--r--  1 iminar  eng  492354 Oct 18 09:13 dist/main.b9d9549455c74aff1480.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 18 09:13 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:43 -07:00
a149605c9f build(aio): turn off preserveWhitespaces in compiler options (#19702)
-rw-r--r--  1 iminar  eng   14942 Oct 13 16:12 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 16:12 dist/inline.eede8140efeab4c45b22.bundle.js
-rw-r--r--  1 iminar  eng  559389 Oct 13 16:12 dist/main.20858f9aa7cf8741b6aa.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 16:12 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:43 -07:00
b396029d39 build(aio): patch @angular/cli to use esm builds of rxjs (#19702)
-rw-r--r--  1 iminar  eng   14942 Oct 13 14:52 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 14:52 dist/inline.6ca24f1c3b848103b041.bundle.js
-rw-r--r--  1 iminar  eng  567802 Oct 13 14:52 dist/main.c8183a2c0116782ca366.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 14:52 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:43 -07:00
040b376052 build(aio): upgrade to rxjs@5.5.0-beta.7 (#19702)
-rw-r--r--  1 iminar  eng   14942 Oct 13 14:30 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 14:30 dist/inline.702d6ff5146ddc373f05.bundle.js
-rw-r--r--  1 iminar  eng  588943 Oct 13 14:30 dist/main.64c96d55a10c56cfd6cd.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 14:30 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:42 -07:00
175c872514 build(aio): upgrade to @angular/{material,cdk}@2.0.0-beta.12 (#19702)
-rw-r--r--  1 iminar  eng   14942 Oct 13 13:35 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 13:35 dist/inline.f005f1bd6803b72f5961.bundle.js
-rw-r--r--  1 iminar  eng  582527 Oct 13 13:35 dist/main.b9ef1abb785be8de15b8.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 13:35 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:42 -07:00
71291aa2c0 fix(aio): hand fix the renaming md->mat issues (#19702)
These are changes that the mat-switcher missed and I had to make them by hand.

PR Close #19702
2017-11-01 15:24:42 -07:00
415e75716a build(aio): upgrade to @angular/{material,cdk}@2.0.0-beta.11 + md->mat migration (#19702)
all the non-npm changes were made by the angular-material-prefix-updater tool.

the tool missed a few things, which I'll fix in a separate commit to preserve the diff.

-rw-r--r--  1 iminar  eng   14942 Oct 13 13:09 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 13:09 dist/inline.0592c25ceb544d6aca3d.bundle.js
-rw-r--r--  1 iminar  eng  578250 Oct 13 13:09 dist/main.45d4edca3facc6d621e7.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 13:09 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:42 -07:00
3216abee2e build(aio): upgrade to @angular/cli@1.5.0-rc.0 and typescript@2.5.3 (#19702)
the size regression has gotten worse:

-rw-r--r--  1 iminar  eng   14942 Oct 13 12:24 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 12:24 dist/inline.41e701c562960ede8ef5.bundle.js
-rw-r--r--  1 iminar  eng  865780 Oct 13 12:24 dist/main.6c4c605d461870b9c2d7.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 12:24 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:41 -07:00
327ad02a28 build(aio): upgrade to @angular/cli@1.4.7 (#19702)
this causes the size regression to get only worse:

-rw-r--r--  1 iminar  eng   14942 Oct 13 10:37 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 10:37 dist/inline.3cc964095cb25e329dc0.bundle.js
-rw-r--r--  1 iminar  eng  846141 Oct 13 10:37 dist/main.5eb64df77b2877327a16.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 10:37 dist/polyfills.965a9a5ad3e11b17f85e.bundle.js

PR Close #19702
2017-11-01 15:24:41 -07:00
1df0c9e1b0 build(aio): update to @angular/core@5.0.0-rc.2 (#19702)
there is a size regression right now because the CLI is out of date:

-rw-r--r--  1 iminar  eng   14942 Oct 13 10:23 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 10:23 dist/inline.812a4af83ecce165c71c.bundle.js
-rw-r--r--  1 iminar  eng  643481 Oct 13 10:23 dist/main.74550bb35f9f5a57e78a.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 10:23 dist/polyfills.965a9a5ad3e11b17f85e.bundle.js

PR Close #19702
2017-11-01 15:24:41 -07:00
280dadaaad docs(aio): fix typo in attribute-directives
Closes #20071
2017-11-01 15:21:09 -07:00
c30eff898a docs(aio): update universal for CLI
PR Close #20039
2017-11-01 15:18:43 -07:00
ca129ba549 docs(aio): upgrade to v5 and cli 1.5
PR Close #20077
2017-11-01 14:14:08 -07:00
171ae154c2 docs(packaging): fix typo
PR Close #18915
2017-11-01 15:45:58 -04:00
4426c0f14e fix(docs): Fix 404 on amcdnl image
PR Close #19120
2017-11-01 15:45:28 -04:00
901436e46f docs: fix link texts
Fixes #19701

PR Close #19709
2017-11-01 15:44:59 -04:00
6fbc2b3be0 docs(aio): update AngularJS Quick Reference guide
PR Close #19939
2017-11-01 15:44:35 -04:00
93d23ddcc8 docs(aio): fix typo
PR Close #20029
2017-11-01 15:42:25 -04:00
548a809a05 docs(aio): fix typo
PR Close #20042
2017-11-01 15:41:57 -04:00
a161b4ab6d docs(aio): fix typo
PR Close #20069
2017-11-01 15:41:27 -04:00
60b91656cd docs: add changelog for 5.0.0 2017-11-01 11:00:39 -07:00
1858d99559 release: cut the 5.0.0 release 2017-11-01 09:47:02 -07:00
3a8665409d build(aio): add i18n boilerplate type (#20004)
PR Close #20004
2017-10-31 01:06:25 -04:00
005a78bd83 refactor: allow compilation with TypeScript 2.5 (#19966)
A small number of types need to be adjusted. The changes seem to be
backwards compatible with TS 2.4.

PR Close #19966
2017-10-31 00:26:43 -04:00
7553ce9dfe docs: add changelog for 5.0.0-rc.9 2017-10-30 21:24:59 -07:00
42bfe4477f release: cut the 5.0.0-rc.9 release 2017-10-30 21:24:54 -07:00
3bdbb18c8b build: update rollup lint rule from bad merge (#20047)
PR Close #20047
2017-10-30 23:43:49 -04:00
13f8648a00 fix: add missing globals from each rollup configuration (#20028)
PR Close #20028
2017-10-30 23:09:17 -04:00
b6abcb2500 refactor: make all rollup config ES5 compatible (#20028)
So they can be required by other Node scripts.

PR Close #20028
2017-10-30 23:09:17 -04:00
f1a9e1e361 build: add lint rule for global flags in rollup config (#20028)
We now verify that every imports is part of the globals defined in the files rollup.config.js.

PR Close #20028
2017-10-30 23:09:16 -04:00
54480f7dfc fix(compiler): report errors properly in G3 in certain conditions (#20041)
Condition: static analysis error, given:
- noResolve:true
- generateCodeForLibraries: false
- CompilerHost.getSourceFile throws on non existent files

All of these are true in G3.
PR Close #20041
2017-10-30 21:24:30 -04:00
951bd33b09 build: add release as a valid commit message subject (#19955)
PR Close #19955
2017-10-30 21:23:18 -04:00
bf57df3e04 build: temporarily increase the commit msg limit to 120
Right now HEAD and 5.0.x have a branch deviation and therefore
all the commits between both branches are being compared. There
exists a problematic commit which has a commit message that is
longer than 100 commits. This patch will temporarily increase
the limit to 120 so that CI passes. Once master is resumed to
being the primary development branch (once 5.0.0 is out) then
the the msg limit will be set back to 100.
2017-10-30 21:20:43 -04:00
420852e2f5 fix(compiler): reexport less symbols in .ngfactory.ts files (#19884)
* don't reexport symbols that the user already reexported
* never reexport symbols that are part of arguments of non simple function calls

Fixes #19883

PR Close #19884
2017-10-30 20:11:29 -04:00
04eb80cc2b fix(compiler): always use relative paths to refer to generated code
Previously we generated imports like `@angular/material/index.ngfactory`,
which doesn’t make sense as we don’t ship generated code on npm

Closes #20031
2017-10-30 18:28:25 -04:00
308fc8e328 docs: add changelog for 5.0.0-rc.8 2017-10-27 23:46:02 -07:00
9bb2939d68 release: cut the 5.0.0-rc.8 release 2017-10-27 23:45:52 -07:00
2f63899be2 docs: ensure examples get rxjs ^5.5.0 (#19985)
This change coincidentally updates other packages that were in `package.json`
because it regenerates `yarn.lock`. This too should be fine.

PR Close #19985
2017-10-27 22:27:27 -07:00
22c66f0e02 fix(compiler-cli): avoid producing source mappings for host views (#19965)
The host view doesn't map back to user code so the template compiler
produces a blank `url` for them.

PR Close #19965
2017-10-27 22:26:57 -07:00
00bc80bb37 fix(platform-server): add missing packages to the UMD global rollup config
This adds the proper bindings for calling angular packages from platform-server in the UMD.
This was not a problem for universal apps that dont use UMD.

Fixes 19899
2017-10-27 22:26:25 -07:00
394d951883 docs: add changelog for 5.0.0-rc.7 2017-10-26 19:47:31 -07:00
98b26381f6 release: cut the 5.0.0-rc.7 release 2017-10-26 19:47:24 -07:00
31797d3b50 fix(compiler): make watch mode work on windows (#19953)
Fixes #19951
PR Close #19953
2017-10-26 18:43:00 -04:00
957be960d2 fix(compiler): recover from structural errors in watch mode (#19953)
This also changes the compiler so that we throw less often
on structural changes and produce a meaningful state
in the `ng.Program` in case of errors.

Related to #19951

PR Close #19953
2017-10-26 18:43:00 -04:00
18e9d86a3b fix(compiler): translate emit diagnostics with noEmitOnError: true. (#19953)
This prevents errors reported against `.ngfactory.ts` files show up
as the result of running `ngc`.

Closes #19935
PR Close #19953
2017-10-26 18:42:59 -04:00
a869aeecd2 fix(compiler): don’t store invalid state when using listLazyRoutes (#19953)
Previously, `listLazyRoute` would store invalid information in a compiler
internal cache, which lead to incorrect paths that were used during emit.
This commit fixes this.

PR Close #19953
2017-10-26 18:42:59 -04:00
eca822b756 fix(service-worker): fix improper call of Observable.merge (#19962)
Observable.merge was called using .call() as if it were an operator
and not an Observable factory. This removes the .call() and uses
the factory properly.

PR Close #19962
2017-10-26 18:09:53 -04:00
17142a778a fix(service-worker): don't block initialization on registration (#19936)
Importing ServiceWorkerModule.register() will schedule registration of
the Service Worker inside an APP_INITIALIZER. Previously, the Promise
returned by navigator.serviceWorker.register() was returned from the
initializer function. This has the unwanted side effect of blocking
initialization until the SW is registered. Even worse, if the SW script
fails to load, this can cause the app initialization to fail.

The solution is to not return the registration promise from the
initializer function, essentially decoupling registration from the rest
of the initialization flow.

This change is not unit testable as there are no mocks/adapters yet for
navigator.serviceWorker. A future integration test should cover this case
with better fidelity.

PR Close #19936
2017-10-26 16:05:07 -04:00
5adb7c9669 fix(service-worker): listen for messages on the right event source (#19954)
Currently, the SwUpdate service doesn't receive messages from the SW.
This is because it attempts to subscribe to the 'message' event on
ServiceWorkerRegistration, when really messages are emitted by the
ServiceWorkerContainer.

This change moves to listening on ServiceWorkerContainer and changes
the mocks to reflect the way the browser actually works.

PR Close #19954
2017-10-26 16:04:59 -04:00
703fcda590 docs: add changelog for 5.0.0-rc.6 2017-10-25 14:49:27 -07:00
f16a7cd7e3 release: cut the 5.0.0-rc.6 release 2017-10-25 14:48:41 -07:00
fde5f2fa14 build: update to rxjs@5.5.2 (#19931)
PR Close #19931
2017-10-25 14:27:05 -04:00
d56724659f fix(compiler): automatically set emitDecoratorMetadata when "annotationsAs": "static fields” (#19927)
This is a workaround for https://github.com/angular/tsickle/issues/635.

Fixes #19916
PR Close #19927
2017-10-25 13:38:39 -04:00
56b18ff063 fix(compiler-cli): produce correct paths for windows output (#19915)
The path mapping was broken for Windows by fc0b1d5b61.
Fixed the path mapping and put code in place to make such a problem
to sneek by again.

PR Close #19915
2017-10-24 17:54:58 -04:00
7bfeac746e fix(compiler-cli): only use error collector when needed. (#19912)
The error collector changes behavior of the metadata resolver
in ways that haven't been fully hardened. This changes limits
its use to the lazy route detection and the language service.

Issue: #19906

PR Close #19912
2017-10-24 17:06:41 -04:00
c92efc15fb fix(compiler): don’t type check templates with skipTemplateCodegen (#19909)
This change is needed to prevent users’ builds from breaking.

If a user sets `fullTemlateTypeCheck` to true, we will
continue to check the templates even when `skipTemplateCodegen` is true
as well.

Related to #19906

PR Close #19909
2017-10-24 17:06:34 -04:00
b51d57deb8 docs: add changelog for 5.0.0-rc.5 2017-10-23 23:50:31 -07:00
c357b40dca release: cut the 5.0.0-rc.5 release 2017-10-23 23:50:25 -07:00
a0ae120093 fix(compiler-cli): report all diagnostic error messages (#19886)
This fixes a problem introduced in 8d45fefc31
which modified how diagnostic error messages are reported for structural
metadata errors causing some of the diagnostics to be lost.

PR Close #19886
2017-10-23 22:41:10 -04:00
d3211a2468 feat(router): add "onSameUrlNavigation" router configuration option (#19463)
PR Close #19463
2017-10-23 20:56:53 -04:00
adab4f3e49 fix(router): do not call location.go when skipping a navigation (#19463)
Closes #18036

PR Close #19463
2017-10-23 20:56:52 -04:00
82fed62af2 fix(router): navigating to the current location works (#19463)
Closes #13340

PR Close #19463
2017-10-23 20:56:52 -04:00
1013 changed files with 26205 additions and 4599 deletions

View File

@ -1,18 +0,0 @@
# Make compilation fast, by keeping a few copies of the compilers
# running as daemons, and cache SourceFile AST's to reduce parse time.
build --strategy=TypeScriptCompile=worker
build --strategy=AngularTemplateCompile=worker
# Don't create bazel-* symlinks in the WORKSPACE directory.
# These require .gitignore and may scare users.
# Also, it's a workaround for https://github.com/bazelbuild/rules_typescript/issues/12
# which affects the common case of having `tsconfig.json` in the WORKSPACE directory.
#
# Instead, you should run `bazel info bazel-bin` to find out where the outputs went.
build --symlink_prefix=/
# Performance: avoid stat'ing input files
build --watchfs
# Don't print all the .d.ts output locations after builds
build --show_result=0

View File

@ -7,17 +7,25 @@
# To validate changes, use an online parser, eg.
# http://yaml-online-parser.appspot.com/
# Variables
## IMPORTANT
# If you change the `docker_image` version, also change the `cache_key` suffix and the version of
# `com_github_bazelbuild_buildtools` in the `/WORKSPACE` file.
var_1: &docker_image angular/ngcontainer:0.1.0
var_2: &cache_key angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.1.0
# Settings common to each job
anchor_1: &job_defaults
working_directory: ~/ng
docker:
- image: angular/ngcontainer:0.0.2
- image: *docker_image
# After checkout, rebase on top of master.
# Similar to travis behavior, but not quite the same.
# See https://discuss.circleci.com/t/1662
anchor_2: &post_checkout
post: git pull --ff-only origin "refs/pull/${CI_PULL_REQUEST//*pull\//}/merge"
post: git pull --ff-only origin "refs/pull/${CIRCLE_PULL_REQUEST//*pull\//}/merge"
version: 2
jobs:
@ -26,25 +34,38 @@ jobs:
steps:
- checkout:
<<: *post_checkout
# Check BUILD.bazel formatting before we have a node_modules directory
# Then we don't need any exclude pattern to avoid checking those files
- run: 'buildifier -mode=check $(find . -type f \( -name BUILD.bazel -or -name BUILD \)) ||
(echo "BUILD files not formatted. Please run ''yarn buildifier''" ; exit 1)'
# Run the skylark linter to check our Bazel rules
- run: 'find . -type f -name "*.bzl" |
xargs java -jar /usr/local/bin/Skylint_deploy.jar ||
(echo -e "\n.bzl files have lint errors. Please run ''yarn skylint''"; exit 1)'
- restore_cache:
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
key: *cache_key
- run: yarn install --frozen-lockfile --non-interactive
- run: ./node_modules/.bin/gulp lint
build:
<<: *job_defaults
resource_class: large
steps:
- checkout:
<<: *post_checkout
- restore_cache:
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
key: *cache_key
- run: bazel info release
- run: bazel run @yarn//:yarn
- run: bazel build packages/...
- run: bazel test @angular//...
# Use bazel query so that we explicitly ask for all buildable targets to be built as well
# This avoids waiting for a build command to finish before running the first test
# See https://github.com/bazelbuild/bazel/issues/4257
- run: bazel query --output=label '//packages/... union @angular//...' | xargs bazel test --config=ci
- save_cache:
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
key: *cache_key
paths:
- "node_modules"

2
.nvmrc
View File

@ -1 +1 @@
6.9.5
8.9

View File

@ -15,7 +15,6 @@
# hansl - Hans Larsen
# IgorMinar - Igor Minar
# jasonaden - Jason Aden
# juleskremer - Jules Kremer
# kara - Kara Erickson
# matsko - Matias Niemelä
# mhevery - Misko Hevery
@ -25,7 +24,6 @@
# tinayuangao - Tina Gao
# vicb - Victor Berchet
# vikerman - Vikram Subramanian
# wardbell - Ward Bell
version: 2
@ -36,14 +34,33 @@ group_defaults:
enabled: true
approve_by_comment:
enabled: false
# see http://docs.pullapprove.com/groups/author_approval/
author_approval:
# If the author is a reviewer on the PR, they will automatically have an "approved" status.
auto: true
groups:
# Require all PRs to have at least one approval from *someone*
all:
users: all
required: 1
# In this group, your self-approval does not count
author_approval:
auto: false
ignored: true
files:
include:
- "*"
root:
conditions:
files:
include:
- "*"
exclude:
- "WORKSPACE"
- "BUILD.bazel"
- ".circleci/*"
- "aio/*"
- "integration/*"
- "modules/*"
@ -70,9 +87,11 @@ groups:
- "*.bazel"
- "*.bzl"
- "packages/bazel/*"
- "tools/bazel.rc"
users:
- alexeagle #primary
- chuckjaz
- IgorMinar
- vikerman #fallback
build-and-ci:
@ -84,6 +103,7 @@ groups:
- "*.lock"
- "tools/*"
exclude:
- "tools/bazel.rc"
- "tools/public_api_guard/*"
- "aio/*"
users:
@ -279,7 +299,7 @@ groups:
files:
- "packages/benchpress/*"
users:
- alxhub #primary
- alxhub # primary
# needs secondary
- IgorMinar #fallback
- mhevery #fallback
@ -307,10 +327,8 @@ groups:
- "aio/content/navigation.json"
- "aio/content/license.md"
users:
- juleskremer #primary
- Foxandxss
- stephenfluin
- wardbell
- Foxandxss
- petebacondarwin
- gkalpak
- IgorMinar #fallback
@ -324,7 +342,6 @@ groups:
- "aio/content/navigation.json"
- "aio/content/license.md"
users:
- juleskremer #primary
- stephenfluin
- petebacondarwin
- gkalpak

View File

@ -1,8 +1,10 @@
language: node_js
sudo: false
# Work-around for https://github.com/travis-ci/travis-ci/issues/8836#issuecomment-356362524.
# (Restore `sudo: false` once that is resolved.)
sudo: required
dist: trusty
node_js:
- '6.9.5'
- '8.9.1'
addons:
# firefox: "38.0"
@ -54,7 +56,6 @@ env:
- CI_MODE=browserstack_optional
- CI_MODE=aio_tools_test
- CI_MODE=aio
- CI_MODE=aio_optional
- CI_MODE=aio_e2e AIO_SHARD=0
- CI_MODE=aio_e2e AIO_SHARD=1
- CI_MODE=bazel
@ -64,7 +65,6 @@ matrix:
allow_failures:
- env: "CI_MODE=saucelabs_optional"
- env: "CI_MODE=browserstack_optional"
- env: "CI_MODE=aio_optional"
before_install:
# source the env.sh script so that the exported variables are available to other scripts later on

View File

@ -1,5 +1,8 @@
package(default_visibility = ["//visibility:public"])
exports_files(["tsconfig.json"])
exports_files([
"tsconfig.json",
])
# This rule belongs in node_modules/BUILD
# It's here as a workaround for
@ -11,17 +14,25 @@ filegroup(
# bazel query "deps(:node_modules)" | wc -l
# This won't scale in the general case.
# TODO(alexeagle): figure out what to do
srcs = glob(["/".join(["node_modules", pkg, "**", ext]) for pkg in [
srcs = glob(["/".join([
"node_modules",
pkg,
"**",
ext,
]) for pkg in [
"jasmine",
"typescript",
"zone.js",
"rxjs",
"@types",
"tsutils",
"@types/jasmine",
"@types/node",
"@types/source-map",
"tsickle",
"hammerjs",
"protobufjs",
"bytebuffer",
"reflect-metadata",
"source-map-support",
"minimist",
] for ext in [
"*.js",
@ -29,3 +40,26 @@ filegroup(
"*.d.ts",
]]),
)
filegroup(
name = "web_test_bootstrap_scripts",
# do not sort
srcs = [
"//:node_modules/reflect-metadata/Reflect.js",
"//:node_modules/zone.js/dist/zone.js",
"//:node_modules/zone.js/dist/async-test.js",
"//:node_modules/zone.js/dist/sync-test.js",
"//:node_modules/zone.js/dist/fake-async-test.js",
"//:node_modules/zone.js/dist/proxy.js",
"//:node_modules/zone.js/dist/jasmine-patch.js",
],
)
filegroup(
name = "angularjs",
# do not sort
srcs = [
"//:node_modules/angular/angular.js",
"//:node_modules/angular-mocks/angular-mocks.js",
],
)

View File

@ -1,3 +1,178 @@
<a name="5.2.0"></a>
# [5.2.0](https://github.com/angular/angular/compare/5.2.0-rc.0...5.2.0) (2018-01-10)
### Bug Fixes
* **bazel:** Give correct module names for ES6 output ([#21320](https://github.com/angular/angular/issues/21320)) ([9728dce](https://github.com/angular/angular/commit/9728dce)), closes [#21022](https://github.com/angular/angular/issues/21022)
* **benchpress:** forward compat with selenium_webdriver 3.6.0 ([#21399](https://github.com/angular/angular/issues/21399)) ([6040ee3](https://github.com/angular/angular/commit/6040ee3))
* **benchpress:** work around missing events from Chrome 63 ([#21396](https://github.com/angular/angular/issues/21396)) ([fa03ae1](https://github.com/angular/angular/commit/fa03ae1))
* **common:** export currencies via `getCurrencySymbol` ([#20983](https://github.com/angular/angular/issues/20983)) ([fecf768](https://github.com/angular/angular/commit/fecf768))
<a name="5.2.0-rc.0"></a>
# [5.2.0-rc.0](https://github.com/angular/angular/compare/5.2.0-beta.1...5.2.0-rc.0) (2018-01-04)
### Bug Fixes
* **animations:** avoid infinite loop with multiple blocked sub triggers ([#21119](https://github.com/angular/angular/issues/21119)) ([86a36ea](https://github.com/angular/angular/commit/86a36ea))
* **animations:** renaming issue with DOMAnimation. ([#21125](https://github.com/angular/angular/issues/21125)) ([871ece6](https://github.com/angular/angular/commit/871ece6))
* **common:** handle JS floating point errors in percent pipe ([#20329](https://github.com/angular/angular/issues/20329)) ([07b81ae](https://github.com/angular/angular/commit/07b81ae)), closes [#20136](https://github.com/angular/angular/issues/20136)
* **language-service:** ignore null metadatas ([#20557](https://github.com/angular/angular/issues/20557)) ([3e47ea2](https://github.com/angular/angular/commit/3e47ea2)), closes [#20260](https://github.com/angular/angular/issues/20260)
* **router:** fix wildcard route with lazy loaded module (again) ([#18139](https://github.com/angular/angular/issues/18139)) ([5ba1cf1](https://github.com/angular/angular/commit/5ba1cf1)), closes [#13848](https://github.com/angular/angular/issues/13848)
<a name="5.1.3"></a>
## [5.1.3](https://github.com/angular/angular/compare/5.1.2...5.1.3) (2018-01-03)
### Bug Fixes
* **animations:** avoid infinite loop with multiple blocked sub triggers ([#21119](https://github.com/angular/angular/issues/21119)) ([3e34fa8](https://github.com/angular/angular/commit/3e34fa8))
* **animations:** renaming issue with DOMAnimation. ([#21125](https://github.com/angular/angular/issues/21125)) ([d1f4500](https://github.com/angular/angular/commit/d1f4500))
* **common:** handle JS floating point errors in percent pipe ([#20329](https://github.com/angular/angular/issues/20329)) ([fa0e8ef](https://github.com/angular/angular/commit/fa0e8ef)), closes [#20136](https://github.com/angular/angular/issues/20136)
* **language-service:** ignore null metadatas ([#20557](https://github.com/angular/angular/issues/20557)) ([48a1f32](https://github.com/angular/angular/commit/48a1f32)), closes [#20260](https://github.com/angular/angular/issues/20260)
* **router:** fix wildcard route with lazy loaded module (again) ([#18139](https://github.com/angular/angular/issues/18139)) ([8c99175](https://github.com/angular/angular/commit/8c99175)), closes [#13848](https://github.com/angular/angular/issues/13848)
<a name="5.2.0-beta.1"></a>
# [5.2.0-beta.1](https://github.com/angular/angular/compare/5.2.0-beta.0...5.2.0-beta.1) (2017-12-20)
### Bug Fixes
* **compiler:** generate the correct imports for summary type-check ([d91ff17](https://github.com/angular/angular/commit/d91ff17))
* **forms:** avoid producing an error with hostBindingTypeCheck ([d213a20](https://github.com/angular/angular/commit/d213a20))
### Features
* **compiler:** allow ngIf to use the ngIf expression directly as a guard ([82bcd83](https://github.com/angular/angular/commit/82bcd83))
* **router:** add "paramsInheritanceStrategy" router configuration option ([5efea2f](https://github.com/angular/angular/commit/5efea2f)), closes [#20572](https://github.com/angular/angular/issues/20572)
<a name="5.1.2"></a>
## [5.1.2](https://github.com/angular/angular/compare/5.1.1...5.1.2) (2017-12-20)
### Bug Fixes
* **common:** fix a Closure compilation issue. ([267ebf3](https://github.com/angular/angular/commit/267ebf3))
* **compiler:** make tsx file aot compatible ([756dd34](https://github.com/angular/angular/commit/756dd34)), closes [#20555](https://github.com/angular/angular/issues/20555)
* **compiler:** report an error for recursive module references ([ced575f](https://github.com/angular/angular/commit/ced575f))
* **compiler-cli:** do not emit invalid .metadata.json files ([a1d4c2d](https://github.com/angular/angular/commit/a1d4c2d))
* **compiler-cli:** do not force type checking on .js files ([3b63e16](https://github.com/angular/angular/commit/3b63e16))
* **service-worker:** check for updates on navigation ([a33182c](https://github.com/angular/angular/commit/a33182c)), closes [#20877](https://github.com/angular/angular/issues/20877)
* **upgrade:** replaces get/setAngularLib with get/setAngularJSGlobal ([66cc2fa](https://github.com/angular/angular/commit/66cc2fa))
<a name="5.2.0-beta.0"></a>
# [5.2.0-beta.0](https://github.com/angular/angular/compare/5.1.0...5.2.0-beta.0) (2017-12-13)
### Features
* **animations:** re-introduce support for transition matching functions ([#20723](https://github.com/angular/angular/issues/20723)) ([590d93b](https://github.com/angular/angular/commit/590d93b)), closes [#18959](https://github.com/angular/angular/issues/18959)
* **compiler:** add a pseudo $any() function to disable type checking ([#20876](https://github.com/angular/angular/issues/20876)) ([70cd124](https://github.com/angular/angular/commit/70cd124))
* **compiler:** narrow types of expressions used in *ngIf ([#20702](https://github.com/angular/angular/issues/20702)) ([e7d9cb3](https://github.com/angular/angular/commit/e7d9cb3))
* **core:** add source to `StaticInjectorError` message ([#20817](https://github.com/angular/angular/issues/20817)) ([b7738e1](https://github.com/angular/angular/commit/b7738e1)), closes [#19302](https://github.com/angular/angular/issues/19302)
* **forms:** allow nulls on setAsyncValidators ([#20327](https://github.com/angular/angular/issues/20327)) ([d41d2c4](https://github.com/angular/angular/commit/d41d2c4)), closes [#20296](https://github.com/angular/angular/issues/20296)
<a name="5.1.1"></a>
## [5.1.1](https://github.com/angular/angular/compare/5.1.0...5.1.1) (2017-12-13)
### Bug Fixes
* **animations:** ensure multi-level route leave animations are queryable ([#20787](https://github.com/angular/angular/issues/20787)) ([d09d497](https://github.com/angular/angular/commit/d09d497)), closes [#19807](https://github.com/angular/angular/issues/19807)
* **animations:** ensure the web-animations driver properly handles empty keyframes ([#20648](https://github.com/angular/angular/issues/20648)) ([c3e8731](https://github.com/angular/angular/commit/c3e8731)), closes [#15858](https://github.com/angular/angular/issues/15858)
* **animations:** properly recover and cleanup DOM when CD failures occur ([#20719](https://github.com/angular/angular/issues/20719)) ([e6a2805](https://github.com/angular/angular/commit/e6a2805)), closes [#19093](https://github.com/angular/angular/issues/19093)
* **animations:** support webkit-based vendor prefixes for prop validations ([#19055](https://github.com/angular/angular/issues/19055)) ([501f01e](https://github.com/angular/angular/commit/501f01e)), closes [#18921](https://github.com/angular/angular/issues/18921)
* **bazel:** don't equate moduleName with fileName ([#20895](https://github.com/angular/angular/issues/20895)) ([0c9f7b0](https://github.com/angular/angular/commit/0c9f7b0))
* **compiler:** support referencing enums in namespaces ([#20947](https://github.com/angular/angular/issues/20947)) ([d6da798](https://github.com/angular/angular/commit/d6da798)), closes [#18170](https://github.com/angular/angular/issues/18170)
* **compiler-cli:** disable checkTypes in emit. ([#20828](https://github.com/angular/angular/issues/20828)) ([160a154](https://github.com/angular/angular/commit/160a154))
* **compiler-cli:** fix swallowed Error messages ([#20846](https://github.com/angular/angular/issues/20846)) ([6727336](https://github.com/angular/angular/commit/6727336))
* **compiler-cli:** merge [@fileoverview](https://github.com/fileoverview) comments. ([#20870](https://github.com/angular/angular/issues/20870)) ([be9a737](https://github.com/angular/angular/commit/be9a737))
* **router:** NavigationError and NavigationCancel should be emitted after resetting the URL ([#20803](https://github.com/angular/angular/issues/20803)) ([baeec4d](https://github.com/angular/angular/commit/baeec4d))
<a name="5.1.0"></a>
# [5.1.0](https://github.com/angular/angular/compare/5.1.0-rc.1...5.1.0) (2017-12-06)
### Bug Fixes
* **animations:** ensure DOM is cleaned up after multiple [@trigger](https://github.com/trigger) leave animations finish ([#20740](https://github.com/angular/angular/issues/20740)) ([b78ada1](https://github.com/angular/angular/commit/b78ada1)), closes [#20541](https://github.com/angular/angular/issues/20541)
* **service-worker:** initialize in browser only ([#20782](https://github.com/angular/angular/issues/20782)) ([7cabaa0](https://github.com/angular/angular/commit/7cabaa0)), closes [#20360](https://github.com/angular/angular/issues/20360)
* **service-worker:** esm2015 points to wrong path ([#20800](https://github.com/angular/angular/issues/20800)) ([da3563c](https://github.com/angular/angular/commit/da3563c))
<a name="5.1.0-rc.1"></a>
# [5.1.0-rc.1](https://github.com/angular/angular/compare/5.1.0-rc.0...5.1.0-rc.1) (2017-12-01)
### Bug Fixes
* **compiler-cli:** propagate ts.SourceFile moduleName into metadata ([f841fbe](https://github.com/angular/angular/commit/f841fbe))
* **service-worker:** allow disabling SW while still using services ([65f4fad](https://github.com/angular/angular/commit/65f4fad))
* **service-worker:** don't crash if SW not supported ([b9a91a5](https://github.com/angular/angular/commit/b9a91a5))
* **service-worker:** send initialization signal from the application ([3fbcde9](https://github.com/angular/angular/commit/3fbcde9))
* **service-worker:** use relative path for ngsw.json ([f582620](https://github.com/angular/angular/commit/f582620))
<a name="5.0.5"></a>
## [5.0.5](https://github.com/angular/angular/compare/5.0.4...5.0.5) (2017-12-01)
### Bug Fixes
* **compiler-cli:** propagate ts.SourceFile moduleName into metadata ([a2ff4ab](https://github.com/angular/angular/commit/a2ff4ab))
* **service-worker:** allow disabling SW while still using services ([f99335b](https://github.com/angular/angular/commit/f99335b))
* **service-worker:** don't crash if SW not supported ([ee37d4b](https://github.com/angular/angular/commit/ee37d4b))
* **service-worker:** send initialization signal from the application ([6bf07b4](https://github.com/angular/angular/commit/6bf07b4))
* **service-worker:** use relative path for ngsw.json ([56c98f7](https://github.com/angular/angular/commit/56c98f7))
<a name="5.1.0-rc.0"></a>
# [5.1.0-rc.0](https://github.com/angular/angular/compare/5.1.0-beta.2...5.1.0-rc.0) (2017-12-01)
### Bug Fixes
* **animations:** ensure multi-level enter animations work ([#19455](https://github.com/angular/angular/issues/19455)) ([dd6237e](https://github.com/angular/angular/commit/dd6237e))
* **animations:** ensure multi-level enter animations work ([#19455](https://github.com/angular/angular/issues/19455)) ([b2a586c](https://github.com/angular/angular/commit/b2a586c))
* **animations:** ensure multi-level leave animations work ([#19455](https://github.com/angular/angular/issues/19455)) ([1366762](https://github.com/angular/angular/commit/1366762))
* **animations:** ensure multi-level leave animations work ([#19455](https://github.com/angular/angular/issues/19455)) ([c2b3792](https://github.com/angular/angular/commit/c2b3792))
* **bazel:** produce named AMD modules for codegen ([#20547](https://github.com/angular/angular/issues/20547)) ([6e83204](https://github.com/angular/angular/commit/6e83204)), closes [#19422](https://github.com/angular/angular/issues/19422)
* **common:** accept falsy values as HTTP bodies ([#19958](https://github.com/angular/angular/issues/19958)) ([15a54df](https://github.com/angular/angular/commit/15a54df)), closes [#19825](https://github.com/angular/angular/issues/19825) [#19195](https://github.com/angular/angular/issues/19195)
* **common:** don't strip XSSI prefix for if error isn't JSON ([#19958](https://github.com/angular/angular/issues/19958)) ([aafa75d](https://github.com/angular/angular/commit/aafa75d))
* **common:** remove useless guard in HttpClient ([#19958](https://github.com/angular/angular/issues/19958)) ([eb01ad5](https://github.com/angular/angular/commit/eb01ad5)), closes [#19223](https://github.com/angular/angular/issues/19223)
* **common:** treat an empty body as null when parsing JSON in HttpClient ([#19958](https://github.com/angular/angular/issues/19958)) ([503be69](https://github.com/angular/angular/commit/503be69)), closes [#18680](https://github.com/angular/angular/issues/18680) [#19413](https://github.com/angular/angular/issues/19413) [#19502](https://github.com/angular/angular/issues/19502) [#19555](https://github.com/angular/angular/issues/19555)
* **compiler:** correctly detect when to serialze summary metadata ([#20668](https://github.com/angular/angular/issues/20668)) ([8bb42df](https://github.com/angular/angular/commit/8bb42df))
* **compiler-cli:** fix memory leak in program creation ([#20692](https://github.com/angular/angular/issues/20692)) ([71e5de6](https://github.com/angular/angular/commit/71e5de6)), closes [#20691](https://github.com/angular/angular/issues/20691)
* **compiler-cli:** normalize sourcepaths for i18n extracted files ([#20417](https://github.com/angular/angular/issues/20417)) ([de78307](https://github.com/angular/angular/commit/de78307)), closes [#20416](https://github.com/angular/angular/issues/20416)
* **core:** should use native addEventListener in ngZone ([#20672](https://github.com/angular/angular/issues/20672)) ([65a2cb8](https://github.com/angular/angular/commit/65a2cb8))
* **language-service:** Allow empty templates ([#20651](https://github.com/angular/angular/issues/20651)) ([3203069](https://github.com/angular/angular/commit/3203069)), closes [#19406](https://github.com/angular/angular/issues/19406)
* **language-service:** Fix crash when no script files are found ([#20550](https://github.com/angular/angular/issues/20550)) ([54bfe14](https://github.com/angular/angular/commit/54bfe14)), closes [#19325](https://github.com/angular/angular/issues/19325)
### Features
* **common:** add locale id parameter to `registerLocaleData` ([#20623](https://github.com/angular/angular/issues/20623)) ([24bf3e2](https://github.com/angular/angular/commit/24bf3e2))
* **compiler-cli:** improve error messages produced during structural errors ([#20459](https://github.com/angular/angular/issues/20459)) ([8ecda94](https://github.com/angular/angular/commit/8ecda94))
<a name="5.0.4"></a>
## [5.0.4](https://github.com/angular/angular/compare/5.0.3...5.0.4) (2017-12-01)
@ -15,6 +190,30 @@
<a name="5.1.0-beta.2"></a>
# [5.1.0-beta.2](https://github.com/angular/angular/compare/5.1.0-beta.1...5.1.0-beta.2) (2017-11-22)
### Bug Fixes
* **animations:** always fire inner trigger callbacks even if blocked by parent animations ([#19753](https://github.com/angular/angular/issues/19753)) ([0e012c9](https://github.com/angular/angular/commit/0e012c9)), closes [#19100](https://github.com/angular/angular/issues/19100)
* **animations:** always fire start and done callbacks in order for noop animations ([#20570](https://github.com/angular/angular/issues/20570)) ([ffb6dbe](https://github.com/angular/angular/commit/ffb6dbe))
* **animations:** validate against trigger() names that use @ symbols ([#20326](https://github.com/angular/angular/issues/20326)) ([1861e41](https://github.com/angular/angular/commit/1861e41))
* **benchpress:** Allow ignoring navigationStart events in perflog metric. ([#20312](https://github.com/angular/angular/issues/20312)) ([717ac5a](https://github.com/angular/angular/commit/717ac5a))
* **common:** return ISubscription from Location.subscribe() ([#20429](https://github.com/angular/angular/issues/20429)) ([437a044](https://github.com/angular/angular/commit/437a044)), closes [#20406](https://github.com/angular/angular/issues/20406)
* **compiler:** emit correct type-check-blocks with TemplateRef's ([#20463](https://github.com/angular/angular/issues/20463)) ([68b53c0](https://github.com/angular/angular/commit/68b53c0))
* **compiler:** support event bindings in `fullTemplateTypeCheck` ([#20490](https://github.com/angular/angular/issues/20490)) ([4ed0439](https://github.com/angular/angular/commit/4ed0439))
* **core:** fix [#20532](https://github.com/angular/angular/issues/20532), should be able to cancel listener from mixed zone ([#20538](https://github.com/angular/angular/issues/20538)) ([a740e4f](https://github.com/angular/angular/commit/a740e4f))
* **core:** should support event.stopImmediatePropagation ([#20469](https://github.com/angular/angular/issues/20469)) ([997336b](https://github.com/angular/angular/commit/997336b))
* **forms:** updateOn should check if change occurred ([#20358](https://github.com/angular/angular/issues/20358)) ([69c53c3](https://github.com/angular/angular/commit/69c53c3)), closes [#20259](https://github.com/angular/angular/issues/20259)
### Features
* **platform-browser-dynamic:** export `JitCompilerFactory` ([#20478](https://github.com/angular/angular/issues/20478)) ([d7a727c](https://github.com/angular/angular/commit/d7a727c)), closes [#20125](https://github.com/angular/angular/issues/20125)
<a name="5.0.3"></a>
## [5.0.3](https://github.com/angular/angular/compare/5.0.2...5.0.3) (2017-11-22)
@ -32,6 +231,28 @@
* **forms:** updateOn should check if change occurred ([#20358](https://github.com/angular/angular/issues/20358)) ([f9f2c20](https://github.com/angular/angular/commit/f9f2c20)), closes [#20259](https://github.com/angular/angular/issues/20259)
<a name="5.1.0-beta.1"></a>
# [5.1.0-beta.1](https://github.com/angular/angular/compare/5.1.0-beta.0...5.1.0-beta.1) (2017-11-16)
### Bug Fixes
* **animations:** always fire inner trigger callbacks even if blocked by parent animations ([#19753](https://github.com/angular/angular/issues/19753)) ([d47b2a6](https://github.com/angular/angular/commit/d47b2a6)), closes [#19100](https://github.com/angular/angular/issues/19100)
* **animations:** ensure final state() styles are applied within @.disabled animations ([#20267](https://github.com/angular/angular/issues/20267)) ([20aafff](https://github.com/angular/angular/commit/20aafff)), closes [#20266](https://github.com/angular/angular/issues/20266)
* **bazel:** adjust mock of tsconfig for ng_module rule unit test ([#20175](https://github.com/angular/angular/issues/20175)) ([c2a24b4](https://github.com/angular/angular/commit/c2a24b4))
* **compiler:** fix corner cases in shadow CSS ([c32f5fd](https://github.com/angular/angular/commit/c32f5fd))
* **compiler:** recognize @NgModule with a redundant @Injectable ([#20320](https://github.com/angular/angular/issues/20320)) ([c33a576](https://github.com/angular/angular/commit/c33a576))
* **compiler:** show explanatory text in template errors ([#20313](https://github.com/angular/angular/issues/20313)) ([3257fcd](https://github.com/angular/angular/commit/3257fcd))
* **core:** ensure init lifecycle events are called ([#20258](https://github.com/angular/angular/issues/20258)) ([24cf8b3](https://github.com/angular/angular/commit/24cf8b3))
* **language-service:** pass compilerOptions.paths to ReflectorHost ([#20222](https://github.com/angular/angular/issues/20222)) ([eb8013e](https://github.com/angular/angular/commit/eb8013e))
* **router:** 'merge' queryParamHandling strategy should be able to remove query params ([#19733](https://github.com/angular/angular/issues/19733)) ([a622e19](https://github.com/angular/angular/commit/a622e19)), closes [#18463](https://github.com/angular/angular/issues/18463) [#17202](https://github.com/angular/angular/issues/17202)
* Update test code to type-check under TS 2.5 ([#20175](https://github.com/angular/angular/issues/20175)) ([5ec1717](https://github.com/angular/angular/commit/5ec1717))
### Features
* **typescript:** support TypeScript 2.5 ([a9f3e2b](https://github.com/angular/angular/commit/a9f3e2b)), closes [#20175](https://github.com/angular/angular/issues/20175)
> Note, if you do `Injector.get(Token)` where `Token` has static members, you'll run into https://github.com/Microsoft/TypeScript/issues/20102 where the returned type is `{}` rather than `Token`. Use `Injector.get<Token>(Token)` to work around.
<a name="5.0.2"></a>
## [5.0.2](https://github.com/angular/angular/compare/5.0.1...5.0.2) (2017-11-16)
@ -46,6 +267,22 @@
* **router:** 'merge' queryParamHandling strategy should be able to remove query params ([#19733](https://github.com/angular/angular/issues/19733)) ([b732fb9](https://github.com/angular/angular/commit/b732fb9)), closes [#18463](https://github.com/angular/angular/issues/18463) [#17202](https://github.com/angular/angular/issues/17202)
<a name="5.1.0-beta.0"></a>
# [5.1.0-beta.0](https://github.com/angular/angular/compare/5.0.0-rc.4...5.1.0-beta.0) (2017-11-08)
### Bug Fixes
* **compiler:** don't overwrite missingTranslation's value in JIT ([#19952](https://github.com/angular/angular/issues/19952)) ([799cbb9](https://github.com/angular/angular/commit/799cbb9))
* **compiler:** report a reasonable error with invalid metadata ([#20062](https://github.com/angular/angular/issues/20062)) ([da22c48](https://github.com/angular/angular/commit/da22c48))
* **compiler-cli:** don't report emit diagnostics when `--noEmitOnError` is off ([#20063](https://github.com/angular/angular/issues/20063)) ([8639995](https://github.com/angular/angular/commit/8639995))
* **core:** `__symbol__` should return `__zone_symbol__` without zone.js loaded ([#19541](https://github.com/angular/angular/issues/19541)) ([678d1cf](https://github.com/angular/angular/commit/678d1cf))
* **core:** should support event.stopImmediatePropagation ([#19222](https://github.com/angular/angular/issues/19222)) ([7083791](https://github.com/angular/angular/commit/7083791))
* **platform-browser:** support Symbols in custom `jasmineToString()` method ([#19794](https://github.com/angular/angular/issues/19794)) ([5a6efa7](https://github.com/angular/angular/commit/5a6efa7))
### Features
* **compiler:** introduce `TestBed.overrideTemplateUsingTestingModule` ([a460066](https://github.com/angular/angular/commit/a460066)), closes [#19815](https://github.com/angular/angular/issues/19815)
<a name="5.0.1"></a>
## [5.0.1](https://github.com/angular/angular/compare/5.0.0...5.0.1) (2017-11-08)

View File

@ -69,36 +69,37 @@ You can file new issues by filling out our [new issue form](https://github.com/a
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
Before you submit your Pull Request (PR) consider the following guidelines:
* Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
that relates to your submission. You don't want to duplicate effort.
* Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
We cannot accept code without this.
* Make your changes in a new git branch:
1. Fork the angular/angular repo.
1. Make your changes in a new git branch:
```shell
git checkout -b my-fix-branch master
```
* Create your patch, **including appropriate test cases**.
* Follow our [Coding Rules](#rules).
* Run the full Angular test suite, as described in the [developer documentation][dev-doc],
1. Create your patch, **including appropriate test cases**.
1. Follow our [Coding Rules](#rules).
1. Run the full Angular test suite, as described in the [developer documentation][dev-doc],
and ensure that all tests pass.
* Commit your changes using a descriptive commit message that follows our
1. Commit your changes using a descriptive commit message that follows our
[commit message conventions](#commit). Adherence to these conventions
is necessary because release notes are automatically generated from these messages.
```shell
git commit -a
```
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
* Push your branch to GitHub:
1. Push your branch to GitHub:
```shell
git push origin my-fix-branch
```
* In GitHub, send a pull request to `angular:master`.
1. In GitHub, send a pull request to `angular:master`.
* If we suggest changes then:
* Make the required updates.
* Re-run the Angular test suites to ensure tests are still passing.

View File

@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2014-2017 Google, Inc. http://angular.io
Copyright (c) 2014-2018 Google, Inc. http://angular.io
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -2,8 +2,6 @@
[![CircleCI](https://circleci.com/gh/angular/angular/tree/master.svg?style=shield)](https://circleci.com/gh/angular/angular/tree/master)
[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06)](https://www.browserstack.com/automate/public-build/LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06)
[![Join the chat at https://gitter.im/angular/angular](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/pr?style=flat)](http://issuestats.com/github/angular/angular)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/issue?style=flat)](http://issuestats.com/github/angular/angular)
[![npm version](https://badge.fury.io/js/%40angular%2Fcore.svg)](https://www.npmjs.com/@angular/core)

View File

@ -5,20 +5,60 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "build_bazel_rules_nodejs",
remote = "https://github.com/bazelbuild/rules_nodejs.git",
# TODO(alexeagle): use the correct tag here.
commit = "2c6243df53fd33fdab283ebdd01582e4eb815db8",
tag = "0.3.1",
)
load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories")
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories")
check_bazel_version("0.9.0")
node_repositories(package_json = ["//:package.json"])
local_repository(
git_repository(
name = "build_bazel_rules_typescript",
path = "node_modules/@bazel/typescript",
remote = "https://github.com/bazelbuild/rules_typescript.git",
# tag = "0.7.1+",
commit = "89d2c75066bea3d9c942f29dd1d2ea543c58d6d5"
)
load("@build_bazel_rules_typescript//:setup.bzl", "ts_setup_workspace")
ts_setup_workspace()
local_repository(
name = "angular",
path = "packages/bazel",
)
local_repository(
name = "rxjs",
path = "node_modules/rxjs/src",
)
git_repository(
name = "com_github_bazelbuild_buildtools",
remote = "https://github.com/bazelbuild/buildtools.git",
# Note, this commit matches the version of buildifier in angular/ngcontainer
# If you change this, also check if it matches the version in the angular/ngcontainer
# version in /.circleci/config.yml
commit = "b3b620e8bcff18ed3378cd3f35ebeb7016d71f71",
)
http_archive(
name = "io_bazel_rules_go",
url = "https://github.com/bazelbuild/rules_go/releases/download/0.7.1/rules_go-0.7.1.tar.gz",
sha256 = "341d5eacef704415386974bc82a1783a8b7ffbff2ab6ba02375e1ca20d9b031c",
)
load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
go_rules_dependencies()
go_register_toolchains()
# Fetching the Bazel source code allows us to compile the Skylark linter
http_archive(
name = "io_bazel",
url = "https://github.com/bazelbuild/bazel/archive/9755c72b48866ed034bd28aa033e9abd27431b1e.zip",
strip_prefix = "bazel-9755c72b48866ed034bd28aa033e9abd27431b1e",
sha256 = "5b8443fc3481b5fcd9e7f348e1dd93c1397f78b223623c39eb56494c55f41962",
)

View File

@ -1,9 +1,10 @@
<hr>
<h4>{{hero.name}} Detail</h4>
<div>Id: {{hero.id}}</div>
<div>Name:
<label>Name:
<!-- #docregion ngModel -->
<input [(ngModel)]="hero.name">
<!-- #enddocregion ngModel -->
</div>
<div>Power:<input [(ngModel)]="hero.power"></div>
</label>
<br />
<label>Power: <input [(ngModel)]="hero.power"></label>

View File

@ -7,7 +7,7 @@ import { TaxRateService } from './tax-rate.service';
selector: 'app-sales-tax',
template: `
<h2>Sales Tax Calculator</h2>
Amount: <input #amountBox (change)="0">
<label>Amount: <input #amountBox (change)="0"></label>
<div *ngIf="amountBox.value">
The sales tax is

View File

@ -5,7 +5,7 @@
"!**/*.d.ts",
"!**/*.js",
"!**/*.[0,1,2].*",
"**/dummy.module.ts"
"!**/dummy.module.ts"
],
"tags": ["dependency", "di"]
}

View File

@ -2,5 +2,6 @@
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
registerLocaleData(localeFr);
// the second parameter 'fr' is optional
registerLocaleData(localeFr, 'fr');
// #enddocregion import-locale

View File

@ -1,7 +1,7 @@
// #docregion import-locale-extra
import { registerLocaleData } from '@angular/common';
import localeFrCa from '@angular/common/locales/fr-CA';
import localeFrCaExtra from '@angular/common/locales/extra/fr-CA';
import localeFr from '@angular/common/locales/fr';
import localeFrExtra from '@angular/common/locales/extra/fr';
registerLocaleData(localeFrCa, localeFrCaExtra);
registerLocaleData(localeFr, 'fr-FR', localeFrExtra);
// #enddocregion import-locale-extra

View File

@ -5,17 +5,6 @@
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfills -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.0.js').catch(function(err){ console.error(err); });
</script>
</head>
<body>

View File

@ -5,17 +5,6 @@
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfills -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.1.js').catch(function(err){ console.error(err); });
</script>
</head>
<body>

View File

@ -5,17 +5,6 @@
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfills -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.1b.js').catch(function(err){ console.error(err); });
</script>
</head>
<body>

View File

@ -5,17 +5,6 @@
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfills -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.2.js').catch(function(err){ console.error(err); });
</script>
</head>
<body>

View File

@ -5,17 +5,6 @@
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfills -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.3.js').catch(function(err){ console.error(err); });
</script>
</head>
<body>

View File

@ -5,13 +5,17 @@
"styles.css",
"app/app.component.ts",
"app/app.component.html",
"app/app.component.css",
"app/app.module.ts",
"app/data-model.ts",
"app/hero.service.ts",
"app/hero-detail.component.html",
"app/hero-detail.component.ts",
"app/hero-list.component.html",
"app/hero-list.component.ts",
"app/hero-detail/hero-detail.component.html",
"app/hero-detail/hero-detail.component.ts",
"app/hero-detail/hero-detail.component.css",
"app/hero-list/hero-list.component.html",
"app/hero-list/hero-list.component.ts",
"app/hero-list/hero-list.component.css",
"main-final.ts",
"index-final.html"

View File

@ -6,18 +6,6 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="styles.css">
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main-final.js').catch(function(err){ console.error(err); });
</script>
</head>
<body>

View File

@ -0,0 +1,44 @@
import { AppPage } from './app.po';
import { browser, element, by } from 'protractor';
describe('sw-example App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to Service Workers!');
});
it('should display the Angular logo', () => {
let logo = element(by.css('img'));
page.navigateTo();
expect(logo.isPresent()).toBe(true);
});
it('should show a header for the list of links', () => {
const listHeader = element(by.css('app-root > h2'));
expect(listHeader.getText()).toEqual('Here are some links to help you start:');
});
it('should show a list of links', function () {
element.all(by.css('ul > li > h2 > a')).then((items) => {
expect(items.length).toBe(4);
expect(items[0].getText()).toBe('Angular Service Worker Intro');
expect(items[1].getText()).toBe('Tour of Heroes');
expect(items[2].getText()).toBe('CLI Documentation');
expect(items[3].getText()).toBe('Angular blog');
});
});
// Check for a rejected promise as the service worker is not enabled
it('SwUpdate.checkForUpdate() should return a rejected promise', () => {
const button = element(by.css('button'));
const rejectMessage = element(by.css('p'));
button.click();
expect(rejectMessage.getText()).toContain('rejected: ');
});
});

View File

@ -0,0 +1,50 @@
{
"name": "angular.io-example",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^5.0.0",
"@angular/common": "^5.0.0",
"@angular/compiler": "^5.0.0",
"@angular/core": "^5.0.0",
"@angular/forms": "^5.0.0",
"@angular/http": "^5.0.0",
"@angular/service-worker": "^5.0.0",
"@angular/platform-browser": "^5.0.0",
"@angular/platform-browser-dynamic": "^5.0.0",
"@angular/router": "^5.0.0",
"core-js": "^2.4.1",
"rxjs": "^5.5.2",
"zone.js": "^0.8.14"
},
"devDependencies": {
"@angular/cli": "1.5.4",
"@angular/compiler-cli": "^5.0.0",
"@angular/language-service": "^5.0.0",
"@types/jasmine": "~2.5.53",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "^4.0.1",
"jasmine-core": "~2.6.2",
"jasmine-spec-reporter": "~4.1.0",
"karma": "~1.7.0",
"karma-chrome-launcher": "~2.1.1",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"ts-node": "~3.2.0",
"tslint": "~5.7.0",
"typescript": "~2.4.2"
}
}

View File

@ -0,0 +1,5 @@
{
"description": "Service Worker",
"basePath": "src/",
"tags": ["service worker"]
}

View File

@ -0,0 +1,27 @@
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Welcome to {{title}}!
</h1>
<img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<button id="check" (click)="updateCheck()">Check for Update</button>
<p id="checkResult">{{updateCheckText}}</p>
<h2>Here are some links to help you start: </h2>
<ul>
<li>
<h2><a target="_blank" rel="noopener" href="https://angular.io/service-worker-intro">Angular Service Worker Intro</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
</li>
</ul>

View File

@ -0,0 +1,27 @@
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('Service Workers');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to Service Workers!');
}));
});

View File

@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
title = 'Service Workers';
updateCheckText = '';
constructor(private update: SwUpdate) {}
updateCheck(): void {
this.update
.checkForUpdate()
.then(() => this.updateCheckText = 'resolved')
.catch(err => this.updateCheckText = `rejected: ${err.message}`);
}
}

View File

@ -0,0 +1,31 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
// #docregion sw-import
import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';
// #enddocregion sw-import
import { CheckForUpdateService } from './check-for-update.service';
import { LogUpdateService } from './log-update.service';
import { PromptUpdateService } from './prompt-update.service';
// #docregion sw-module
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
ServiceWorkerModule.register('/ngsw-worker.js', {enabled: environment.production})
],
providers: [
CheckForUpdateService,
LogUpdateService,
PromptUpdateService,
],
bootstrap: [AppComponent]
})
export class AppModule { }
// #enddocregion sw-module

View File

@ -0,0 +1,15 @@
import { Injectable } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
// #docregion sw-check-update
import { interval } from 'rxjs/observable/interval';
@Injectable()
export class CheckForUpdateService {
constructor(updates: SwUpdate) {
interval(6 * 60 * 60).subscribe(() => updates.checkForUpdate());
}
}
// #enddocregion sw-check-update

View File

@ -0,0 +1,19 @@
import { Injectable } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
// #docregion sw-update
@Injectable()
export class LogUpdateService {
constructor(updates: SwUpdate) {
updates.available.subscribe(event => {
console.log('current version is', event.current);
console.log('available version is', event.available);
});
updates.activated.subscribe(event => {
console.log('old version was', event.previous);
console.log('new version is', event.current);
});
}
}
// #enddocregion sw-update

View File

@ -0,0 +1,20 @@
import { Injectable } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
function promptUser(event): boolean {
return true;
}
// #docregion sw-activate
@Injectable()
export class PromptUpdateService {
constructor(updates: SwUpdate) {
updates.available.subscribe(event => {
if (promptUser(event)) {
updates.activateUpdate().then(() => document.location.reload());
}
});
}
}
// #enddocregion sw-activate

View File

@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>SwExample</title>
<base href="/">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -0,0 +1,12 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));

View File

@ -0,0 +1,28 @@
{
"index": "/index.html",
"assetGroups": [{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html"
],
"versionedFiles": [
"/*.bundle.css",
"/*.bundle.js",
"/*.chunk.js"
]
}
}, {
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**"
]
}
}]
}

View File

@ -37,7 +37,7 @@ describe('Structural Directives', function () {
expect(paragraph.count()).toEqual(1);
});
it('myUnless should show 3 paragraph (A)s and (B)s at the start', function () {
it('appUnless should show 3 paragraph (A)s and (B)s at the start', function () {
const paragraph = element.all(by.css('p.unless'));
expect(paragraph.count()).toEqual(3);
for (let i = 0; i < 3; i++) {
@ -45,7 +45,7 @@ describe('Structural Directives', function () {
}
});
it('myUnless should show 1 paragraph (B) after toggling condition', function () {
it('appUnless should show 1 paragraph (B) after toggling condition', function () {
const toggleConditionButton = element.all(by.cssContainingText('button', 'Toggle condition')).get(0);
const paragraph = element.all(by.css('p.unless'));

View File

@ -188,7 +188,7 @@
<hr>
<h2 id="myUnless">UnlessDirective</h2>
<h2 id="appUnless">UnlessDirective</h2>
<p>
The condition is currently
<span [ngClass]="{ 'a': !condition, 'b': condition, 'unless': true }">{{condition}}</span>.
@ -198,31 +198,31 @@
Toggle condition to {{condition ? 'false' : 'true'}}
</button>
</p>
<!-- #docregion myUnless-->
<!-- #docregion appUnless-->
<p *appUnless="condition" class="unless a">
(A) This paragraph is displayed because the condition is false.
</p>
<p *appUnless="!condition" class="unless b">
(B) Although the condition is true,
this paragraph is displayed because myUnless is set to false.
this paragraph is displayed because appUnless is set to false.
</p>
<!-- #enddocregion myUnless-->
<!-- #enddocregion appUnless-->
<h4>UnlessDirective with template</h4>
<!-- #docregion myUnless-1 -->
<!-- #docregion appUnless-1 -->
<p *appUnless="condition">Show this sentence unless the condition is true.</p>
<!-- #enddocregion myUnless-1 -->
<!-- #enddocregion appUnless-1 -->
<p *appUnless="condition" class="code unless">
(A) &lt;p *myUnless="condition" class="code unless"&gt;
(A) &lt;p *appUnless="condition" class="code unless"&gt;
</p>
<ng-template [appUnless]="condition">
<p class="code unless">
(A) &lt;ng-template [myUnless]="condition"&gt;
(A) &lt;ng-template [appUnless]="condition"&gt;
</p>
</ng-template>

View File

@ -8,7 +8,7 @@ import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
* Add the template content to the DOM unless the condition is true.
// #enddocregion no-docs
*
* If the expression assigned to `myUnless` evaluates to a truthy value
* If the expression assigned to `appUnless` evaluates to a truthy value
* then the templated elements are removed removed from the DOM,
* the templated elements are (re)inserted into the DOM.
*
@ -18,8 +18,8 @@ import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
*
* ### Syntax
*
* - `<div *myUnless="condition">...</div>`
* - `<ng-template [myUnless]="condition"><div>...</div></ng-template>`
* - `<div *appUnless="condition">...</div>`
* - `<ng-template [appUnless]="condition"><div>...</div></ng-template>`
*
// #docregion no-docs
*/

View File

@ -130,7 +130,7 @@ function tests() {
it('can get RouterLinks from template', () => {
expect(links.length).toBe(3, 'should have 3 links');
expect(links[0].linkParams).toBe('/dashboard', '1st link should go to Dashboard');
expect(links[1].linkParams).toBe('/heroes', '1st link should go to Heroes');
expect(links[1].linkParams).toBe('/heroes', '2nd link should go to Heroes');
});
it('can click Heroes link in template', () => {

View File

@ -2,7 +2,7 @@
The Angular Ahead-of-Time (AOT) compiler converts your Angular HTML and TypeScript code into efficient JavaScript code during the build phase _before_ the browser downloads and runs that code.
This guide explains how to to build with the AOT compiler and how to write Angular metadata that AOT can compile.
This guide explains how to build with the AOT compiler using different compiler options and how to write Angular metadata that AOT can compile.
<div class="l-sub-section">
@ -77,6 +77,174 @@ AOT compiles HTML templates and components into JavaScript files long before the
With no templates to read and no risky client-side HTML or JavaScript evaluation,
there are fewer opportunities for injection attacks.
{@a compiler-options}
## Angular Compiler Options
You can control your app compilation by providing template compiler options in the `tsconfig.json` file along with the options supplied to the TypeScript compiler. The template compiler options are specified as members of
`"angularCompilerOptions"` object as shown below:
```json
{
"compilerOptions": {
"experimentalDecorators": true,
...
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"preserveWhiteSpace": false,
...
}
}
```
### *skipMetadataEmit*
This option tells the compiler not to produce `.metadata.json` files.
The option is `false` by default.
`.metadata.json` files contain infomration needed by the template compiler from a `.ts`
file that is not included in the `.d.ts` file produced by the TypeScript compiler. This information contains,
for example, the content of annotations (such as a component's template) which TypeScript
emits to the `.js` file but not to the `.d.ts` file.
This option should be set to `true` if using TypeScript's `--outFile` option, as the metadata files
are not valid for this style of TypeScript output. It is not recommeded to use `--outFile` with
Angular. Use a bundler, such as [webpack](https://webpack.js.org/), instead.
This option can also be set to `true` when using factory summaries as the factory summaries
include a copy of the information that is in the `.metadata.json` file.
### *strictMetadataEmit*
This option tells the template compiler to report an error to the `.metadata.json`
file if `"skipMetadataEmit"` is `false` . This option is `false` by default. This should only be used when `"skipMetadataEmit"` is `false` and `"skipTemplateCodeGen"` is `true`.
It is intended to validate the `.metadata.json` files emitted for bundling with an `npm` package. The validation is overly strict and can emit errors for metadata that would never produce an error when used by the template compiler. You can choose to suppress the error emitted by this option for an exported symbol by including `@dynamic` in the comment documenting the symbol.
It is valid for `.metadata.json` files to contain errors. The template compiler reports these errors
if the metadata is used to determine the contents of an annotation. The metadata
collector cannot predict the symbols that are designed to use in an annotation, so it will preemptively
include error nodes in the metadata for the exported symbols. The template compiler can then use the error
nodes to report an error if these symbols are used. If the client of a library intends to use a symbol in an annotation, the template compiler will not normally report
this until the client uses the symbol. This option allows detecting these errors during the build phase of
the library and is used, for example, in producing Angular libraries themselves.
### *skipTemplateCodegen*
This option tells the compiler to suppress emitting `.ngfactory.js` and `.ngstyle.js` files. When set,
this turns off most of the template compiler and disables reporting template diagnostics.
This option can be used to instruct the
template compiler to produce `.metadata.json` files for distribution with an `npm` package while
avoiding the production of `.ngfactory.js` and `.ngstyle.js` files that cannot be distributed to
`npm`.
### *strictInjectionParameters*
When set to `true`, this options tells the compiler to report an error for a parameter supplied
whose injection type cannot be determined. When this value option is not provided or is `false`, constructor parameters of classes marked with `@Injectable` whose type cannot be resolved will
produce a warning.
*Note*: It is recommended to change this option explicitly to `true` as this option will default to `true` in the future.
### *flatModuleOutFile*
When set to `true`, this option tells the template compiler to generate a flat module
index of the given file name and the corresponding flat module metadata. Use this option when creating
flat modules that are packaged similarly to `@angular/core` and `@angular/common`. When this option
is used, the `package.json` for the library should refer
to the generated flat module index instead of the library index file. With this
option only one `.metadata.json` file is produced that contains all the metadata necessary
for symbols exported from the library index. In the generated `.ngfactory.js` files, the flat
module index is used to import symbols that includes both the public API from the library index
as well as shrowded internal symbols.
By default the `.ts` file supplied in the `files` field is assumed to be library index.
If more than one `.ts` file is specified, `libraryIndex` is used to select the file to use.
If more than one `.ts` file is supplied without a `libraryIndex`, an error is produced. A flat module
index `.d.ts` and `.js` will be created with the given `flatModuleOutFile` name in the same
location as the library index `.d.ts` file. For example, if a library uses
`public_api.ts` file as the library index of the module, the `tsconfig.json` `files` field
would be `["public_api.ts"]`. The `flatModuleOutFile` options could then be set to, for
example `"index.js"`, which produces `index.d.ts` and `index.metadata.json` files. The
library's `package.json`'s `module` field would be `"index.js"` and the `typings` field
would be `"index.d.ts"`.
### *flatModuleId*
This option specifies the preferred module id to use for importing a flat module.
References generated by the template compiler will use this module name when importing symbols
from the flat module.
This is only meaningful when `flatModuleOutFile` is also supplied. Otherwise the compiler ignores
this option.
### *generateCodeForLibraries*
This option tells the template compiler to generate factory files (`.ngfactory.js` and `.ngstyle.js`)
for `.d.ts` files with a corresponding `.metadata.json` file. This option defaults to
`true`. When this option is `false`, factory files are generated only for `.ts` files.
This option should be set to `false` when using factory summaries.
### *fullTemplateTypeCheck*
This option tells the compiler to enable the [binding expression validation](#binding-expresion-validation)
phase of the template compiler which uses TypeScript to validate binding expressions.
This option is `false` by default.
*Note*: It is recommended to set this to `true` as this option will default to `true` in the future.
### *annotateForClosureCompiler*
This option tells the compiler to use [Tsickle](https://github.com/angular/tsickle) to annotate the emitted
JavaScript with [JsDoc](http://usejsdoc.org/) comments needed by the
[Closure Compiler](https://github.com/google/closure-compiler). This option defaults to `false`.
### *annotationsAs*
Use this option to modify how the Angular specific annotations are emitted to improve tree-shaking. Non-Angular
annotations and decorators are unnaffected. Default is `static fields`.
value | description
----------------|-------------------------------------------------------------
`decorators` | Leave the Decorators in-place. This makes compilation faster. TypeScript will emit calls to the __decorate helper. Use `--emitDecoratorMetadata` for runtime reflection. However, the resulting code will not properly tree-shake.
`static fields` | Replace decorators with a static field in the class. Allows advanced tree-shakers like [Closure Compiler](https://github.com/google/closure-compiler) to remove unused classes.
### *trace*
This tells the compiler to print extra information while compiling templates.
### *enableLegacyTemplate*
The use of `<template>` element was deprecated starting in Angular 4.0 in favor of using
`<ng-template>` to avoid colliding with the DOM's element of the same name. Setting this option to
`true` enables the use of the deprecated `<template>` element . This option
is `false` by default. This option might be required by some third-party Angular libraries.
### *disableExpressionLowering*
The Angular template compiler transforms code that is used, or could be used, in an annotation
to allow it to be imported from template factory modules. See
[metadata rewriting](#metadata-rewriting) for more information.
Setting this option to `false` disables this rewriting, requiring the rewriting to be
done manually.
### *preserveWhitespaces*
This option tells the compiler whether to remove blank text nodes from compiled templates.
This option is `true` by default.
*Note*: It is recommended to set this explicitly to `false` as it emits smaller template factory modules and might be set to `false` by default in the future.
### *allowEmptyCodegenFiles*
Tells the compiler to generate all the possible generated files even if they are empty. This option is
`false` by default. This is an option used by `bazel` build rules and is needed to simplify
how `bazel` rules track file dependencies. It is not recommended to use this option outside of the `bazel`
rules.
## Angular Metadata and AOT
The Angular **AOT compiler** extracts and interprets **metadata** about the parts of the application that Angular is supposed to manage.
@ -212,6 +380,7 @@ export function serverFactory() {
Beginning in version 5, the compiler automatically performs this rewritting while emitting the `.js` file.
{@a function-calls}
### Limited function calls
The _collector_ can represent a function call or object creation with `new` as long as the syntax is valid. The _collector_ only cares about proper syntax.
@ -411,6 +580,7 @@ The Angular [`RouterModule`](api/router/RouterModule) exports two macro static m
Review the [source code](https://github.com/angular/angular/blob/master/packages/router/src/router_module.ts#L139 "RouterModule.forRoot source code")
for these methods to see how macros can simplify configuration of complex [NgModules](guide/ngmodule).
{@ metadata-rewriting}
### Metadata rewriting
The compiler treats object literals containing the fields `useClass`, `useValue`, `useFactory`, and `data` specially. The compiler converts the expression initializing one of these fields into an exported variable, which replaces the expression. This process of rewriting these expressions removes all the restrictions on what can be in them because
@ -961,7 +1131,155 @@ This error can occur if you use an expression in the `extends` clause of a class
Chuck: After reviewing your PR comment I'm still at a loss. See [comment there](https://github.com/angular/angular/pull/17712#discussion_r132025495).
-->
{@a binding-expresion-validation}
## Phase 3: binding expression validation
In the validation phase, the Angular template compiler uses the TypeScript compiler to validate the
binding expressions in templates. Enable this phase explicity by adding the compiler
option `"fullTemplateTypeCheck"` in the `"angularCompilerOptions"` of the project's `tsconfig.json` (see
[Angular Compiler Options](#compiler-options)).
Template validation produces error messages when a type error is detected in a template binding
expression, similar to how type errors are reported by the TypeScript compiler against code in a `.ts`
file.
For example, consider the following component:
```ts
@Component({
selector: 'my-component',
template: '{{person.addresss.street}}'
})
class MyComponent {
person?: Person;
}
```
This will produce the following error:
```
my.component.ts.MyComponent.html(1,1): : Property 'addresss' does not exist on type 'Person'. Did you mean 'address'?
```
The file name reported in the error message, `my.component.ts.MyComponent.html`, is a synthetic file
generated by the template compiler that holds contents of the `MyComponent` class template.
Compiler never writes this file to disk. The line and column numbers are relative to the template string
in the `@Component` annotation of the class, `MyComponent` in this case. If a component uses
`templateUrl` instead of `template`, the errors are reported in the HTML file refereneced by the
`templateUrl` instead of a synthetic file.
The error location is the beginning of the text node that contains the interpolation expression with
the error. If the error is in an attribute binding such as `[value]="person.address.street"`, the error
location is the location of the attribute that contains the error.
The validation uses the TypeScript type checker and the options supplied to the TypeScript compiler to control
how detailed the type validation is. For example, if the `strictTypeChecks` is specified, the error ```my.component.ts.MyComponent.html(1,1): : Object is possibly 'undefined'``` is reported as well as the above error message.
### Type narrowing
The expression used in an `ngIf` directive is used to narrow type unions in the Angular
template compiler, the same way the `if` expression does in TypeScript. For example, to avoid
`Object is possibly 'undefined'` error in the template above, modify it to only emit the
interpolation if the value of `person` is initialized as shown below:
```ts
@Component({
selector: 'my-component',
template: '<span *ngIf="person"> {{person.addresss.street}} </span>'
})
class MyComponent {
person?: Person;
}
```
Using `*ngIf` allows the TypeScript compiler to infer that the `person` used in the
binding expression will never be `undefined`.
#### Custom `ngIf` like directives
Directives that behave like `*ngIf` can declare that they want the same treatment by including
a static member marker that is a signal to the template compiler to treat them
like `*ngIf`. This static member for `*ngIf` is:
```ts
public static ngIfUseIfTypeGuard: void;
```
This declares that the input property `ngIf` of the `NgIf` directive should be treated as a
guard to the use of its template, implying that the template will only be instantiated if
the `ngIf` input property is true.
### Non-null type assertion operator
Use the [non-null type assertion operator](guide/template-syntax#non-null-assertion-operator)
to suppress the `Object is possibly 'undefined'` error when it is incovienent to use
`*ngIf` or when some constraint in the component ensures that the expression is always
non-null when the binding expression is interpolated.
In the following example, the `person` and `address` properties are always set together,
implying that `address` is always non-null if `person` is non-null. There is no convenient
way to describe this constraint to TypeScript and the template compiler, but the error
is suppressed in the example by using `address!.street`.
```ts
@Component({
selector: 'my-component',
template: '<span *ngIf="person"> {{person.name}} lives on {{address!.street}} </span>'
})
class MyComponent {
person?: Person;
address?: Address;
setData(person: Person, address: Address) {
this.person = person;
this.address = address;
}
}
```
The non-null assertion operator should be used sparingly as refactoring of the component
might break this constraint.
In this example it is recommended to include the checking of `address`
in the `*ngIf`as shown below:
```ts
@Component({
selector: 'my-component',
template: '<span *ngIf="person && address"> {{person.name}} lives on {{address.street}} </span>'
})
class MyComponent {
person?: Person;
address?: Address;
setData(person: Person, address: Address) {
this.person = person;
this.address = address;
}
}
```
### Disabling type checking using `$any()`
Disable checking of a binding expression by surrounding the expression
in a call to the [`$any()` cast pseudo-function](guide/template-syntax).
The compiler treats it as a cast to the `any` type just like in TypeScript when a `<any>`
or `as any` cast is used.
In the following example, the error `Property addresss does not exist` is suppressed
by casting `person` to the `any` type.
```ts
@Component({
selector: 'my-component',
template: '{{$any(person).addresss.street}}'
})
class MyComponent {
person?: Person;
}
```
## Summary
* What the AOT compiler does and why it is important.
@ -970,3 +1288,5 @@ Chuck: After reviewing your PR comment I'm still at a loss. See [comment there](
* Other restrictions on metadata definition.
* Macro-functions and macro-static methods.
* Compiler errors related to metadata.
* Validation of binding expressions

View File

@ -179,7 +179,7 @@ to a component's `@Component` decorator:
<code-tabs>
<code-pane title="src/app/hero-app.component.ts (CSS in file)" path="component-styles/src/app/hero-app.component.1.ts"></code-pane>
<code-pane title="src/app/hero-app.component.css" path="component-styles/src/app/hero-app.component.1.css"></code-pane>
</code-tabs>
</code-tabs>
<div class="alert is-critical">
@ -245,7 +245,8 @@ See the [CLI documentation](https://github.com/angular/angular-cli/wiki/stories-
### Non-CSS style files
You can write style files in [sass](http://sass-lang.com/), [less](http://lesscss.org/), or [stylus](http://stylus-lang.com/) and specify those files in the `styleUrls` metadata, e.g.,
If you're building with the CLI,
you can write style files in [sass](http://sass-lang.com/), [less](http://lesscss.org/), or [stylus](http://stylus-lang.com/) and specify those files in the `@Component.styleUrls` metadata with the appropriate extensions (`.scss`, `.less`, `.styl`) as in the following example:
<code-example>
@Component({
@ -256,10 +257,18 @@ You can write style files in [sass](http://sass-lang.com/), [less](http://lesscs
...
</code-example>
The CLI build process runs the corresponding CSS pre-processors.
The CLI build process runs the pertinent CSS preprocessor.
You can also configure the CLI to default to your preferred CSS pre-processer
as explained in the [CLI documentation](https://github.com/angular/angular-cli/wiki/stories-css-preprocessors).
When generating a component file with `ng generate component`, the CLI emits an empty CSS styles file (`.css`) by default.
You can configure the CLI to default to your preferred CSS preprocessor
as explained in the [CLI documentation](https://github.com/angular/angular-cli/wiki/stories-css-preprocessors
"CSS Preprocessor integration").
<div class="alert is-important">
Style strings added to the `@Component.styles` array _must be written in CSS_ because the CLI cannot apply a preprocessor to inline styles.
</div>
{@a view-encapsulation}

View File

@ -371,7 +371,7 @@ and to
Angular developers may encounter a
<a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing" title="Cross-origin resource sharing">
<i>cross-origin resource sharing</i></a> error when making a service request (typically a data service request).
<i>cross-origin resource sharing</i></a> error when making a service request (typically a data service request)
to a server other than the application's own host server.
Browsers forbid such requests unless the server permits them explicitly.

View File

@ -273,6 +273,8 @@ has a single `intercept()` method. Here is a simple interceptor which does nothi
import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
@Injectable()
export class NoopInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
@ -297,6 +299,8 @@ Simply declaring the `NoopInterceptor` above doesn't cause your app to use it. Y
import {NgModule} from '@angular/core';
import {HTTP_INTERCEPTORS} from '@angular/common/http';
import {NoopInterceptor} from 'noop.interceptor.ts';
@NgModule({
providers: [{
provide: HTTP_INTERCEPTORS,
@ -547,12 +551,12 @@ In order to prevent collisions in environments where multiple Angular apps share
### Configuring custom cookie/header names
If your backend service uses different names for the XSRF token cookie or header, use `HttpClientXsrfModule.withConfig()` to override the defaults.
If your backend service uses different names for the XSRF token cookie or header, use `HttpClientXsrfModule.withOptions()` to override the defaults.
```javascript
imports: [
HttpClientModule,
HttpClientXsrfModule.withConfig({
HttpClientXsrfModule.withOptions({
cookieName: 'My-Xsrf-Cookie',
headerName: 'My-Xsrf-Header',
}),

View File

@ -8,7 +8,6 @@ See the <live-example downloadOnly name="i18n">i18n Example</live-example> for a
an AOT-compiled app, translated into French.
{@a angular-i18n}
## Angular and i18n
Angular simplifies the following aspects of internationalization:
@ -62,6 +61,23 @@ For more information about Unicode locale identifiers, see the
For a complete list of locales supported by Angular, see
[the Angular repository](https://github.com/angular/angular/tree/master/packages/common/locales).
The locale identifiers used by CLDR and Angular are based on [BCP47](http://www.rfc-editor.org/rfc/bcp/bcp47.txt).
These specifications change over time; the following table maps previous identifiers to current ones at
time of writing:
| Locale name | Old locale id | New locale id |
|-------------------------------|-------------------|---------------|
| Indonesian | in | id |
| Hebrew | iw | he |
| Romanian Moldova | mo | ro-MD |
| Norwegian Bokmål | no, no-NO | nb |
| Serbian Latin | sh | sr-Latn |
| Filipino | tl | fil |
| Portuguese Brazil | pt-BR | pt |
| Chinese Simplified | zh-cn, zh-Hans-CN | zh-Hans |
| Chinese Traditional | zh-tw, zh-Hant-TW | zh-Hant |
| Chinese Traditional Hong Kong | zh-hk | zh-Hant-HK |
## i18n pipes
@ -78,6 +94,14 @@ If you want to import locale data for other languages, you can do it manually:
<code-example path="i18n/doc-files/app.locale_data.ts" region="import-locale" title="src/app/app.module.ts" linenums="false">
</code-example>
The first parameter is an object containing the locale data imported from `@angular/common/locales`.
By default, the imported locale data is registered with the locale id that is defined in the Angular
locale data itself.
If you want to register the imported locale data with another locale id, use the second parameter to
specify a custom locale id. For example, Angular's locale data defines the locale id for French as
"fr". You can use the second parameter to associate the imported French locale data with the custom
locale id "fr-FR" instead of "fr".
The files in `@angular/common/locales` contain most of the locale data that you
need, but some advanced formatting options might only be available in the extra dataset that you can
import from `@angular/common/locales/extra`. An error message informs you when this is the case.
@ -118,7 +142,6 @@ in the target language.
You need to build and deploy a separate version of the app for each supported language.
{@a i18n-attribute}
### Mark text with the i18n attribute
The Angular `i18n` attribute marks translatable content. Place it on every element tag whose fixed
@ -144,7 +167,6 @@ To mark the greeting for translation, add the `i18n` attribute to the `<h1>` tag
{@a help-translator}
### Help the translator with a description and meaning
To translate a text message accurately, the translator may need additional information or context.
@ -175,7 +197,6 @@ text messages with different descriptions (not different meanings), then they ar
{@a custom-id}
### Set a custom id for persistence and maintenance
The angular i18n extractor tool generates a file with a translation unit entry for each `i18n`
@ -250,7 +271,6 @@ the same text, `Bonjour`:
{@a no-element}
### Translate text without creating an element
If there is a section of text that you would like to translate, you can wrap it in a `<span>` tag.
@ -262,7 +282,6 @@ The `<ng-container>` is transformed into an html comment:
</code-example>
{@a translate-attributes}
## Add i18n translation attributes
You also can translate attributes.
@ -286,7 +305,6 @@ You also can assign a meaning, description, and id with the `i18n-x="<meaning>|<
syntax.
{@a plural-ICU}
## Translate singular and plural
Different languages have different pluralization rules.
@ -342,7 +360,6 @@ for two, three, or any other number if the pluralization rules were different. F
</div>
{@a select-ICU}
## Select among alternative text messages
If your template needs to display different text messages depending on the value of a variable, you
@ -360,7 +377,6 @@ The message maps those values to the appropriate translations:
</code-example>
{@a nesting-ICUS}
## Nesting plural and select ICU expressions
You can also nest different ICU expressions together, as shown in this example:
@ -369,7 +385,6 @@ You can also nest different ICU expressions together, as shown in this example:
</code-example>
{@a ng-xi18n}
## Create a translation source file with _ng xi18n_
Use the `ng xi18n` command provided by the CLI to extract the text messages marked with `i18n` into
@ -394,7 +409,6 @@ package, or you can manually use the CLI Webpack plugin `ExtractI18nPlugin` from
</div>
{@a other-formats}
### Other translation formats
Angular i18n tooling supports three translation formats:
@ -422,7 +436,6 @@ The sample in this guide uses the default XLIFF 1.2 format.
</div>
{@a ng-xi18n-options}
### Other options
You can specify the output path used by the CLI to extract your translation source file with
@ -456,7 +469,6 @@ file. This information is not used by Angular, but external translation tools ma
{@a translate}
## Translate text messages
The `ng xi18n` command generates a translation source file named `messages.xlf` in the project `src`
@ -466,7 +478,6 @@ The next step is to translate this source file into the specific language
translation files. The example in this guide creates a French translation file.
{@a localization-folder}
### Create a localization folder
Most apps are translated into more than one other language. For this reason, it is standard practice
@ -500,7 +511,6 @@ For this example:
If you were translating to other languages, you would repeat these steps for each target language.
{@a translate-text-nodes}
### Translate text nodes
In a large translation project, you would send the `messages.fr.xlf` file to a French translator who
@ -544,7 +554,6 @@ This sample file is easy to translate without a special editor or knowledge of F
</div>
{@a translate-plural-select}
## Translate plural and select expressions
_Plural_ and _select_ ICU expressions are extracted separately, so they require special attention
@ -555,7 +564,6 @@ elsewhere in the source template. In this example, you know the translation unit
must be just below the translation unit for the logo.
{@a translate-plural}
### Translate _plural_
To translate a `plural`, translate its ICU format match values:
@ -567,7 +575,6 @@ You can add or remove plural cases, with each language having its own cardinalit
[CLDR plural rules](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html).)
{@a translate-select}
### Translate _select_
Below is the content of our example `select` ICU expression in the component template:
@ -598,7 +605,6 @@ Here they are together, after translation:
</code-example>
{@a translate-nested}
### Translate a nested expression
A nested expression is similar to the previous examples. As in the previous example, there are
@ -621,7 +627,6 @@ The entire template translation is complete. The next section describes how to l
into the app.
{@a app-pre-translation}
### The app and its translation file
The sample app and its translation file are now as follows:
@ -640,7 +645,6 @@ The sample app and its translation file are now as follows:
</code-tabs>
{@a merge}
## Merge the completed translation file into the app
To merge the translated text into component templates, compile the app with the completed
@ -656,12 +660,11 @@ format that Angular understands, such as `.xtb`.
How you provide this information depends upon whether you compile with
the JIT compiler or the AOT compiler.
* With [AOT](guide/i18n#aot), you pass the information as a CLI parameter.
* With [JIT](guide/i18n#jit), you provide the information at bootstrap time.
* With [AOT](guide/i18n#merge-aot), you pass the information as a CLI parameter.
* With [JIT](guide/i18n#merge-jit), you provide the information at bootstrap time.
{@a aot}
{@a merge-aot}
### Merge with the AOT compiler
The AOT (_Ahead-of-Time_) compiler is part of a build process that produces a small, fast,
@ -685,8 +688,7 @@ guide:
ng serve --aot --i18nFile=src/locale/messages.fr.xlf --i18nFormat=xlf --locale=fr
</code-example>
{@a jit}
{@a merge-jit}
### Merge with the JIT compiler
The JIT compiler compiles the app in the browser as the app loads.
@ -713,3 +715,29 @@ Then provide the `LOCALE_ID` in the main module:
<code-example path="i18n/doc-files/app.module.ts" title="src/app/app.module.ts" linenums="false">
</code-example>
{@a missing-translation}
### Report missing translations
By default, when a translation is missing, the build succeeds but generates a warning such as
`Missing translation for message "foo"`. You can configure the level of warning that is generated by
the Angular compiler:
* Error: throw an error. If you are using AOT compilation, the build will fail. If you are using JIT
compilation, the app will fail to load.
* Warning (default): show a 'Missing translation' warning in the console or shell.
* Ignore: do nothing.
If you use the AOT compiler, specify the warning level by using the CLI parameter
`--missingTranslation`. The example below shows how to set the warning level to error:
<code-example language="sh" class="code-shell">
ng serve --aot --missingTranslation=error
</code-example>
If you use the JIT compiler, specify the warning level in the compiler config at bootstrap by adding
the 'MissingTranslationStrategy' property. The example below shows how to set the warning level to
error:
<code-example path="i18n/doc-files/main.3.ts" title="src/main.ts">
</code-example>

View File

@ -140,7 +140,7 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</tr>
<tr style='vertical-align:top'>
<td>
<code>ngOnDestroy</code>
<code>ngOnDestroy()</code>
</td>
<td>

View File

@ -0,0 +1,54 @@
# Service Worker Communication
Importing `ServiceWorkerModule` into your `AppModule` doesn't just register the service worker, it also provides a few services you can use to interact with the service worker and control the caching of your app.
#### Prerequisites
A basic understanding of the following:
* [Getting Started with Service Workers](guide/service-worker-getting-started).
<hr />
## `SwUpdate` service
The `SwUpdate` service gives you access to events that indicate when the service worker has discovered an available update for your app or when it has activated such an update&mdash;meaning it is now serving content from that update to your app.
The `SwUpdate` service supports four separate operations:
* Getting notified of *available* updates. These are new versions of the app to be loaded if the page is refreshed.
* Getting notified of update *activation*. This is when the service worker starts serving a new version of the app immediately.
* Asking the service worker to check the server for new updates.
* Asking the service worker to activate the latest version of the app for the current tab.
### Available and activated updates
The two update events, `available` and `activated`, are `Observable` properties of `SwUpdate`:
<code-example path="service-worker-getting-started/src/app/log-update.service.ts" linenums="false" title="log-update.service.ts" region="sw-update"> </code-example>
You can use these events to notify the user of a pending update or to refresh their pages when the code they are running is out of date.
### Checking for updates
It's possible to ask the service worker to check if any updates have been deployed to the server. You might choose to do this if you have a site that changes frequently or want updates to happen on a schedule.
Do this with the `checkForUpdate()` method:
<code-example path="service-worker-getting-started/src/app/check-for-update.service.ts" linenums="false" title="check-for-update.service.ts" region="sw-check-update"> </code-example>
This method returns a `Promise` which indicates that the update check has completed successfully, though it does not indicate whether an update was discovered as a result of the check. Even if one is found, the service worker must still successfully download the changed files, which can fail. If successful, the `available` event will indicate availability of a new version of the app.
### Forcing update activation
If the current tab needs to be updated to the latest app version immediately, it can ask to do so with the `activateUpdate()` method:
<code-example path="service-worker-getting-started/src/app/prompt-update.service.ts" linenums="false" title="prompt-update.service.ts" region="sw-activate"> </code-example>
Doing this could break lazy-loading into currently running apps, especially if the lazy-loaded chunks use filenames with hashes, which change every version.
## More on Angular service workers
You may also be interested in the following:
* [Service Worker in Production](guide/service-worker-devops).

View File

@ -0,0 +1,169 @@
{@a glob}
# Service Worker Configuration
#### Prerequisites
A basic understanding of the following:
* [Service Worker in Production](guide/service-worker-devops).
<hr />
The `src/ngsw-config.json` configuration file specifies which files and data URLs the Angular
service worker should cache and how it should update the cached files and data. The
CLI processes the configuration file during `ng build --prod`. Manually, you can process
it with the `ngsw-config` tool:
```sh
ngsw-config dist src/ngsw-config.json /base/href
```
The configuration file uses the JSON format. All file paths must begin with `/`, which is the deployment directory&mdash;usually `dist` in CLI projects.
Patterns use a limited glob format:
* `**` matches 0 or more path segments.
* `*` matches exactly one path segment or filename segment.
* The `!` prefix marks the pattern as being negative, meaning that only files that don't match the pattern will be included.
Example patterns:
* `/**/*.html` specifies all HTML files.
* `/*.html` specifies only HTML files in the root.
* `!/**/*.map` exclude all sourcemaps.
Each section of the configuration file is described below.
## `appData`
This section enables you to pass any data you want that describes this particular version of the app.
The `SwUpdate` service includes that data in the update notifications. Many apps use this section to provide additional information for the display of UI popups, notifying users of the available update.
## `index`
Specifies the file that serves as the index page to satisfy navigation requests. Usually this is `/index.html`.
## `assetGroups`
*Assets* are resources that are part of the app version that update along with the app. They can include resources loaded from the page's origin as well as third-party resources loaded from CDNs and other external URLs. As not all such external URLs may be known at build time, URL patterns can be matched.
This field contains an array of asset groups, each of which defines a set of asset resources and the policy by which they are cached.
```json
{
"assetGroups": [{
...
}, {
...
}]
}
```
Each asset group specifies both a group of resources and a policy that governs them. This policy determines when the resources are fetched and what happens when changes are detected.
Asset groups follow the Typescript interface shown here:
```typescript
interface AssetGroup {
name: string;
installMode?: 'prefetch' | 'lazy';
updateMode?: 'prefetch' | 'lazy';
resources: {
files?: string[];
versionedFiles?: string[];
urls?: string[];
};
}
```
### `name`
A `name` is mandatory. It identifies this particular group of assets between versions of the configuration.
### `installMode`
The `installMode` determines how these resources are initially cached. The `installMode` can be either of two values:
* `prefetch` tells the Angular service worker to fetch every single listed resource while it's caching the current version of the app. This is bandwidth-intensive but ensures resources are available whenever they're requested, even if the browser is currently offline.
* `lazy` does not cache any of the resources up front. Instead, the Angular service worker only caches resources for which it receives requests. This is an on-demand caching mode. Resources that are never requested will not be cached. This is useful for things like images at different resolutions, so the service worker only caches the correct assets for the particular screen and orientation.
### `updateMode`
For resources already in the cache, the `updateMode` determines the caching behavior when a new version of the app is discovered. Any resources in the group that have changed since the previous version are updated in accordance with `updateMode`.
* `prefetch` tells the service worker to download and cache the changed resources immediately.
* `lazy` tells the service worker to not cache those resources. Instead, it treats them as unrequested and waits until they're requested again before updating them. An `updateMode` of `lazy` is only valid if the `installMode` is also `lazy`.
### `resources`
This section describes the resources to cache, broken up into three groups.
* `files` lists patterns that match files in the distribution directory. These can be single files or glob-like patterns that match a number of files.
* `versionedFiles` is like `files` but should be used for build artifacts that already include a hash in the filename, which is used for cache busting. The Angular service worker can optimize some aspects of its operation if it can assume file contents are immutable.
* `urls` includes both URLs and URL patterns that will be matched at runtime. These resources are not fetched directly and do not have content hashes, but they will be cached according to their HTTP headers. This is most useful for CDNs such as the Google Fonts service.
## `dataGroups`
Unlike asset resources, data requests are not versioned along with the app. They're cached according to manually-configured policies that are more useful for situations such as API requests and other data dependencies.
Data groups follow this Typescript interface:
```typescript
export interface DataGroup {
name: string;
urls: string[];
version?: number;
cacheConfig: {
maxSize: number;
maxAge: string;
timeout?: string;
strategy?: 'freshness' | 'performance';
};
}
```
### `name`
Similar to `assetGroups`, every data group has a `name` which uniquely identifies it.
### `urls`
A list of URL patterns. URLs that match these patterns will be cached according to this data group's policy.
### `version`
Occasionally APIs change formats in a way that is not backward-compatible. A new version of the app may not be compatible with the old API format and thus may not be compatible with existing cached resources from that API.
`version` provides a mechanism to indicate that the resources being cached have been updated in a backwards-incompatible way, and that the old cache entries&mdash;those from previous versions&mdash;should be discarded.
`version` is an integer field and defaults to `0`.
### `cacheConfig`
This section defines the policy by which matching requests will be cached.
#### `maxSize`
(required) The maximum number of entries, or responses, in the cache. Open-ended caches can grow in unbounded ways and eventually exceed storage quotas, calling for eviction.
#### `maxAge`
(required) The `maxAge` parameter indicates how long responses are allowed to remain in the cache before being considered invalid and evicted. `maxAge` is a duration string, using the following unit suffixes:
* `d`: days
* `h`: hours
* `m`: minutes
* `s`: seconds
* `u`: milliseconds
For example, the string `3d12h` will cache content for up to three and a half days.
#### `timeout`
This duration string specifies the network timeout. The network timeout is how long the Angular service worker will wait for the network to respond before using a cached response, if configured to do so.
#### `strategy`
The Angular service worker can use either of two caching strategies for data resources.
* `performance`, the default, optimizes for responses that are as fast as possible. If a resource exists in the cache, the cached version is used. This allows for some staleness, depending on the `maxAge`, in exchange for better performance. This is suitable for resources that don't change often; for example, user avatar images.
* `freshness` optimizes for currency of data, preferentially fetching requested data from the network. Only if the network times out, according to `timeout`, does the request fall back to the cache. This is useful for resources that change frequently; for example, account balances.

View File

@ -0,0 +1,311 @@
# Service Worker in Production
This page is a reference for deploying and supporting production apps that use the Angular service worker. It explains how the Angular service worker fits into the larger production environment, the service worker's behavior under various conditions, and available recourses and fail-safes.
#### Prerequisites
A basic understanding of the following:
* [Service Worker Communication](guide/service-worker-communications).
<hr />
## Service worker and caching of app resources
Conceptually, you can imagine the Angular service worker as a forward cache or a CDN edge that is installed in the end user's web browser. The service worker's job is to satisfy requests made by the Angular app for resources or data from a local cache, without needing to wait for the network. Like any cache, it has rules for how content is expired and updated.
{@a versions}
### App versions
In the context of an Angular service worker, a "version" is a collection of resources that represent a specific build of the Angular app. Whenever a new build of the app is deployed, the service worker treats that build as a new version of the app. This is true even if only a single file is updated. At any given time, the service worker may have multiple versions of the app in its cache and it may be serving them simultaneously. For more information, see the [App tabs](guide/service-worker-devops#tabs) section below.
To preserve app integrity, the Angular service worker groups all files into a version together. The files grouped into a version usually include HTML, JS, and CSS files. Grouping of these files is essential for integrity because HTML, JS, and CSS files frequently refer to each other and depend on specific content. For example, an `index.html` file might have a `<script>` tag that references `bundle.js` and it might attempt to call a function `startApp()` from within that script. Any time this version of `index.html` is served, the corresponding `bundle.js` must be served with it. For example, assume that the `startApp()` function is renamed to `runApp()` in both files. In this scenario, it is not valid to serve the old `index.html`, which calls `startApp()`, along with the new bundle, which defines `runApp()`.
This file integrity is especially important when lazy loading modules.
A JS bundle may reference many lazy chunks, and the filenames of the
lazy chunks are unique to the particular build of the app. If a running
app at version `X` attempts to load a lazy chunk, but the server has
updated to version `X + 1` already, the lazy loading operation will fail.
The version identifier of the app is determined by the contents of all
resources, and it changes if any of them change. In practice, the version
is determined by the contents of the `ngsw.json` file, which includes
hashes for all known content. If any of the cached files change, the file's
hash will change in `ngsw.json`, causing the Angular service worker to
treat the active set of files as a new version.
With the versioning behavior of the Angular service worker, an application
server can ensure that the Angular app always has a consistent set of files.
#### Update checks
Every time the user opens or refreshes the application, the Angular service worker
checks for updates to the app by looking for updates to the `ngsw.json` manifest. If
an update is found, it is downloaded and cached automatically, and will be served
the next time the application is loaded.
### Resource integrity
One of the potential side effects of long caching is inadvertently
caching an invalid resource. In a normal HTTP cache, a hard refresh
or cache expiration limits the negative effects of caching an invalid
file. A service worker ignores such constraints and effectively long
caches the entire app. Consequently, it is essential that the service worker
get the correct content.
To ensure resource integrity, the Angular service worker validates
the hashes of all resources for which it has a hash. Typically for
a CLI app, this is everything in the `dist` directory covered by
the user's `src/ngsw-config.json` configuration.
If a particular file fails validation, the Angular service worker
attempts to re-fetch the content using a "cache-busting" URL
parameter to eliminate the effects of browser or intermediate
caching. If that content also fails validation, the service worker
considers the entire version of the app to be invalid and it stops
serving the app. If necessary, the service worker enters a safe mode
where requests fall back on the network, opting not to use its cache
if the risk of serving invalid, broken, or outdated content is high.
Hash mismatches can occur for a variety of reasons:
* Caching layers in between the origin server and the end user could serve stale content.
* A non-atomic deployment could result in the Angular service worker having visibility of partially updated content.
* Errors during the build process could result in updated resources without `ngsw.json` being updated. The reverse could also happen resulting in an updated `ngsw.json` without updated resources.
#### Unhashed content
The only resources that have hashes in the `ngsw.json`
manifest are resources that were present in the `dist`
directory at the time the manifest was built. Other
resources, especially those loaded from CDNs, have
content that is unknown at build time or are updated
more frequently than the app is deployed.
If the Angular service worker does not have a hash to validate
a given resource, it still caches its contents but it honors
the HTTP caching headers by using a policy of "stale while
revalidate." That is, when HTTP caching headers for a cached
resource indicate that the resource has expired, the Angular
service worker continues to serve the content and it attempts
to refresh the resource in the background. This way, broken
unhashed resources do not remain in the cache beyond their
configured lifetimes.
{@a tabs}
### App tabs
It can be problematic for an app if the version of resources
it's receiving changes suddenly or without warning. See the
[Versions](guide/service-worker-devops#versions) section above
for a description of such issues.
The Angular service worker provides a guarantee: a running app
will continue to run the same version of the app. If another
instance of the app is opened in a new web browser tab, then
the most current version of the app is served. As a result,
that new tab can be running a different version of the app
than the original tab.
It's important to note that this guarantee is **stronger**
than that provided by the normal web deployment model. Without
a service worker, there is no guarantee that code lazily loaded
later in a running app is from the same version as the initial
code for the app.
There are a few limited reasons why the Angular service worker
might change the version of a running app. Some of them are
error conditions:
* The current version becomes invalid due to a failed hash.
* An unrelated error causes the service worker to enter safe mode; that is, temporary deactivation.
The Angular service worker is aware of which versions are in
use at any given moment and it cleans up versions when
no tab is using them.
Other reasons the Angular service worker might change the version
of a running app are normal events:
* The page is reloaded/refreshed.
* The page requests an update be immediately activated via the `SwUpdate` service.
### Service worker updates
The Angular service worker is a small script that runs in web browsers.
From time to time, the service worker will be updated with bug
fixes and feature improvements.
The Angular service worker is downloaded when the app is first opened
and when the app is accessed after a period of inactivity. If the
service worker has changed, the service worker will be updated in the background.
Most updates to the Angular service worker are transparent to the
app&mdash;the old caches are still valid and content is still served
normally. However, occasionally a bugfix or feature in the Angular
service worker requires the invalidation of old caches. In this case,
the app will be refreshed transparently from the network.
## Debugging the Angular service worker
Occasionally, it may be necessary to examine the Angular service
worker in a running state to investigate issues or to ensure that
it is operating as designed. Browsers provide built-in tools for
debugging service workers and the Angular service worker itself
includes useful debugging features.
### Locating and analyzing debugging information
The Angular service worker exposes debugging information under
the `ngsw/` virtual directory. Currently, the single exposed URL
is `ngsw/state`. Here is an example of this debug page's contents:
```
NGSW Debug Info:
Driver state: NORMAL ((nominal))
Latest manifest hash: eea7f5f464f90789b621170af5a569d6be077e5c
Last update check: never
=== Version eea7f5f464f90789b621170af5a569d6be077e5c ===
Clients: 7b79a015-69af-4d3d-9ae6-95ba90c79486, 5bc08295-aaf2-42f3-a4cc-9e4ef9100f65
=== Idle Task Queue ===
Last update tick: 1s496u
Last update run: never
Task queue:
* init post-load (update, cleanup)
Debug log:
```
#### Driver state
The first line indicates the driver state:
```
Driver state: NORMAL ((nominal))
```
`NORMAL` indicates that the service worker is operating normally and is not in a degraded state.
There are two possible degraded states:
* `EXISTING_CLIENTS_ONLY`: the service worker does not have a
clean copy of the latest known version of the app. Older cached
versions are safe to use, so existing tabs continue to run from
cache, but new loads of the app will be served from the network.
* `SAFE_MODE`: the service worker cannot guarantee the safety of
using cached data. Either an unexpected error occurred or all
cached versions are invalid. All traffic will be served from the
network, running as little service worker code as possible.
In both cases, the parenthetical annotation provides the
error that caused the service worker to enter the degraded state.
#### Latest manifest hash
```
Latest manifest hash: eea7f5f464f90789b621170af5a569d6be077e5c
```
This is the SHA1 hash of the most up-to-date version of the app that the service worker knows about.
#### Last update check
```
Last update check: never
```
This indicates the last time the service worker checked for a new version, or update, of the app. `never` indicates that the service worker has never checked for an update.
In this example debug file, the update check is currently scheduled, as explained the next section.
#### Version
```
=== Version eea7f5f464f90789b621170af5a569d6be077e5c ===
Clients: 7b79a015-69af-4d3d-9ae6-95ba90c79486, 5bc08295-aaf2-42f3-a4cc-9e4ef9100f65
```
In this example, the service worker has one version of the app cached and
being used to serve two different tabs. Note that this version hash
is the "latest manifest hash" listed above. Both clients are on the
latest version. Each client is listed by its ID from the `Clients`
API in the browser.
#### Idle task queue
```
=== Idle Task Queue ===
Last update tick: 1s496u
Last update run: never
Task queue:
* init post-load (update, cleanup)
```
The Idle Task Queue is the queue of all pending tasks that happen
in the background in the service worker. If there are any tasks
in the queue, they are listed with a description. In this example,
the service worker has one such task scheduled, a post-initialization
operation involving an update check and cleanup of stale caches.
The last update tick/run counters give the time since specific
events happened related to the idle queue. The "Last update run"
counter shows the last time idle tasks were actually executed.
"Last update tick" shows the time since the last event after
which the queue might be processed.
#### Debug log
```
Debug log:
```
Errors that occur within the service worker will be logged here.
### Developer Tools
Browsers such as Chrome provide developer tools for interacting
with service workers. Such tools can be powerful when used properly,
but there are a few things to keep in mind.
* When using developer tools, the service worker is kept running
in the background and never restarts. This can cause behavior with Dev
Tools open to differ from behavior a user might experience.
* If you look in the Cache Storage viewer, the cache is frequently
out of date. Right click the Cache Storage title and refresh the caches.
Stopping and starting the service worker in the Service Worker
pane triggers a check for updates.
## Fail-safe
Like any complex system, bugs or broken configurations can cause
the Angular service worker to act in unforeseen ways. While its
design attempts to minimize the impact of such problems, the
Angular service worker contains a failsafe mechanism in case
an administrator ever needs to deactivate the service worker quickly.
To deactivate the service worker, remove or rename the
`ngsw-config.json` file. When the service worker's request
for `ngsw.json` returns a `404`, then the service worker
removes all of its caches and de-registers itself,
essentially self-destructing.
## More on Angular service workers
You may also be interested in the following:
* [Service Worker Configuration](guide/service-worker-config).

View File

@ -0,0 +1,203 @@
# Getting Started with Service Workers
#### Prerequisites
A basic understanding of the following:
* [Introduction to Angular service workers](guide/service-worker-intro).
<hr />
Beginning in Angular 5.0.0, you can easily enable Angular service worker support in any CLI project. This document explains how to enable Angular service worker support in new and existing projects. It then uses a simple example to show you a service worker in action, demonstrating loading and basic caching.
## Adding a service worker to a new application
If you're generating a new CLI project, you can use the CLI to set up the Angular service worker as part of creating the project. To do so, add the `--service-worker` flag to the `ng new` command:
```sh
ng new my-project --service-worker
```
The `--service-worker` flag takes care of configuring your app to
use service workers by adding the `service-worker` package along
with setting up the necessary files to support service workers.
For information on the details, see the following section
which covers the process in detail as it shows you how to add a
service worker manually to an existing app.
## Adding a service worker to an existing app
To add a service worker to an existing app:
1. Add the service worker package.
2. Enable service worker build support in the CLI.
3. Import and register the service worker.
4. Create the service worker configuration file, which specifies the caching behaviors and other settings.
5. Build the project.
### Step 1: Add the service worker package
Add the package `@angular/service-worker`, using the yarn utility as shown here:
```sh
yarn add @angular/service-worker
```
### Step 2: Enable service worker build support in the CLI
To enable the Angular service worker, the CLI must generate an Angular service worker manifest at build time. To cause the CLI to generate the manifest for an existing project, set the `serviceWorker` flag to `true` in the project's `.angular-cli.json` file as shown here:
```sh
ng set apps.0.serviceWorker=true
```
### Step 3: Import and register the service worker
To import and register the Angular service worker:
At the top of the root module, `src/app/app.module.ts`, import `ServiceWorkerModule` and `environment`.
<code-example path="service-worker-getting-started/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts" region="sw-import"> </code-example>
Add `ServiceWorkerModule` to the `@NgModule` `imports` array. Use the `register()` helper to take care of registering the service worker, taking care to disable the service worker when not running in production mode.
<code-example path="service-worker-getting-started/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts" region="sw-module"> </code-example>
The file `ngsw-worker.js` is the name of the prebuilt service worker script, which the CLI copies into `dist/` to deploy along with your server.
### Step 4: Create the configuration file, `ngsw-config.json`
The Angular CLI needs a service worker configuration file, called `ngsw-config.json`. The configuration file controls how the service worker caches files and data
resources.
You can begin with the boilerplate version from the CLI, which configures sensible defaults for most applications.
Alternately, save the following as `src/ngsw-config.json`:
<code-example path="service-worker-getting-started/src/ngsw-config.json" linenums="false" title="src/ngsw-config.json"> </code-example>
### Step 5: Build the project
Finally, build the project:
```sh
ng build --prod
```
The CLI project is now set up to use the Angular service worker.
## Service worker in action: a tour
This section demonstrates a service worker in action,
using an example application.
### Serving with `http-server`
Because `ng serve` does not work with service workers, you must use a seperate HTTP server to test your project locally. You can use any HTTP server. The example below uses the [http-server](https://www.npmjs.com/package/http-server) package from npm. To reduce the possibility of conflicts, test on a dedicated port.
To serve with `http-server`, change to the directory containing your web files and start the web server:
```sh
cd dist
http-server -p 8080
```
### Initial load
With the server running, you can point your browser at http://localhost:8080/. Your application should load normally.
**Tip:** When testing Angular service workers, it's a good idea to use an incognito or private window in your browser to ensure the service worker doesn't end up reading from a previous leftover state, which can cause unexpected behavior.
### Simulating a network issue
To simulate a network issue, disable network interaction for your application. In Chrome:
1. Select **Tools** > **Developer Tools** (from the Chrome menu located at the top right corner).
2. Go to the **Network tab**.
3. Check the **Offline box**.
<figure>
<img src="generated/images/guide/service-worker/offline-checkbox.png" alt="The offline checkbox in the Network tab is checked">
</figure>
Now the app has no access to network interaction.
For applications that do not use the Angular service worker, refreshing now would display Chrome's Internet disconnected page that says "There is no Internet connection".
With the addition of an Angular service worker, the application behavior changes. On a refresh, the page loads normally.
If you look at the Network tab, you can verify that the service worker is active.
<figure>
<img src="generated/images/guide/service-worker/sw-active.png" alt="Requests are marked as from ServiceWorker">
</figure>
Notice that under the "Size" column, the requests state is `(from ServiceWorker)`. This means that the resources are not being loaded from the network. Instead, they are being loaded from the service worker's cache.
### What's being cached?
Notice that all of the files the browser needs to render this application are cached. The `ngsw-config.json` boilerplate configuration is set up to cache the specific resources used by the CLI:
* `index.html`.
* `favicon.ico`.
* Build artifacts (JS and CSS bundles).
* Anything under `assets`.
### Making changes to your application
Now that you've seen how service workers cache your application, the
next step is understanding how updates work.
1. If you're testing in an incognito window, open a second blank tab. This will keep the incognito and the cache state alive during your test.
2. Close the application tab, but not the window. This should also close the Developer Tools.
3. Shut down `http-server`.
4. Next, make a change to the application, and watch the service worker install the update.
5. Open `src/app/app.component.html` for editing.
6. Change the text `Welcome to {{title}}!` to `Bienvenue à {{title}}!`.
7. Build and run the server again:
```sh
ng build --prod
cd dist
http-server -p 8080
```
### Updating your application in the browser
Now look at how the browser and service worker handle the updated application.
1. Open http://localhost:8080 again in the same window. What happens?
<figure>
<img src="generated/images/guide/service-worker/welcome-msg-en.png" alt="It still says Welcome to Service Workers!">
</figure>
What went wrong? Nothing, actually. The Angular service worker is doing its job and serving the version of the application that it has **installed**, even though there is an update available. In the interest of speed, the service worker doesn't wait to check for updates before it serves the application that it has cached.
If you look at the `http-server` logs, you can see the service worker requesting `/ngsw.json`. This is how the service worker checks for updates.
2. Refresh the page.
<figure>
<img src="generated/images/guide/service-worker/welcome-msg-fr.png" alt="The text has changed to say Bienvenue à app!">
</figure>
The service worker installed the updated version of your app *in the background*, and the next time the page is loaded or reloaded, the service worker switches to the latest version.
<hr />
## More on Angular service workers
You may also be interested in the following:
* [Communicating with service workers](guide/service-worker-communications).

View File

@ -0,0 +1,53 @@
# Angular Service Worker Introduction
Service workers augment the traditional web deployment model and empower applications to deliver a user experience with the reliability and performance on par with natively-installed code.
At its simplest, a service worker is a script that runs in the web browser and manages caching for an application.
Service workers function as a network proxy. They intercept all outgoing HTTP requests made by the application and can choose how to respond to them. For example, they can query a local cache and deliver a cached response if one is available. Proxying isn't limited to requests made through programmatic APIs, such as `fetch`; it also includes resources referenced in HTML and even the initial request to `index.html`. Service worker-based caching is thus completely programmable and doesn't rely on server-specified caching headers.
Unlike the other scripts that make up an application, such as the Angular app bundle, the service worker is preserved after the user closes the tab. The next time that browser loads the application, the service worker loads first, and can intercept every request for resources to load the application. If the service worker is designed to do so, it can *completely satisfy the loading of the application, without the need for the network*.
Even across a fast reliable network, round-trip delays can introduce significant latency when loading the application. Using a service worker to reduce dependency on the network can significantly improve the user experience.
## Service workers in Angular
Angular applications, as single-page applications, are in a prime position to benefit from the advantages of service workers. Starting with version 5.0.0, Angular ships with a service worker implementation. Angular developers can take advantage of this service worker and benefit from the increased reliability and performance it provides, without needing to code against low-level APIs.
Angular's service worker is designed to optimize the end user experience of using an application over a slow or unreliable network connection, while also minimizing the risks of serving outdated content.
The Angular service worker's behavior follows that design goal:
* Caching an application is like installing a native application. The application is cached as one unit, and all files update together.
* A running application continues to run with the same version of all files. It does not suddenly start receiving cached files from a newer version, which are likely incompatible.
* When users refresh the application, they see the latest fully cached version. New tabs load the latest cached code.
* Updates happen in the background, relatively quickly after changes are published. The previous version of the application is served until an update is installed and ready.
* The service worker conserves bandwidth when possible. Resources are only downloaded if they've changed.
To support these behaviors, the Angular service worker loads a *manifest* file from the server. The manifest describes the resources to cache and includes hashes of every file's contents. When an update to the application is deployed, the contents of the manifest change, informing the service worker that a new version of the application should be downloaded and cached. This manifest is generated from a user-provided configuration file called `ngsw-config.json`, by using a build tool such as the Angular CLI.
Installing the Angular service worker is as simple as including an `NgModule`. In addition to registering the Angular service worker with the browser, this also makes a few services available for injection which interact with the service worker and can be used to control it. For example, an application can ask to be notified when a new update becomes available, or an application can ask the service worker to check the server for available updates.
## Prerequisites
To use Angular service workers, you must have the following Angular and CLI versions:
* Angular 5.0.0 or later.
* Angular CLI 1.6.0 or later.
Your application must run in a web browser that supports service workers. Currently, the latest versions of Chrome and Firefox are supported. To learn about other browsers that are service worker ready, see the [Can I Use](http://caniuse.com/#feat=serviceworkers) page.
## Related resources
For more information about service workers in general, see [Service Workers: an Introduction](https://developers.google.com/web/fundamentals/primers/service-workers/).
For more information about browser support, see the [browser support](https://developers.google.com/web/fundamentals/primers/service-workers/#browser_support) section of [Service Workers: an Introduction](https://developers.google.com/web/fundamentals/primers/service-workers/), Jake Archibald's [Is Serviceworker ready?](https://jakearchibald.github.io/isserviceworkerready/), and
[Can I Use](http://caniuse.com/#feat=serviceworkers).
The remainder of this Angular documentation specifically addresses the Angular implementation of service workers.
## More on Angular service workers
You may also be interested in the following:
* [Getting Started with service workers](guide/service-worker-getting-started).

View File

@ -625,7 +625,7 @@ that does the opposite of `NgIf`.
`UnlessDirective` displays the content when the condition is ***false***.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless-1)" region="myUnless-1">
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (appUnless-1)" region="appUnless-1">
</code-example>
@ -650,14 +650,14 @@ Here's how you might begin:
The directive's _selector_ is typically the directive's **attribute name** in square brackets, `[myUnless]`.
The directive's _selector_ is typically the directive's **attribute name** in square brackets, `[appUnless]`.
The brackets define a CSS
<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors" title="MDN: Attribute selectors">attribute selector</a>.
The directive _attribute name_ should be spelled in _lowerCamelCase_ and begin with a prefix.
Don't use `ng`. That prefix belongs to Angular.
Pick something short that fits you or your company.
In this example, the prefix is `my`.
In this example, the prefix is `app`.
The directive _class_ name ends in `Directive` per the [style guide](guide/styleguide#02-03 "Angular Style Guide").
@ -685,10 +685,10 @@ You inject both in the directive constructor as private variables of the class.
### The _myUnless_ property
### The _appUnless_ property
The directive consumer expects to bind a true/false condition to `[myUnless]`.
That means the directive needs a `myUnless` property, decorated with `@Input`
The directive consumer expects to bind a true/false condition to `[appUnless]`.
That means the directive needs an `appUnless` property, decorated with `@Input`
<div class="l-sub-section">
@ -708,8 +708,8 @@ Read about `@Input` in the [_Template Syntax_](guide/template-syntax#inputs-outp
Angular sets the `myUnless` property whenever the value of the condition changes.
Because the `myUnless` property does work, it needs a setter.
Angular sets the `appUnless` property whenever the value of the condition changes.
Because the `appUnless` property does work, it needs a setter.
* If the condition is falsy and the view hasn't been created previously,
tell the _view container_ to create the _embedded view_ from the template.
@ -717,7 +717,7 @@ tell the _view container_ to create the _embedded view_ from the template.
* If the condition is truthy and the view is currently displayed,
clear the container which also destroys the view.
Nobody reads the `myUnless` property so it doesn't need a getter.
Nobody reads the `appUnless` property so it doesn't need a getter.
The completed directive code looks like this:
@ -733,7 +733,7 @@ Add this directive to the `declarations` array of the AppModule.
Then create some HTML to try it.
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (myUnless)" region="myUnless">
<code-example path="structural-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (appUnless)" region="appUnless">
</code-example>

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -2,7 +2,7 @@
@description
The MIT License
Copyright (c) 2014-2017 Google, Inc.
Copyright (c) 2014-2018 Google, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -639,6 +639,13 @@
"rev": true,
"title": "Formation JavaScript (French)",
"url": "https://formationjavascript.com/formation-angular/"
},
"wao": {
"desc": "Onsite Angular Training delivered by We Are One Sàrl in Switzerland",
"logo": "https://weareone.ch/wordpress/wao-content/uploads/2014/12/logo_200_2x.png",
"rev": true,
"title": "We Are One Sàrl",
"url": "https://weareone.ch/courses/angular/"
}
}
}

View File

@ -350,6 +350,37 @@
}
]
},
{
"title": "Service Workers",
"tooltip": "Angular service workers: Controlling caching of application resources.",
"children": [
{
"url": "guide/service-worker-intro",
"title": "Introduction",
"tooltip": "Angular's implementation of service workers improves user experience with slow or unreliable network connectivity."
},
{
"url": "guide/service-worker-getting-started",
"title": "Getting Started",
"tooltip": "Enabling the service worker in a CLI project and observing behavior in the browser."
},
{
"url": "guide/service-worker-communications",
"title": "Service Worker Communication",
"tooltip": "Services that enable you to interact with an Angular service worker."
},
{
"url": "guide/service-worker-devops",
"title": "Service Worker in Production",
"tooltip": "Running applications with service workers, managing application update, debugging, and killing applications."
},
{
"url": "guide/service-worker-config",
"title": "Service Worker Configuration",
"tooltip": "Configuring service worker caching behavior."
}
]
},
{
"title": "Upgrading",

View File

@ -121,7 +121,7 @@ Import the `HeroService` instead.
<code-example path="toh-pt4/src/app/heroes/heroes.component.ts" title="src/app/heroes/heroes.component.ts (import HeroService)" region="hero-service-import">
</code-example>
Replace the the definition of the `heroes` property with a simple declaration.
Replace the definition of the `heroes` property with a simple declaration.
<code-example path="toh-pt4/src/app/heroes/heroes.component.ts" region="heroes">
</code-example>

View File

@ -275,6 +275,7 @@ The `HeroDetailsComponent` displays details of a selected hero.
At the moment the `HeroDetailsComponent` is only visible at the bottom of the `HeroesComponent`
The user should be able to get to these details in three ways.
1. By clicking a hero in the dashboard.
1. By clicking a hero in the heroes list.
1. By pasting a "deep link" URL into the browser address bar that identifies the hero to display.

View File

@ -7,15 +7,16 @@ describe('site App', function() {
beforeEach(() => {
SitePage.setWindowWidth(1050); // Make the window wide enough to show the SideNav side-by-side.
page = new SitePage();
page.navigateTo();
});
it('should show features text after clicking "Features"', () => {
page.navigateTo('');
page.getTopMenuLink('features').click();
expect(page.getDocViewerText()).toMatch(/Progressive web apps/i);
});
it('should set appropriate window titles', () => {
page.navigateTo('');
expect(browser.getTitle()).toBe('Angular');
page.getTopMenuLink('features').click();
@ -25,9 +26,9 @@ describe('site App', function() {
expect(browser.getTitle()).toBe('Angular');
});
it('should show the tutorial index page at `/tutorial/` after jitterbugging through features', () => {
it('should show the tutorial index page at `/tutorial` after jitterbugging through features', () => {
// check that we can navigate directly to the tutorial page
page.navigateTo('tutorial/');
page.navigateTo('tutorial');
expect(page.getDocViewerText()).toMatch(/Tutorial: Tour of Heroes/i);
// navigate to a different page
@ -52,24 +53,26 @@ describe('site App', function() {
describe('scrolling to the top', () => {
it('should scroll to the top when navigating to another page', () => {
page.navigateTo('guide/security');
browser.sleep(1000); // Wait for initial async scroll-to-top after `onDocRendered`.
page.scrollToBottom();
page.getScrollTop().then(scrollTop => expect(scrollTop).toBeGreaterThan(0));
expect(page.getScrollTop()).toBeGreaterThan(0);
page.navigateTo('api');
page.getScrollTop().then(scrollTop => expect(scrollTop).toBe(0));
page.getNavItem(/api/i).click();
browser.waitForAngular();
expect(page.locationPath()).toBe('/api');
expect(page.getScrollTop()).toBe(0);
});
it('should scroll to the top when navigating to the same page', () => {
page.navigateTo('guide/security');
browser.sleep(1000); // Wait for initial async scroll-to-top after `onDocRendered`.
page.scrollToBottom();
page.getScrollTop().then(scrollTop => expect(scrollTop).toBeGreaterThan(0));
expect(page.getScrollTop()).toBeGreaterThan(0);
page.navigateTo('guide/security');
page.getScrollTop().then(scrollTop => expect(scrollTop).toBe(0));
page.getNavItem(/security/i).click();
browser.waitForAngular();
expect(page.locationPath()).toBe('/guide/security');
expect(page.getScrollTop()).toBe(0);
});
});
@ -87,42 +90,50 @@ describe('site App', function() {
page.navigateTo('api');
page.locationPath()
.then(p => path = p)
.then(() => page.ga().then(calls => {
.then(() => page.ga())
.then(calls => {
// The last call (length-1) will be the `send` command
// The second to last call (length-2) will be the command to `set` the page url
expect(calls[calls.length - 2]).toEqual(['set', 'page', path]);
done();
}));
});
});
it('should call ga with new URL on navigation', done => {
let path: string;
page.navigateTo('');
page.getTopMenuLink('features').click();
page.locationPath()
.then(p => path = p)
.then(() => page.ga().then(calls => {
.then(() => page.ga())
.then(calls => {
// The last call (length-1) will be the `send` command
// The second to last call (length-2) will be the command to `set` the page url
expect(calls[calls.length - 2]).toEqual(['set', 'page', path]);
done();
}));
});
});
});
describe('search', () => {
it('should find pages when searching by a partial word in the title', () => {
page.navigateTo('');
page.enterSearch('ngCont');
expect(page.getSearchResults().map(link => link.getText())).toContain('NgControl');
expect(page.getSearchResults()).toContain('NgControl');
page.enterSearch('accessor');
expect(page.getSearchResults().map(link => link.getText())).toContain('ControlValueAccessor');
expect(page.getSearchResults()).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');
const results = page.getSearchResults();
expect(results).toContain('Http');
expect(results).toContain('Router');
});
});
});

View File

@ -28,8 +28,11 @@ export class SitePage {
ga() { return browser.executeScript('return window["ga"].q') as promise.Promise<any[][]>; }
locationPath() { return browser.executeScript('return document.location.pathname') as promise.Promise<string>; }
navigateTo(pageUrl = '') {
return browser.get('/' + pageUrl);
navigateTo(pageUrl) {
// Navigate to the page, disable animations, and wait for Angular.
return browser.get('/' + pageUrl)
.then(() => browser.executeScript('document.body.classList.add(\'no-animations\')'))
.then(() => browser.waitForAngular());
}
getDocViewerText() {
@ -59,6 +62,6 @@ export class SitePage {
getSearchResults() {
const results = element.all(by.css('.search-results li'));
browser.wait(ExpectedConditions.presenceOf(results.first()), 8000);
return results;
return results.map(link => link.getText());
}
}

View File

@ -40,7 +40,12 @@
{"type": 301, "source": "/docs/ts/latest/:any*", "destination": "/:any*"},
// aot-compiler.md and metadata.md combined into aot-compiler.md - issue #19510
{"type": 301, "source": "/guide/metadata", "destination": "/guide/aot-compiler"}
{"type": 301, "source": "/guide/metadata", "destination": "/guide/aot-compiler"},
// service-worker-getstart.md, service-worker-comm.md, service-worker-configref.md
{"type": 301, "source": "/guide/service-worker-getstart", "destination": "/guide/service-worker-getting-started"},
{"type": 301, "source": "/guide/service-worker-comm", "destination": "/guide/service-worker-communications"},
{"type": 301, "source": "/guide/service-worker-configref", "destination": "/guide/service-worker-config"}
],
"rewrites": [
{

View File

@ -56,39 +56,39 @@
"~~check-env": "node scripts/check-environment",
"~~build": "ng build --target=production --environment=stable -sm",
"post~~build": "yarn sw-manifest && yarn sw-copy",
"~~update-webdriver": "webdriver-manager update --standalone false --gecko false"
"~~update-webdriver": "webdriver-manager update --standalone false --gecko false $CHROMEDRIVER_VERSION_ARG"
},
"engines": {
"node": ">=6.9.5 <7.0.0",
"node": ">=8.9.1 <9.0.0",
"yarn": ">=1.0.2 <2.0.0"
},
"private": true,
"dependencies": {
"@angular/animations": "^5.0.0",
"@angular/cdk": "^2.0.0-beta.12",
"@angular/common": "^5.0.0",
"@angular/compiler": "^5.0.0",
"@angular/core": "^5.0.0-rc.9",
"@angular/forms": "^5.0.0",
"@angular/http": "^5.0.0",
"@angular/material": "^2.0.0-beta.12",
"@angular/platform-browser": "^5.0.0",
"@angular/platform-browser-dynamic": "^5.0.0",
"@angular/platform-server": "^5.0.0",
"@angular/router": "^5.0.0",
"@angular/animations": "^5.1.0-beta.2",
"@angular/cdk": "^5.0.0-rc.1",
"@angular/common": "^5.1.0-beta.2",
"@angular/compiler": "^5.1.0-beta.2",
"@angular/core": "^5.1.0-beta.2",
"@angular/forms": "^5.1.0-beta.2",
"@angular/http": "^5.1.0-beta.2",
"@angular/material": "^5.0.0-rc.1",
"@angular/platform-browser": "^5.1.0-beta.2",
"@angular/platform-browser-dynamic": "^5.1.0-beta.2",
"@angular/platform-server": "^5.1.0-beta.2",
"@angular/router": "^5.1.0-beta.2",
"@angular/service-worker": "^1.0.0-beta.16",
"classlist.js": "^1.1.20150312",
"core-js": "^2.4.1",
"jasmine": "^2.6.0",
"ng-pwa-tools": "^0.0.10",
"rxjs": "^5.5.0",
"rxjs": "^5.5.2",
"tslib": "^1.7.1",
"web-animations-js": "^2.2.5",
"zone.js": "0.8.16"
"zone.js": "^0.8.19"
},
"devDependencies": {
"@angular/cli": "^1.5.0",
"@angular/compiler-cli": "^5.0.0",
"@angular/cli": "^1.6.0-rc.0",
"@angular/compiler-cli": "^5.1.0-beta.2",
"@types/jasmine": "^2.5.52",
"@types/jasminewd2": "^2.0.3",
"@types/node": "~6.0.60",
@ -113,7 +113,7 @@
"http-server": "^0.9.0",
"ignore": "^3.3.3",
"image-size": "^0.5.1",
"jasmine-core": "^2.6.4",
"jasmine-core": "^2.8.0",
"jasmine-spec-reporter": "^4.1.0",
"jsdom": "^9.12.0",
"karma": "^1.7.0",

View File

@ -1,3 +1,21 @@
{
"aio":{"master":{"change":"application","gzip7":{"inline":925,"main":119519,"polyfills":11863},"gzip9":{"inline":925,"main":119301,"polyfills":11861},"uncompressed":{"inline":1533,"main":486493,"polyfills":37068}}}
}
"aio": {
"master": {
"gzip7": {
"inline": 941,
"main": 116124,
"polyfills": 13102
},
"gzip9": {
"inline": 941,
"main": 115954,
"polyfills": 13104
},
"uncompressed": {
"inline": 1558,
"main": 456432,
"polyfills": 40684
}
}
}
}

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -eu -o pipefail

View File

@ -4,12 +4,14 @@
<mat-progress-bar mode="indeterminate" color="warn"></mat-progress-bar>
</div>
<mat-toolbar color="primary" class="app-toolbar">
<button class="hamburger" [class.starting]="isStarting" mat-button
(click)="sidenav.toggle()" title="Docs menu">
<mat-icon [ngClass]="{'sidenav-open': !isSideBySide }" svgIcon="menu"></mat-icon>
<mat-toolbar color="primary" class="app-toolbar" [class.transitioning]="isTransitioning">
<button mat-button class="hamburger" (click)="sidenav.toggle()" title="Docs menu">
<mat-icon svgIcon="menu"></mat-icon>
</button>
<a class="nav-link home" href="/"><img src="{{ homeImageUrl }}" title="Home" alt="Home"></a>
<a class="nav-link home" href="/" [ngSwitch]="isSideBySide">
<img *ngSwitchCase="true" src="assets/images/logos/angular/logo-nav@2x.png" width="150" height="40" title="Home" alt="Home">
<img *ngSwitchDefault src="assets/images/logos/angular/shield-large.svg" width="37" height="40" title="Home" alt="Home">
</a>
<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>
</mat-toolbar>
@ -17,7 +19,7 @@
<mat-sidenav-container class="sidenav-container" [class.starting]="isStarting" [class.has-floating-toc]="hasFloatingToc" role="main">
<mat-sidenav [ngClass]="{'collapsed': !isSideBySide }" #sidenav class="sidenav" [opened]="isOpened" [mode]="mode" (open)="updateHostClasses()" (close)="updateHostClasses()">
<mat-sidenav [ngClass]="{'collapsed': !isSideBySide}" #sidenav class="sidenav" [opened]="isOpened" [mode]="mode" (open)="updateHostClasses()" (close)="updateHostClasses()">
<aio-nav-menu *ngIf="!isSideBySide" [nodes]="topMenuNarrowNodes" [currentNode]="currentNodes?.TopBarNarrow" [isWide]="false"></aio-nav-menu>
<aio-nav-menu [nodes]="sideNavNodes" [currentNode]="currentNodes?.SideNav" [isWide]="isSideBySide"></aio-nav-menu>
@ -28,7 +30,13 @@
<section class="sidenav-content" [id]="pageId" role="content">
<aio-mode-banner [mode]="deployment.mode" [version]="versionInfo"></aio-mode-banner>
<aio-doc-viewer [doc]="currentDocument" (docRendered)="onDocRendered()"></aio-doc-viewer>
<aio-doc-viewer [class.no-animations]="isStarting"
[doc]="currentDocument"
(docReady)="onDocReady()"
(docRemoved)="onDocRemoved()"
(docInserted)="onDocInserted()"
(docRendered)="onDocRendered()">
</aio-doc-viewer>
<aio-dt [on]="dtOn" [(doc)]="currentDocument"></aio-dt>
</section>

View File

@ -6,12 +6,14 @@ import { HttpClient } from '@angular/common/http';
import { MatProgressBar, MatSidenav } from '@angular/material';
import { By } from '@angular/platform-browser';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { AppComponent } from './app.component';
import { AppModule } from './app.module';
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
import { Deployment } from 'app/shared/deployment.service';
import { EmbedComponentsService } from 'app/embed-components/embed-components.service';
import { GaService } from 'app/shared/ga.service';
import { LocationService } from 'app/shared/location.service';
import { Logger } from 'app/shared/logger.service';
@ -24,7 +26,7 @@ import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
import { SearchResultsComponent } from 'app/shared/search-results/search-results.component';
import { SearchService } from 'app/search/search.service';
import { SelectComponent } from 'app/shared/select/select.component';
import { TocComponent } from 'app/embedded/toc/toc.component';
import { TocComponent } from 'app/layout/toc/toc.component';
import { TocItem, TocService } from 'app/shared/toc.service';
const sideBySideBreakPoint = 992;
@ -55,13 +57,18 @@ describe('AppComponent', () => {
tocService = de.injector.get(TocService);
};
describe('with proper DocViewer', () => {
beforeEach(() => {
DocViewerComponent.animationsEnabled = false;
createTestingModule('a/b');
initializeTest();
});
afterEach(() => DocViewerComponent.animationsEnabled = true);
it('should create', () => {
expect(component).toBeDefined();
});
@ -147,34 +154,35 @@ describe('AppComponent', () => {
});
describe('SideNav when side-by-side (wide)', () => {
const navigateTo = (path: string) => {
locationService.go(path);
component.updateSideNav();
fixture.detectChanges();
};
beforeEach(() => {
component.onResize(sideBySideBreakPoint + 1); // side-by-side
});
it('should open when nav to a guide page (guide/pipes)', () => {
locationService.go('guide/pipes');
fixture.detectChanges();
navigateTo('guide/pipes');
expect(sidenav.opened).toBe(true);
});
it('should open when nav to an api page', () => {
locationService.go('api/a/b/c/d');
fixture.detectChanges();
navigateTo('api/a/b/c/d');
expect(sidenav.opened).toBe(true);
});
it('should be closed when nav to a marketing page (features)', () => {
locationService.go('features');
fixture.detectChanges();
navigateTo('features');
expect(sidenav.opened).toBe(false);
});
describe('when manually closed', () => {
beforeEach(() => {
locationService.go('guide/pipes');
fixture.detectChanges();
navigateTo('guide/pipes');
hamburger.click();
fixture.detectChanges();
});
@ -184,56 +192,53 @@ describe('AppComponent', () => {
});
it('should stay closed when nav from one guide page to another', () => {
locationService.go('guide/bags');
fixture.detectChanges();
navigateTo('guide/bags');
expect(sidenav.opened).toBe(false);
});
it('should stay closed when nav from a guide page to api page', () => {
locationService.go('api');
fixture.detectChanges();
navigateTo('api');
expect(sidenav.opened).toBe(false);
});
it('should reopen when nav to market page and back to guide page', () => {
locationService.go('features');
fixture.detectChanges();
locationService.go('guide/bags');
fixture.detectChanges();
navigateTo('features');
navigateTo('guide/bags');
expect(sidenav.opened).toBe(true);
});
});
});
describe('SideNav when NOT side-by-side (narrow)', () => {
const navigateTo = (path: string) => {
locationService.go(path);
component.updateSideNav();
fixture.detectChanges();
};
beforeEach(() => {
component.onResize(sideBySideBreakPoint - 1); // NOT side-by-side
});
it('should be closed when nav to a guide page (guide/pipes)', () => {
locationService.go('guide/pipes');
fixture.detectChanges();
navigateTo('guide/pipes');
expect(sidenav.opened).toBe(false);
});
it('should be closed when nav to an api page', () => {
locationService.go('api/a/b/c/d');
fixture.detectChanges();
navigateTo('api/a/b/c/d');
expect(sidenav.opened).toBe(false);
});
it('should be closed when nav to a marketing page (features)', () => {
locationService.go('features');
fixture.detectChanges();
navigateTo('features');
expect(sidenav.opened).toBe(false);
});
describe('when manually opened', () => {
beforeEach(() => {
locationService.go('guide/pipes');
fixture.detectChanges();
navigateTo('guide/pipes');
hamburger.click();
fixture.detectChanges();
});
@ -246,25 +251,22 @@ describe('AppComponent', () => {
const sidenavBackdrop = fixture.debugElement.query(By.css('.mat-drawer-backdrop')).nativeElement;
sidenavBackdrop.click();
fixture.detectChanges();
expect(sidenav.opened).toBe(false);
expect(sidenav.opened).toBe(false);
});
it('should close when nav to another guide page', () => {
locationService.go('guide/bags');
fixture.detectChanges();
expect(sidenav.opened).toBe(false);
navigateTo('guide/bags');
expect(sidenav.opened).toBe(false);
});
it('should close when nav to api page', () => {
locationService.go('api');
fixture.detectChanges();
expect(sidenav.opened).toBe(false);
navigateTo('api');
expect(sidenav.opened).toBe(false);
});
it('should close again when nav to market page', () => {
locationService.go('features');
fixture.detectChanges();
expect(sidenav.opened).toBe(false);
navigateTo('features');
expect(sidenav.opened).toBe(false);
});
});
@ -318,96 +320,7 @@ describe('AppComponent', () => {
});
});
describe('pageId', () => {
it('should set the id of the doc viewer container based on the current doc', () => {
const container = fixture.debugElement.query(By.css('section.sidenav-content'));
locationService.go('guide/pipes');
fixture.detectChanges();
expect(component.pageId).toEqual('guide-pipes');
expect(container.properties['id']).toEqual('guide-pipes');
locationService.go('news');
fixture.detectChanges();
expect(component.pageId).toEqual('news');
expect(container.properties['id']).toEqual('news');
locationService.go('');
fixture.detectChanges();
expect(component.pageId).toEqual('home');
expect(container.properties['id']).toEqual('home');
});
it('should not be affected by changes to the query', () => {
const container = fixture.debugElement.query(By.css('section.sidenav-content'));
locationService.go('guide/pipes');
fixture.detectChanges();
locationService.go('guide/other?search=http');
fixture.detectChanges();
expect(component.pageId).toEqual('guide-other');
expect(container.properties['id']).toEqual('guide-other');
});
});
describe('hostClasses', () => {
it('should set the css classes of the host container based on the current doc and navigation view', () => {
locationService.go('guide/pipes');
fixture.detectChanges();
checkHostClass('page', 'guide-pipes');
checkHostClass('folder', 'guide');
checkHostClass('view', 'SideNav');
locationService.go('features');
fixture.detectChanges();
checkHostClass('page', 'features');
checkHostClass('folder', 'features');
checkHostClass('view', 'TopBar');
locationService.go('');
fixture.detectChanges();
checkHostClass('page', 'home');
checkHostClass('folder', 'home');
checkHostClass('view', '');
});
it('should set the css class of the host container based on the open/closed state of the side nav', () => {
locationService.go('guide/pipes');
fixture.detectChanges();
checkHostClass('sidenav', 'open');
sidenav.close();
sidenav.onClose.next();
fixture.detectChanges();
checkHostClass('sidenav', 'closed');
sidenav.open();
sidenav.onOpen.next();
fixture.detectChanges();
checkHostClass('sidenav', 'open');
});
it('should set the css class of the host container based on the initial deployment mode', () => {
createTestingModule('a/b', 'archive');
initializeTest();
checkHostClass('mode', 'archive');
});
function checkHostClass(type, value) {
const host = fixture.debugElement;
const classes = host.properties['className'];
const classArray = classes.split(' ').filter(c => c.indexOf(`${type}-`) === 0);
expect(classArray.length).toBeLessThanOrEqual(1, `"${classes}" should have only one class matching ${type}-*`);
expect(classArray).toEqual([`${type}-${value}`], `"${classes}" should contain ${type}-${value}`);
}
});
describe('currentDocument', () => {
it('should display a guide page (guide/pipes)', () => {
locationService.go('guide/pipes');
fixture.detectChanges();
@ -447,16 +360,19 @@ describe('AppComponent', () => {
const scrollDelay = 500;
let scrollService: ScrollService;
let scrollSpy: jasmine.Spy;
let scrollToTopSpy: jasmine.Spy;
beforeEach(() => {
scrollService = fixture.debugElement.injector.get(ScrollService);
scrollSpy = spyOn(scrollService, 'scroll');
scrollToTopSpy = spyOn(scrollService, 'scrollToTop');
});
it('should not scroll immediately when the docId (path) changes', () => {
locationService.go('guide/pipes');
// deliberately not calling `fixture.detectChanges` because don't want `onDocRendered`
// deliberately not calling `fixture.detectChanges` because don't want `onDocInserted`
expect(scrollSpy).not.toHaveBeenCalled();
expect(scrollToTopSpy).not.toHaveBeenCalled();
});
it('should scroll when just the hash changes (# alone)', () => {
@ -486,7 +402,7 @@ describe('AppComponent', () => {
expect(scrollSpy).toHaveBeenCalledTimes(1);
});
it('should scroll when e-nav to the empty path', () => {
it('should scroll when re-nav to the empty path', () => {
locationService.go('');
scrollSpy.calls.reset();
@ -494,17 +410,29 @@ describe('AppComponent', () => {
expect(scrollSpy).toHaveBeenCalledTimes(1);
});
it('should scroll after a delay when call onDocRendered directly', fakeAsync(() => {
component.onDocRendered();
it('should scroll to top when call `onDocRemoved` directly', () => {
scrollToTopSpy.calls.reset();
component.onDocRemoved();
expect(scrollToTopSpy).toHaveBeenCalled();
});
it('should scroll after a delay when call `onDocInserted` directly', fakeAsync(() => {
component.onDocInserted();
expect(scrollSpy).not.toHaveBeenCalled();
tick(scrollDelay);
expect(scrollSpy).toHaveBeenCalled();
}));
it('should scroll (via onDocRendered) when finish navigating to a new doc', fakeAsync(() => {
it('should scroll (via `onDocInserted`) when finish navigating to a new doc', fakeAsync(() => {
expect(scrollToTopSpy).not.toHaveBeenCalled();
locationService.go('guide/pipes');
fixture.detectChanges(); // triggers the event that calls onDocRendered
fixture.detectChanges(); // triggers the event that calls `onDocInserted`
expect(scrollToTopSpy).toHaveBeenCalled();
expect(scrollSpy).not.toHaveBeenCalled();
tick(scrollDelay);
expect(scrollSpy).toHaveBeenCalled();
}));
@ -795,14 +723,14 @@ describe('AppComponent', () => {
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
});
it('should redirect to `docs` if deployment mode is `next` and not at a docs page', () => {
it('should not redirect if deployment mode is `next`', () => {
createTestingModule('', 'next');
initializeTest();
expect(TestBed.get(LocationService).replace).toHaveBeenCalledWith('docs');
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
createTestingModule('resources', 'next');
initializeTest();
expect(TestBed.get(LocationService).replace).toHaveBeenCalledWith('docs');
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
createTestingModule('guide/aot-compiler', 'next');
initializeTest();
@ -829,7 +757,7 @@ describe('AppComponent', () => {
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
});
it('should not redirect to `docs` if deployment mode is `stable` and not at a docs page', () => {
it('should not redirect to `docs` if deployment mode is `stable`', () => {
createTestingModule('', 'stable');
initializeTest();
expect(TestBed.get(LocationService).replace).not.toHaveBeenCalled();
@ -867,7 +795,9 @@ describe('AppComponent', () => {
describe('with mocked DocViewer', () => {
const getDocViewer = () => fixture.debugElement.query(By.css('aio-doc-viewer'));
const triggerDocRendered = () => getDocViewer().triggerEventHandler('docRendered', {});
const triggerDocViewerEvent =
(evt: 'docReady' | 'docRemoved' | 'docInserted' | 'docRendered') =>
getDocViewer().triggerEventHandler(evt, undefined);
beforeEach(() => {
createTestingModule('a/b');
@ -879,7 +809,7 @@ describe('AppComponent', () => {
});
describe('initial rendering', () => {
it('should initially add the starting class until the first document is rendered', fakeAsync(() => {
it('should initially add the starting class until a document is rendered', () => {
const getSidenavContainer = () => fixture.debugElement.query(By.css('mat-sidenav-container'));
initializeTest();
@ -887,21 +817,181 @@ describe('AppComponent', () => {
expect(component.isStarting).toBe(true);
expect(getSidenavContainer().classes['starting']).toBe(true);
triggerDocRendered();
fixture.detectChanges();
expect(component.isStarting).toBe(true);
expect(getSidenavContainer().classes['starting']).toBe(true);
tick(499);
fixture.detectChanges();
expect(component.isStarting).toBe(true);
expect(getSidenavContainer().classes['starting']).toBe(true);
tick(2);
triggerDocViewerEvent('docRendered');
fixture.detectChanges();
expect(component.isStarting).toBe(false);
expect(getSidenavContainer().classes['starting']).toBe(false);
}));
});
it('should initially disable animations on the DocViewer for the first rendering', () => {
initializeTest();
expect(component.isStarting).toBe(true);
expect(docViewer.classList.contains('no-animations')).toBe(true);
triggerDocViewerEvent('docRendered');
fixture.detectChanges();
expect(component.isStarting).toBe(false);
expect(docViewer.classList.contains('no-animations')).toBe(false);
});
});
describe('subsequent rendering', () => {
beforeEach(jasmine.clock().install);
afterEach(jasmine.clock().uninstall);
it('should set the transitioning class on `.app-toolbar` while a document is being rendered', () => {
const getToolbar = () => fixture.debugElement.query(By.css('.app-toolbar'));
initializeTest();
// Initially, `isTransitoning` is true.
expect(component.isTransitioning).toBe(true);
expect(getToolbar().classes['transitioning']).toBe(true);
triggerDocViewerEvent('docRendered');
fixture.detectChanges();
expect(component.isTransitioning).toBe(false);
expect(getToolbar().classes['transitioning']).toBe(false);
// While a document is being rendered, `isTransitoning` is set to true.
triggerDocViewerEvent('docReady');
fixture.detectChanges();
expect(component.isTransitioning).toBe(true);
expect(getToolbar().classes['transitioning']).toBe(true);
triggerDocViewerEvent('docRendered');
fixture.detectChanges();
expect(component.isTransitioning).toBe(false);
expect(getToolbar().classes['transitioning']).toBe(false);
});
it('should update the sidenav state as soon as a new document is inserted', () => {
initializeTest();
const updateSideNavSpy = spyOn(component, 'updateSideNav');
triggerDocViewerEvent('docInserted');
jasmine.clock().tick(0);
expect(updateSideNavSpy).toHaveBeenCalledTimes(1);
triggerDocViewerEvent('docInserted');
jasmine.clock().tick(0);
expect(updateSideNavSpy).toHaveBeenCalledTimes(2);
});
});
describe('pageId', () => {
const navigateTo = (path: string) => {
locationService.go(path);
triggerDocViewerEvent('docInserted');
jasmine.clock().tick(0);
fixture.detectChanges();
};
beforeEach(jasmine.clock().install);
afterEach(jasmine.clock().uninstall);
it('should set the id of the doc viewer container based on the current doc', () => {
initializeTest();
const container = fixture.debugElement.query(By.css('section.sidenav-content'));
navigateTo('guide/pipes');
expect(component.pageId).toEqual('guide-pipes');
expect(container.properties['id']).toEqual('guide-pipes');
navigateTo('news');
expect(component.pageId).toEqual('news');
expect(container.properties['id']).toEqual('news');
navigateTo('');
expect(component.pageId).toEqual('home');
expect(container.properties['id']).toEqual('home');
});
it('should not be affected by changes to the query', () => {
initializeTest();
const container = fixture.debugElement.query(By.css('section.sidenav-content'));
navigateTo('guide/pipes');
navigateTo('guide/other?search=http');
expect(component.pageId).toEqual('guide-other');
expect(container.properties['id']).toEqual('guide-other');
});
});
describe('hostClasses', () => {
const triggerUpdateHostClasses = () => {
triggerDocViewerEvent('docInserted');
jasmine.clock().tick(0);
fixture.detectChanges();
};
const navigateTo = (path: string) => {
locationService.go(path);
triggerUpdateHostClasses();
};
beforeEach(jasmine.clock().install);
afterEach(jasmine.clock().uninstall);
it('should set the css classes of the host container based on the current doc and navigation view', () => {
initializeTest();
navigateTo('guide/pipes');
checkHostClass('page', 'guide-pipes');
checkHostClass('folder', 'guide');
checkHostClass('view', 'SideNav');
navigateTo('features');
checkHostClass('page', 'features');
checkHostClass('folder', 'features');
checkHostClass('view', 'TopBar');
navigateTo('');
checkHostClass('page', 'home');
checkHostClass('folder', 'home');
checkHostClass('view', '');
});
it('should set the css class of the host container based on the open/closed state of the side nav', async () => {
initializeTest();
navigateTo('guide/pipes');
checkHostClass('sidenav', 'open');
sidenav.close();
await waitForEmit(sidenav.onClose);
fixture.detectChanges();
checkHostClass('sidenav', 'closed');
sidenav.open();
await waitForEmit(sidenav.onOpen);
fixture.detectChanges();
checkHostClass('sidenav', 'open');
function waitForEmit(emitter: Observable<void>): Promise<void> {
return new Promise(resolve => {
emitter.subscribe(resolve);
fixture.detectChanges();
});
}
});
it('should set the css class of the host container based on the initial deployment mode', () => {
createTestingModule('a/b', 'archive');
initializeTest();
triggerUpdateHostClasses();
checkHostClass('mode', 'archive');
});
function checkHostClass(type, value) {
const host = fixture.debugElement;
const classes = host.properties['className'];
const classArray = classes.split(' ').filter(c => c.indexOf(`${type}-`) === 0);
expect(classArray.length).toBeLessThanOrEqual(1, `"${classes}" should have only one class matching ${type}-*`);
expect(classArray).toEqual([`${type}-${value}`], `"${classes}" should contain ${type}-${value}`);
}
});
describe('progress bar', () => {
@ -910,7 +1000,7 @@ describe('AppComponent', () => {
const getProgressBar = () => fixture.debugElement.query(By.directive(MatProgressBar));
const initializeAndCompleteNavigation = () => {
initializeTest();
triggerDocRendered();
triggerDocViewerEvent('docReady');
tick(HIDE_DELAY);
};
@ -947,7 +1037,7 @@ describe('AppComponent', () => {
it('should not be shown when re-navigating to the empty path', fakeAsync(() => {
initializeAndCompleteNavigation();
locationService.urlSubject.next('');
triggerDocRendered();
triggerDocViewerEvent('docReady');
locationService.urlSubject.next('');
@ -958,12 +1048,12 @@ describe('AppComponent', () => {
tick(HIDE_DELAY); // Fire the remaining timer or `fakeAsync()` complains.
}));
it('should not be shown if the doc is rendered quickly', fakeAsync(() => {
it('should not be shown if the doc is prepared quickly', fakeAsync(() => {
initializeAndCompleteNavigation();
locationService.urlSubject.next('c/d');
tick(SHOW_DELAY - 1);
triggerDocRendered();
triggerDocViewerEvent('docReady');
tick(1);
fixture.detectChanges();
@ -972,12 +1062,12 @@ describe('AppComponent', () => {
tick(HIDE_DELAY); // Fire the remaining timer or `fakeAsync()` complains.
}));
it('should be shown if rendering the doc takes too long', fakeAsync(() => {
it('should be shown if preparing the doc takes too long', fakeAsync(() => {
initializeAndCompleteNavigation();
locationService.urlSubject.next('c/d');
tick(SHOW_DELAY);
triggerDocRendered();
triggerDocViewerEvent('docReady');
fixture.detectChanges();
expect(getProgressBar()).toBeTruthy();
@ -985,12 +1075,12 @@ describe('AppComponent', () => {
tick(HIDE_DELAY); // Fire the remaining timer or `fakeAsync()` complains.
}));
it('should be hidden (after a delay) once the doc is rendered', fakeAsync(() => {
it('should be hidden (after a delay) once the doc has been prepared', fakeAsync(() => {
initializeAndCompleteNavigation();
locationService.urlSubject.next('c/d');
tick(SHOW_DELAY);
triggerDocRendered();
triggerDocViewerEvent('docReady');
fixture.detectChanges();
expect(getProgressBar()).toBeTruthy();
@ -1007,10 +1097,10 @@ describe('AppComponent', () => {
it('should only take the latest request into account', fakeAsync(() => {
initializeAndCompleteNavigation();
locationService.urlSubject.next('c/d'); // The URL changes.
locationService.urlSubject.next('e/f'); // The URL changes again before `onDocRendered()`.
locationService.urlSubject.next('e/f'); // The URL changes again before `onDocReady()`.
tick(SHOW_DELAY - 1); // `onDocRendered()` is triggered (for the last doc),
triggerDocRendered(); // before the progress bar is shown.
tick(SHOW_DELAY - 1); // `onDocReady()` is triggered (for the last doc),
triggerDocViewerEvent('docReady'); // before the progress bar is shown.
tick(1);
fixture.detectChanges();
@ -1033,6 +1123,7 @@ function createTestingModule(initialUrl: string, mode: string = 'stable') {
imports: [ AppModule ],
providers: [
{ provide: APP_BASE_HREF, useValue: '/' },
{ provide: EmbedComponentsService, useClass: TestEmbedComponentsService },
{ provide: GaService, useClass: TestGaService },
{ provide: HttpClient, useClass: TestHttpClient },
{ provide: LocationService, useFactory: () => mockLocationService },
@ -1047,6 +1138,10 @@ function createTestingModule(initialUrl: string, mode: string = 'stable') {
});
}
class TestEmbedComponentsService {
embedInto = jasmine.createSpy('embedInto').and.returnValue(of([]));
}
class TestGaService {
locationChanged = jasmine.createSpy('locationChanged');
}

View File

@ -1,10 +1,9 @@
import { Component, ElementRef, HostBinding, HostListener, OnInit,
QueryList, ViewChild, ViewChildren } from '@angular/core';
import { MatSidenav } from '@angular/material';
import { MatSidenav } from '@angular/material/sidenav';
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 { ScrollService } from 'app/shared/scroll.service';
@ -16,6 +15,7 @@ import { TocService } from 'app/shared/toc.service';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { combineLatest } from 'rxjs/observable/combineLatest';
import 'rxjs/add/operator/first';
const sideNavView = 'SideNav';
@ -58,6 +58,7 @@ export class AppComponent implements OnInit {
isFetching = false;
isStarting = true;
isTransitioning = true;
isSideBySide = false;
private isFetchingTimeout: any;
private isSideNavDoc = false;
@ -75,18 +76,9 @@ export class AppComponent implements OnInit {
versionInfo: VersionInfo;
get homeImageUrl() {
return this.isSideBySide ?
'assets/images/logos/angular/logo-nav@2x.png' :
'assets/images/logos/angular/shield-large.svg';
}
get isOpened() { return this.isSideBySide && this.isSideNavDoc; }
get mode() { return this.isSideBySide ? 'side' : 'over'; }
// Need the doc-viewer element for scrolling the contents
@ViewChild(DocViewerComponent, { read: ElementRef })
docViewer: ElementRef;
// Search related properties
showSearchResults = false;
searchResults: Observable<SearchResults>;
@ -120,17 +112,18 @@ export class AppComponent implements OnInit {
/* No need to unsubscribe because this root component never dies */
this.documentService.currentDocument.subscribe(doc => {
this.currentDocument = doc;
this.setPageId(doc.id);
this.setFolderId(doc.id);
this.updateHostClasses();
});
this.documentService.currentDocument.subscribe(doc => this.currentDocument = doc);
// Generally, we want to delay updating the host classes for the new document, until after the
// leaving document has been removed (to avoid having the styles for the new document applied
// prematurely).
// On the first document, though, (when we know there is no previous document), we want to
// ensure the styles are applied as soon as possible to avoid flicker.
this.documentService.currentDocument.first().subscribe(doc => this.updateHostClassesForDoc(doc));
this.locationService.currentPath.subscribe(path => {
// Redirect to docs if we are in not in stable mode and are not hitting a docs page
// Redirect to docs if we are in archive mode and are not hitting a docs page
// (i.e. we have arrived at a marketing page)
if (this.deployment.mode !== 'stable' && !/^(docs$|api|guide|tutorial)/.test(path)) {
if (this.deployment.mode === 'archive' && !/^(docs$|api|guide|tutorial)/.test(path)) {
this.locationService.replace('docs');
}
if (path === this.currentPath) {
@ -146,21 +139,7 @@ export class AppComponent implements OnInit {
}
});
this.navigationService.currentNodes.subscribe(currentNodes => {
this.currentNodes = currentNodes;
// Preserve current sidenav open state by default
let openSideNav = this.sidenav.opened;
const isSideNavDoc = !!currentNodes[sideNavView];
if (this.isSideNavDoc !== isSideNavDoc) {
// View type changed. Is it now a sidenav view (e.g, guide or tutorial)?
// Open if changed to a sidenav doc; close if changed to a marketing doc.
openSideNav = this.isSideNavDoc = isSideNavDoc;
}
// May be open or closed when wide; always closed when narrow
this.sideNavToggle(this.isSideBySide ? openSideNav : false);
});
this.navigationService.currentNodes.subscribe(currentNodes => this.currentNodes = currentNodes);
// Compute the version picker list from the current version and the versions in the navigation map
combineLatest(
@ -203,20 +182,41 @@ export class AppComponent implements OnInit {
this.scrollService.scroll();
}
onDocRendered() {
onDocReady() {
// About to transition to new view.
this.isTransitioning = true;
// Stop fetching timeout (which, when render is fast, means progress bar never shown)
clearTimeout(this.isFetchingTimeout);
// Put page in a clean visual state
this.scrollService.scrollToTop();
// If progress bar has been shown, keep it for at least 500ms (to avoid flashing).
setTimeout(() => this.isFetching = false, 500);
}
// Scroll 500ms after the doc-viewer has finished rendering the new doc
// The delay is to allow time for async layout to complete
onDocRemoved() {
// The previous document has been removed.
// Scroll to top to restore a clean visual state for the new document.
this.scrollService.scrollToTop();
}
onDocInserted() {
// TODO: Find a better way to avoid `ExpressionChangedAfterItHasBeenChecked` error.
setTimeout(() => {
this.autoScroll();
this.isStarting = false;
this.isFetching = false;
}, 500);
// Update the SideNav state (if necessary).
this.updateSideNav();
// Update the host classes to match the new document.
this.updateHostClassesForDoc(this.currentDocument);
});
// Scroll 500ms after the new document has been inserted into the doc-viewer.
// The delay is to allow time for async layout to complete.
setTimeout(() => this.autoScroll(), 500);
}
onDocRendered() {
this.isStarting = false;
this.isTransitioning = false;
}
onDocVersionChange(versionIndex: number) {
@ -283,6 +283,27 @@ export class AppComponent implements OnInit {
this.hostClasses = `${mode} ${sideNavOpen} ${pageClass} ${folderClass} ${viewClasses}`;
}
updateHostClassesForDoc(doc: DocumentContents) {
this.setPageId(doc.id);
this.setFolderId(doc.id);
this.updateHostClasses();
}
updateSideNav() {
// Preserve current sidenav open state by default.
let openSideNav = this.sidenav.opened;
const isSideNavDoc = !!this.currentNodes[sideNavView];
if (this.isSideNavDoc !== isSideNavDoc) {
// View type changed. Is it now a sidenav view (e.g, guide or tutorial)?
// Open if changed to a sidenav doc; close if changed to a marketing doc.
openSideNav = this.isSideNavDoc = isSideNavDoc;
}
// May be open or closed when wide; always closed when narrow.
this.sideNavToggle(this.isSideBySide && openSideNav);
}
// Dynamically change height of table of contents container
@HostListener('window:scroll')
onScroll() {
@ -291,7 +312,7 @@ export class AppComponent implements OnInit {
const el = this.hostElement.nativeElement as Element;
this.tocMaxHeightOffset =
el.querySelector('footer').clientHeight +
el.querySelector('mat-toolbar.app-toolbar').clientHeight +
el.querySelector('.app-toolbar').clientHeight +
24; // fudge margin
}

View File

@ -0,0 +1,48 @@
import { TestBed } from '@angular/core/testing';
import { AppModule } from 'app/app.module';
import { ComponentsOrModulePath, EMBEDDED_COMPONENTS } from 'app/embed-components/embed-components.service';
import { embeddedComponents } from 'app/embedded/embedded.module';
describe('AppModule', () => {
let componentsMap: {[multiSelectorstring: string]: ComponentsOrModulePath};
beforeEach(() => {
TestBed.configureTestingModule({imports: [AppModule]});
componentsMap = TestBed.get(EMBEDDED_COMPONENTS);
});
it('should provide a map of selectors to embedded components (or module)', () => {
const allSelectors = Object.keys(componentsMap);
expect(allSelectors.length).toBeGreaterThan(1);
allSelectors.forEach(selector => {
const value = componentsMap[selector];
const isArrayOrString = Array.isArray(value) || (typeof value === 'string');
expect(isArrayOrString).toBe(true);
});
});
it('should provide a list of eagerly-loaded embedded components', () => {
const eagerSelector = Object.keys(componentsMap).find(selector => Array.isArray(componentsMap[selector]));
const selectorCount = eagerSelector.split(',').length;
expect(eagerSelector).not.toBeNull();
expect(selectorCount).toBe(componentsMap[eagerSelector].length);
// For example...
expect(eagerSelector).toContain('aio-toc');
});
it('should provide a list of lazy-loaded embedded components', () => {
const lazySelector = Object.keys(componentsMap).find(selector => selector.includes('code-example'));
const selectorCount = lazySelector.split(',').length;
expect(lazySelector).not.toBeNull();
expect(selectorCount).toBe(embeddedComponents.length);
// For example...
expect(lazySelector).toContain('code-example');
expect(lazySelector).toContain('code-tabs');
expect(lazySelector).toContain('live-example');
});
});

View File

@ -5,36 +5,22 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
import {
MatButtonModule,
MatIconModule,
MatIconRegistry,
MatInputModule,
MatProgressBarModule,
MatSidenavModule,
MatTabsModule,
MatToolbarModule
} from '@angular/material';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule, MatIconRegistry } from '@angular/material/icon';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatToolbarModule } from '@angular/material/toolbar';
import {
Platform
} from '@angular/cdk/platform';
import { ROUTES } from '@angular/router';
// Temporary fix for MatSidenavModule issue:
// crashes with "missing first" operator when SideNav.mode is "over"
import 'rxjs/add/operator/first';
import { SwUpdatesModule } from 'app/sw-updates/sw-updates.module';
import { AppComponent } from 'app/app.component';
import { ApiService } from 'app/embedded/api/api.service';
import { EMBEDDED_COMPONENTS, EmbeddedComponentsMap } from 'app/embed-components/embed-components.service';
import { CustomIconRegistry, SVG_ICONS } from 'app/shared/custom-icon-registry';
import { Deployment } from 'app/shared/deployment.service';
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
import { DtComponent } from 'app/layout/doc-viewer/dt.component';
import { ModeBannerComponent } from 'app/layout/mode-banner/mode-banner.component';
import { EmbeddedModule } from 'app/embedded/embedded.module';
import { GaService } from 'app/shared/ga.service';
import { Logger } from 'app/shared/logger.service';
import { LocationService } from 'app/shared/location.service';
@ -47,11 +33,18 @@ 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 { SearchBoxComponent } from './search/search-box/search-box.component';
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
import { TocComponent } from 'app/layout/toc/toc.component';
import { TocService } from 'app/shared/toc.service';
import { WindowToken, windowProvider } from 'app/shared/window';
import { EmbedComponentsModule } from 'app/embed-components/embed-components.module';
import { SharedModule } from 'app/shared/shared.module';
import { SwUpdatesModule } from 'app/sw-updates/sw-updates.module';
// The path to the `EmbeddedModule`.
const embeddedModulePath = 'app/embedded/embedded.module#EmbeddedModule';
// These are the hardcoded inline svg sources to be used by the `<mat-icon>` component
export const svgIconProviders = [
@ -78,15 +71,13 @@ export const svgIconProviders = [
@NgModule({
imports: [
BrowserModule,
EmbeddedModule,
HttpClientModule,
BrowserAnimationsModule,
EmbedComponentsModule,
HttpClientModule,
MatButtonModule,
MatIconModule,
MatInputModule,
MatProgressBarModule,
MatSidenavModule,
MatTabsModule,
MatToolbarModule,
SwUpdatesModule,
SharedModule
@ -100,10 +91,10 @@ export const svgIconProviders = [
NavMenuComponent,
NavItemComponent,
SearchBoxComponent,
TocComponent,
TopMenuComponent,
],
providers: [
ApiService,
Deployment,
DocumentService,
GaService,
@ -113,15 +104,32 @@ export const svgIconProviders = [
LocationService,
{ provide: MatIconRegistry, useClass: CustomIconRegistry },
NavigationService,
Platform,
ScrollService,
ScrollSpyService,
SearchService,
svgIconProviders,
TocService,
{ provide: WindowToken, useFactory: windowProvider },
{
provide: EMBEDDED_COMPONENTS,
useValue: {
/* tslint:disable: max-line-length */
'aio-toc': [TocComponent],
'aio-api-list, aio-contributor-list, aio-file-not-found-search, aio-resource-list, code-example, code-tabs, current-location, live-example': embeddedModulePath,
/* tslint:enable: max-line-length */
} as EmbeddedComponentsMap,
},
{
// This is currently the only way to get `@angular/cli`
// to split `EmbeddedModule` into a separate chunk :(
provide: ROUTES,
useValue: [{ path: '/embedded', loadChildren: embeddedModulePath }],
multi: true,
},
],
bootstrap: [AppComponent]
entryComponents: [ TocComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule {
}

View File

@ -0,0 +1,13 @@
import { NgModule, NgModuleFactoryLoader, SystemJsNgModuleLoader } from '@angular/core';
import { EmbedComponentsService } from './embed-components.service';
@NgModule({
providers: [
EmbedComponentsService,
{ provide: NgModuleFactoryLoader, useClass: SystemJsNgModuleLoader },
],
})
export class EmbedComponentsModule {
}

View File

@ -0,0 +1,378 @@
import { ComponentFactory, ComponentFactoryResolver, ComponentRef, NgModuleFactoryLoader } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import {
MockNgModuleFactoryLoader, TestEmbedComponentsService, TestModule, mockEmbeddedModulePath,
testEagerEmbeddedComponents, testEagerEmbeddedSelectors, testLazyEmbeddedComponents
} from 'testing/embed-components-utils';
import { EmbedComponentsService, ComponentsOrModulePath } from './embed-components.service';
describe('EmbedComponentsService', () => {
let service: TestEmbedComponentsService;
let host: HTMLElement;
beforeEach(() => {
TestBed.configureTestingModule({imports: [TestModule]});
service = TestBed.get(EmbedComponentsService);
host = document.createElement('div');
});
it('should be instantiated', () => {
expect(service).toEqual(jasmine.any(EmbedComponentsService));
});
describe('#createComponentFactories()', () => {
let factories: typeof service.componentFactories;
let resolver: ComponentFactoryResolver;
const doCreateComponentFactories = () =>
service.createComponentFactories(testEagerEmbeddedComponents, resolver);
beforeEach(() => {
factories = service.componentFactories;
resolver = TestBed.get(ComponentFactoryResolver) as ComponentFactoryResolver;
});
it('should create a factory entry for each component', () => {
expect(factories.size).toBe(0);
doCreateComponentFactories();
expect(factories.size).toBe(testEagerEmbeddedComponents.length);
});
it('should key the factory entries by selector', () => {
doCreateComponentFactories();
const actualSelectors = Array.from(factories.keys());
const expectedSelectors = testEagerEmbeddedSelectors;
expect(actualSelectors).toEqual(expectedSelectors);
});
it('should store the projected content property name', () => {
doCreateComponentFactories();
const actualContentPropNames = Array.from(factories.values()).map(x => x.contentPropertyName);
const expectedContentPropNames = testEagerEmbeddedSelectors.map(x => service.selectorToContentPropertyName(x));
expect(actualContentPropNames).toEqual(expectedContentPropNames);
});
it('should store the factory for each component', () => {
doCreateComponentFactories();
const actualFactories = Array.from(factories.values()).map(x => x.factory);
const expectedComponentTypes = testEagerEmbeddedComponents;
actualFactories.forEach((factory, i) => {
expect(factory).toEqual(jasmine.any(ComponentFactory));
expect(factory.componentType).toBe(expectedComponentTypes[i]);
});
});
});
describe('#createComponents()', () => {
const FooComponent = testEagerEmbeddedComponents[0];
const BarComponent = testEagerEmbeddedComponents[1];
beforeEach(() => service.prepareComponentFactories(testEagerEmbeddedComponents));
it('should apply all embedded components (and return the `ComponentRef`s)', () => {
host.innerHTML = `
<p>Header</p>
<p><aio-eager-foo></aio-eager-foo></p>
<p><aio-eager-bar></aio-eager-bar></p>
<p>Footer</p>
`;
const componentRefs = service.createComponents(host);
expect(host.innerHTML).toContain('Foo Component');
expect(host.innerHTML).toContain('Bar Component');
expect(componentRefs.length).toBe(2);
expect(componentRefs[0].instance).toEqual(jasmine.any(FooComponent));
expect(componentRefs[1].instance).toEqual(jasmine.any(BarComponent));
});
it('should apply embedded components to all matching elements', () => {
host.innerHTML = `
<p>Header</p>
<p><aio-eager-foo></aio-eager-foo></p>
<p><aio-eager-bar></aio-eager-bar></p>
<p><aio-eager-foo></aio-eager-foo></p>
<p><aio-eager-bar></aio-eager-bar></p>
<p>Footer</p>
`;
const componentRefs = service.createComponents(host);
expect(componentRefs.length).toBe(4);
expect(componentRefs[0].instance).toEqual(jasmine.any(FooComponent));
expect(componentRefs[1].instance).toEqual(jasmine.any(FooComponent));
expect(componentRefs[2].instance).toEqual(jasmine.any(BarComponent));
expect(componentRefs[3].instance).toEqual(jasmine.any(BarComponent));
});
it('should allow projecting content by assigning it on the element', () => {
const projectedContent = 'Projected content';
host.innerHTML = `
<p>Header</p>
<p><aio-eager-bar>${projectedContent}</aio-eager-bar></p>
<p>Footer</p>
`;
const componentRefs = service.createComponents(host);
componentRefs[0].changeDetectorRef.detectChanges();
const barEl = host.querySelector('aio-eager-bar');
expect(barEl['aioEagerBarContent']).toBe(projectedContent);
expect(barEl.innerHTML).toContain(projectedContent);
});
// Because `FooComponent` is processed before `BarComponent`...
it('should apply `FooComponent` within `BarComponent`', () => {
host.innerHTML = `
<aio-eager-bar>
<aio-eager-foo></aio-eager-foo>
</aio-eager-bar>
`;
const componentRefs = service.createComponents(host);
componentRefs.forEach(ref => ref.changeDetectorRef.detectChanges());
expect(host.innerHTML).toContain('Foo Component');
expect(host.innerHTML).toContain('Bar Component');
expect(componentRefs.length).toBe(2);
expect(componentRefs[0].instance).toEqual(jasmine.any(FooComponent));
expect(componentRefs[1].instance).toEqual(jasmine.any(BarComponent));
});
// Because `BarComponent` is processed after `FooComponent`...
it('should not apply `BarComponent` within `FooComponent`', () => {
host.innerHTML = `
<aio-eager-foo>
<aio-eager-bar></aio-eager-bar>
</aio-eager-foo>
`;
const componentRefs = service.createComponents(host);
componentRefs.forEach(ref => ref.changeDetectorRef.detectChanges());
expect(host.innerHTML).toContain('Foo Component');
expect(host.innerHTML).not.toContain('Bar Component');
expect(componentRefs.length).toBe(1);
expect(componentRefs[0].instance).toEqual(jasmine.any(FooComponent));
});
});
describe('#embedInto()', () => {
let mockComponentRefs: ComponentRef<any>[];
let createComponentsSpy: jasmine.Spy;
let prepareComponentFactoriesSpy: jasmine.Spy;
const doEmbed = (contents: string) =>
new Promise<ComponentRef<any>[]>((resolve, reject) => {
host.innerHTML = contents;
service.embedInto(host).subscribe(resolve, reject);
});
beforeEach(() => {
mockComponentRefs = [{foo: true}, {bar: true}] as any as ComponentRef<any>[];
createComponentsSpy = spyOn(service, 'createComponents').and.returnValue(mockComponentRefs);
prepareComponentFactoriesSpy = spyOn(service, 'prepareComponentFactories')
.and.returnValue(Promise.resolve());
});
it('should return an observable', done => {
service.embedInto(host).subscribe(done, done.fail);
});
describe('(preparing component factories)', () => {
it('should return an array of `ComponentRef`s', async () => {
// When there are embedded components.
expect(await doEmbed('<aio-eager-foo></aio-eager-foo>')).toEqual(mockComponentRefs);
expect(await doEmbed('<aio-lazy-bar></aio-lazy-bar>')).toEqual(mockComponentRefs);
// When there are no embedded components.
expect(await doEmbed('<div>Test</div>')).toEqual([]);
expect(await doEmbed('')).toEqual([]);
});
it('should prepare all component factories if there are embedded components', async () => {
await doEmbed(`
<div><aio-eager-foo><b>foo</b></aio-eager-foo></div>
<span><aio-lazy-foo><i>bar</i></aio-lazy-foo></span>
`);
expect(prepareComponentFactoriesSpy).toHaveBeenCalledTimes(2);
expect(prepareComponentFactoriesSpy).toHaveBeenCalledWith(testEagerEmbeddedComponents);
expect(prepareComponentFactoriesSpy).toHaveBeenCalledWith(mockEmbeddedModulePath);
});
it('should only prepare the necessary factories', async () => {
await doEmbed('<aio-eager-foo>Eager only</aio-eager-foo>');
expect(prepareComponentFactoriesSpy).toHaveBeenCalledTimes(1);
expect(prepareComponentFactoriesSpy).toHaveBeenCalledWith(testEagerEmbeddedComponents);
await doEmbed('<aio-lazy-foo>Lazy only</aio-lazy-foo>');
expect(prepareComponentFactoriesSpy).toHaveBeenCalledTimes(2);
expect(prepareComponentFactoriesSpy).toHaveBeenCalledWith(mockEmbeddedModulePath);
});
it('should not load embedded components if the document does not contain any', async () => {
await doEmbed('');
await doEmbed('<no-aio-eager-foo></no-aio-eager-foo>');
await doEmbed('<no-aio-lazy-foo></no-aio-lazy-foo>');
expect(prepareComponentFactoriesSpy).not.toHaveBeenCalled();
});
});
describe('(creating embedded components)', () => {
it('should create embedded components if the element contains any', async () => {
await doEmbed('<div><aio-eager-foo><i>blah</i></aio-eager-foo></div>');
expect(createComponentsSpy).toHaveBeenCalledTimes(1);
expect(prepareComponentFactoriesSpy).toHaveBeenCalledBefore(createComponentsSpy);
prepareComponentFactoriesSpy.calls.reset();
createComponentsSpy.calls.reset();
await doEmbed('<aio-lazy-bar><i>blah</i></aio-lazy-bar>');
expect(createComponentsSpy).toHaveBeenCalledTimes(1);
expect(prepareComponentFactoriesSpy).toHaveBeenCalledBefore(createComponentsSpy);
});
it('should emit the created embedded components', async () => {
const componentRefs = await doEmbed('<aio-eager-foo></aio-eager-foo>');
expect(componentRefs).toBe(mockComponentRefs);
});
it('should not create embedded components if the element does not contain any', async () => {
await doEmbed(`
<aio-eager-foo-not></aio-eager-foo-not>
&lt;aio-lazy-bar&gt;&lt;/aio-lazy-bar&gt;
`);
expect(createComponentsSpy).not.toHaveBeenCalled();
});
it('should not create embedded components if the document is empty', async () => {
await doEmbed('');
expect(createComponentsSpy).not.toHaveBeenCalled();
});
it('should not create embedded components if unsubscribed from', async () => {
const preparePromise = Promise.resolve();
prepareComponentFactoriesSpy.and.returnValue(preparePromise);
// When not unsubscribed from...
host.innerHTML = '<aio-eager-foo></aio-eager-foo>';
service.embedInto(host).subscribe();
await new Promise(resolve => setTimeout(resolve));
expect(createComponentsSpy).toHaveBeenCalledTimes(1);
createComponentsSpy.calls.reset();
// When unsubscribed from...
host.innerHTML = '<aio-eager-foo></aio-eager-foo>';
service.embedInto(host).subscribe().unsubscribe();
await new Promise(resolve => setTimeout(resolve));
expect(createComponentsSpy).not.toHaveBeenCalled();
});
});
});
describe('#prepareComponentFactories()', () => {
let loader: MockNgModuleFactoryLoader;
let resolver: ComponentFactoryResolver;
let createComponentFactoriesSpy: jasmine.Spy;
beforeEach(() => {
loader = TestBed.get(NgModuleFactoryLoader);
resolver = TestBed.get(ComponentFactoryResolver);
createComponentFactoriesSpy = spyOn(service, 'createComponentFactories');
});
[testLazyEmbeddedComponents, mockEmbeddedModulePath].forEach((compsOrPath: ComponentsOrModulePath) => {
const useComponents = Array.isArray(compsOrPath);
describe(`(using ${useComponents ? 'component types' : 'module path'})`, () => {
const doPrepareComponentFactories = () =>
service.prepareComponentFactories(compsOrPath);
it('should return a promise', done => {
doPrepareComponentFactories().then(done, done.fail);
});
it('should create the component factories', async () => {
expect(createComponentFactoriesSpy).not.toHaveBeenCalled();
await doPrepareComponentFactories();
expect(createComponentFactoriesSpy).toHaveBeenCalledTimes(1);
const args = createComponentFactoriesSpy.calls.mostRecent().args;
expect(args[0]).toBe(testLazyEmbeddedComponents);
if (useComponents) {
expect(args[1]).toBe(resolver);
} else {
expect(args[1]).not.toBe(resolver);
}
});
it('should not create create the component factories more than once', async () => {
const results = await Promise.all([
doPrepareComponentFactories(),
doPrepareComponentFactories(),
]);
expect(createComponentFactoriesSpy).toHaveBeenCalledTimes(1);
expect(results[1]).toBe(results[0]);
const anotherResult = await doPrepareComponentFactories();
expect(createComponentFactoriesSpy).toHaveBeenCalledTimes(1);
expect(anotherResult).toBe(results[0]);
});
it(`should ${useComponents ? 'not load' : 'load'} the embedded module`, async () => {
expect(loader.loadedPaths).toEqual([]);
await doPrepareComponentFactories();
const expectedLoadedPaths = useComponents ? [] : [mockEmbeddedModulePath];
expect(loader.loadedPaths).toEqual(expectedLoadedPaths);
});
it(`should not load the embedded module more than once`, async () => {
await Promise.all([
doPrepareComponentFactories(),
doPrepareComponentFactories(),
]);
const loadedPathCount = loader.loadedPaths.length;
expect(loadedPathCount).toBeLessThan(2);
await doPrepareComponentFactories();
expect(loader.loadedPaths.length).toBe(loadedPathCount);
});
});
});
});
describe('#selectorToContentPropertyName()', () => {
it('should convert an element selector to a property name', () => {
expect(service.selectorToContentPropertyName('foobar')).toBe('foobarContent');
expect(service.selectorToContentPropertyName('baz-qux')).toBe('bazQuxContent');
});
});
});

View File

@ -0,0 +1,154 @@
import {
ComponentFactory, ComponentFactoryResolver, ComponentRef, Inject, Injectable, InjectionToken,
Injector, NgModuleFactory, NgModuleFactoryLoader, Type
} from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import 'rxjs/add/operator/switchMap';
export interface EmbeddedComponentFactory {
contentPropertyName: string;
factory: ComponentFactory<any>;
}
/**
* A mapping from combined component selectors (keys) to the corresponding components (values). The
* components can be specified either as a list of embedded components or a path to a module that
* provides embedded components (i.e. implements `WithEmbeddedComponents`).
*/
export interface EmbeddedComponentsMap {
[multiSelector: string]: ComponentsOrModulePath;
}
/**
* Interface expected to be implemented by all modules that contribute components to the
* `EmbeddedComponentsMap`.
*/
export interface WithEmbeddedComponents {
embeddedComponents: Type<any>[];
}
/**
* Either an array of components or the path to a module that implements `WithEmbeddedComponents`.
*/
export type ComponentsOrModulePath = Type<any>[] | string;
/**
* The injection token for the `EmbeddedComponentsMap`.
*/
export const EMBEDDED_COMPONENTS = new InjectionToken<EmbeddedComponentsMap>('EMBEDDED_COMPONENTS');
/**
* Embed components into an element. It takes care of indentifying the embedded components, loading
* the necessary modules and instantiating the components.
*
* Embeddable components are identified and loaded based on the info in `EmbeddedComponentsMap`
* (provided through dependency injection).
*
* The caller is responsible for trigering change detection and destroying the components as
* necessary.
*/
@Injectable()
export class EmbedComponentsService {
private componentFactoriesReady = new Map<ComponentsOrModulePath, Promise<void>>();
protected componentFactories = new Map<string, EmbeddedComponentFactory>();
constructor(
private injector: Injector,
private loader: NgModuleFactoryLoader,
private resolver: ComponentFactoryResolver,
@Inject(EMBEDDED_COMPONENTS) private embeddedComponentsMap: EmbeddedComponentsMap) { }
/**
* Embed components into the specified element:
* - Load the necessary modules (if any).
* - Prepare the component factories.
* - Instantiate the components.
*
* Return the list of `ComponentRef`s.
*/
embedInto(elem: HTMLElement): Observable<ComponentRef<any>[]> {
const requiredComponents = Object.keys(this.embeddedComponentsMap)
.filter(selector => elem.querySelector(selector))
.map(selector => this.embeddedComponentsMap[selector]);
const factoriesReady = requiredComponents.map(compsOrPath => this.prepareComponentFactories(compsOrPath));
return !requiredComponents.length
? of([])
: of(undefined)
.switchMap(() => Promise.all(factoriesReady))
.switchMap(() => [this.createComponents(elem)]);
}
/**
* Resolve the embedded component factories (which will later be used to instantiate components).
*/
protected createComponentFactories(components: Type<any>[], resolver: ComponentFactoryResolver): void {
for (const comp of components) {
const factory = resolver.resolveComponentFactory(comp);
const selector = factory.selector;
const contentPropertyName = this.selectorToContentPropertyName(selector);
this.componentFactories.set(selector, {contentPropertyName, factory});
}
}
/**
* Instantiate embedded components for the current contents of `elem`.
* (Store the original HTML contents of each element on the corresponding property for later
* retrieval by the component instance.)
*/
protected createComponents(elem: HTMLElement): ComponentRef<any>[] {
const componentRefs: ComponentRef<any>[] = [];
this.componentFactories.forEach(({contentPropertyName, factory}, selector) => {
const componentHosts = elem.querySelectorAll(selector);
// Cast due to https://github.com/Microsoft/TypeScript/issues/4947.
for (const host of componentHosts as any as HTMLElement[]) {
// Hack: Preserve the current element content, because the factory will empty it out.
// Security: The source of this `innerHTML` should always be authored by the documentation
// team and is considered to be safe.
host[contentPropertyName] = host.innerHTML;
componentRefs.push(factory.create(this.injector, [], host));
}
});
return componentRefs;
}
/**
* Prepare the component factories for the given components.
* If necessary, load and instantiate the module first.
*/
protected prepareComponentFactories(compsOrPath: ComponentsOrModulePath): Promise<void> {
if (!this.componentFactoriesReady.has(compsOrPath)) {
const componentsAndResolverPromise = (typeof compsOrPath !== 'string')
? Promise.resolve({components: compsOrPath, resolver: this.resolver})
: this.loader.load(compsOrPath).then((ngModuleFactory: NgModuleFactory<WithEmbeddedComponents>) => {
const moduleRef = ngModuleFactory.create(this.injector);
return {
components: moduleRef.instance.embeddedComponents,
resolver: moduleRef.componentFactoryResolver,
};
});
const readyPromise = componentsAndResolverPromise
.then(({components, resolver}) => this.createComponentFactories(components, resolver));
this.componentFactoriesReady.set(compsOrPath, readyPromise);
}
return this.componentFactoriesReady.get(compsOrPath);
}
/**
* Compute the component content property name by converting the selector to camelCase and
* appending `Content`, e.g. `live-example` => `liveExampleContent`.
*/
protected selectorToContentPropertyName(selector: string): string {
return selector.replace(/-(.)/g, (match, $1) => $1.toUpperCase()) + 'Content';
}
}

View File

@ -43,15 +43,15 @@ export class ApiListComponent implements OnInit {
// API types
types: Option[] = [
{ value: 'all', title: 'All' },
{ value: 'directive', title: 'Directive' },
{ value: 'pipe', title: 'Pipe'},
{ value: 'decorator', title: 'Decorator' },
{ value: 'class', title: 'Class' },
{ value: 'interface', title: 'Interface' },
{ value: 'function', title: 'Function' },
{ value: 'const', title: 'Const'},
{ value: 'decorator', title: 'Decorator' },
{ value: 'directive', title: 'Directive' },
{ value: 'enum', title: 'Enum' },
{ value: 'type-alias', title: 'Type Alias' },
{ value: 'const', title: 'Const'}
{ value: 'function', title: 'Function' },
{ value: 'interface', title: 'Interface' },
{ value: 'pipe', title: 'Pipe'},
{ value: 'type-alias', title: 'Type Alias' }
];
statuses: Option[] = [

View File

@ -2,7 +2,7 @@ import { Component, ElementRef, ViewChild, OnChanges, Input } from '@angular/cor
import { Logger } from 'app/shared/logger.service';
import { PrettyPrinter } from './pretty-printer.service';
import { CopierService } from 'app/shared/copier.service';
import { MatSnackBar } from '@angular/material';
import { MatSnackBar } from '@angular/material/snack-bar';
const defaultLineNumsCount = 10; // by default, show linenums over this number

View File

@ -1,20 +1,24 @@
import { NgModule } from '@angular/core';
import { NgModule, Type } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ContributorService } from './contributor/contributor.service';
import { CopierService } from 'app/shared/copier.service';
import { PrettyPrinter } from './code/pretty-printer.service';
import { WithEmbeddedComponents } from 'app/embed-components/embed-components.service';
// Any components that we want to use inside embedded components must be declared or imported here
// It is not enough just to import them inside the AppModule
// Reusable components (used inside embedded components)
import { MatIconModule, MatSnackBarModule, MatTabsModule } from '@angular/material';
import { MatIconModule } from '@angular/material/icon';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatTabsModule } from '@angular/material/tabs';
import { CodeComponent } from './code/code.component';
import { SharedModule } from 'app/shared/shared.module';
// Embedded Components
import { ApiListComponent } from './api/api-list.component';
import { ApiService } from './api/api.service';
import { CodeExampleComponent } from './code/code-example.component';
import { CodeTabsComponent } from './code/code-tabs.component';
import { ContributorListComponent } from './contributor/contributor-list.component';
@ -24,22 +28,16 @@ import { FileNotFoundSearchComponent } from './search/file-not-found-search.comp
import { LiveExampleComponent, EmbeddedPlunkerComponent } from './live-example/live-example.component';
import { ResourceListComponent } from './resource/resource-list.component';
import { ResourceService } from './resource/resource.service';
import { TocComponent } from './toc/toc.component';
/** Components that can be embedded in docs
/**
* Components that can be embedded in docs,
* such as CodeExampleComponent, LiveExampleComponent,...
*/
export const embeddedComponents: any[] = [
export const embeddedComponents: Type<any>[] = [
ApiListComponent, CodeExampleComponent, CodeTabsComponent, ContributorListComponent,
CurrentLocationComponent, FileNotFoundSearchComponent, LiveExampleComponent, ResourceListComponent,
TocComponent
CurrentLocationComponent, FileNotFoundSearchComponent, LiveExampleComponent, ResourceListComponent
];
/** Injectable class w/ property returning components that can be embedded in docs */
export class EmbeddedComponents {
components = embeddedComponents;
}
@NgModule({
imports: [
CommonModule,
@ -54,16 +52,15 @@ export class EmbeddedComponents {
ContributorComponent,
EmbeddedPlunkerComponent
],
exports: [
TocComponent
],
providers: [
ApiService,
ContributorService,
CopierService,
EmbeddedComponents,
PrettyPrinter,
ResourceService
],
entryComponents: [ embeddedComponents ]
})
export class EmbeddedModule { }
export class EmbeddedModule implements WithEmbeddedComponents {
embeddedComponents = embeddedComponents;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,22 @@
import {
Component, ComponentFactory, ComponentFactoryResolver, ComponentRef,
DoCheck, ElementRef, EventEmitter, Injector, Input, OnDestroy,
Output
} from '@angular/core';
import { EmbeddedComponents } from 'app/embedded/embedded.module';
import { DocumentContents } from 'app/documents/document.service';
import { Component, ComponentRef, DoCheck, ElementRef, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { timer } from 'rxjs/observable/timer';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/takeUntil';
import { DocumentContents } from 'app/documents/document.service';
import { EmbedComponentsService } from 'app/embed-components/embed-components.service';
import { Logger } from 'app/shared/logger.service';
import { TocService } from 'app/shared/toc.service';
interface EmbeddedComponentFactory {
contentPropertyName: string;
factory: ComponentFactory<any>;
}
// Constants
export const NO_ANIMATIONS = 'no-animations';
// Initialization prevents flicker once pre-rendering is on
const initialDocViewerElement = document.querySelector('aio-doc-viewer');
@ -25,19 +29,48 @@ const initialDocViewerContent = initialDocViewerElement ? initialDocViewerElemen
// encapsulation: ViewEncapsulation.Native
})
export class DocViewerComponent implements DoCheck, OnDestroy {
// Enable/Disable view transition animations.
static animationsEnabled = true;
private embeddedComponents: ComponentRef<any>[] = [];
private embeddedComponentFactories: Map<string, EmbeddedComponentFactory> = new Map();
private hostElement: HTMLElement;
@Output()
docRendered = new EventEmitter();
private void$ = of<void>(undefined);
private onDestroy$ = new EventEmitter<void>();
private docContents$ = new EventEmitter<DocumentContents>();
protected embeddedComponentRefs: ComponentRef<any>[] = [];
protected currViewContainer: HTMLElement = document.createElement('div');
protected nextViewContainer: HTMLElement = document.createElement('div');
@Input()
set doc(newDoc: DocumentContents) {
// Ignore `undefined` values that could happen if the host component
// does not initially specify a value for the `doc` input.
if (newDoc) {
this.docContents$.emit(newDoc);
}
}
// The new document is ready to be inserted into the viewer.
// (Embedded components have been loaded and instantiated, if necessary.)
@Output() docReady = new EventEmitter<void>();
// The previous document has been removed from the viewer.
// (The leaving animation (if any) has been completed and the node has been removed from the DOM.)
@Output() docRemoved = new EventEmitter<void>();
// The new document has been inserted into the viewer.
// (The node has been inserted into the DOM, but the entering animation may still be in progress.)
@Output() docInserted = new EventEmitter<void>();
// The new document has been fully rendered into the viewer.
// (The entering animation has been completed.)
@Output() docRendered = new EventEmitter<void>();
constructor(
componentFactoryResolver: ComponentFactoryResolver,
elementRef: ElementRef,
embeddedComponents: EmbeddedComponents,
private injector: Injector,
private embedComponentsService: EmbedComponentsService,
private logger: Logger,
private titleService: Title,
private tocService: TocService
) {
@ -45,84 +78,158 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
// Security: the initialDocViewerContent comes from the prerendered DOM and is considered to be secure
this.hostElement.innerHTML = initialDocViewerContent;
for (const component of embeddedComponents.components) {
const factory = componentFactoryResolver.resolveComponentFactory(component);
const selector = factory.selector;
const contentPropertyName = this.selectorToContentPropertyName(selector);
this.embeddedComponentFactories.set(selector, { contentPropertyName, factory });
}
}
@Input()
set doc(newDoc: DocumentContents) {
this.ngOnDestroy();
if (newDoc) {
this.build(newDoc);
this.docRendered.emit();
}
}
/**
* Add doc content to host element and build it out with embedded components
*/
private build(doc: DocumentContents) {
// security: the doc.content is always authored by the documentation team
// and is considered to be safe
this.hostElement.innerHTML = doc.contents || '';
if (!doc.contents) { return; }
this.addTitleAndToc(doc.id);
// TODO(i): why can't I use for-of? why doesn't typescript like Map#value() iterators?
this.embeddedComponentFactories.forEach(({ contentPropertyName, factory }, selector) => {
const embeddedComponentElements = this.hostElement.querySelectorAll(selector);
// cast due to https://github.com/Microsoft/TypeScript/issues/4947
for (const element of embeddedComponentElements as any as HTMLElement[]){
// hack: preserve the current element content because the factory will empty it out
// security: the source of this innerHTML is always authored by the documentation team
// and is considered to be safe
element[contentPropertyName] = element.innerHTML;
this.embeddedComponents.push(factory.create(this.injector, [], element));
}
});
}
private addTitleAndToc(docId: string) {
this.tocService.reset();
const titleEl = this.hostElement.querySelector('h1');
let title = '';
// Only create TOC for docs with an <h1> title
// If you don't want a TOC, add "no-toc" class to <h1>
if (titleEl) {
title = (typeof titleEl.innerText === 'string') ? titleEl.innerText : titleEl.textContent;
if (!/(no-toc|notoc)/i.test(titleEl.className)) {
this.tocService.genToc(this.hostElement, docId);
titleEl.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>');
}
if (this.hostElement.firstElementChild) {
this.currViewContainer = this.hostElement.firstElementChild as HTMLElement;
}
this.titleService.setTitle(title ? `Angular - ${title}` : 'Angular');
this.onDestroy$.subscribe(() => this.destroyEmbeddedComponents());
this.docContents$
.switchMap(newDoc => this.render(newDoc))
.takeUntil(this.onDestroy$)
.subscribe();
}
ngDoCheck() {
this.embeddedComponents.forEach(comp => comp.changeDetectorRef.detectChanges());
this.embeddedComponentRefs.forEach(comp => comp.changeDetectorRef.detectChanges());
}
ngOnDestroy() {
// destroy these components else there will be memory leaks
this.embeddedComponents.forEach(comp => comp.destroy());
this.embeddedComponents.length = 0;
this.onDestroy$.emit();
}
/**
* Compute the component content property name by converting the selector to camelCase and appending
* 'Content', e.g. live-example => liveExampleContent
* Destroy the embedded components to avoid memory leaks.
*/
private selectorToContentPropertyName(selector: string) {
return selector.replace(/-(.)/g, (match, $1) => $1.toUpperCase()) + 'Content';
protected destroyEmbeddedComponents(): void {
this.embeddedComponentRefs.forEach(comp => comp.destroy());
this.embeddedComponentRefs = [];
}
/**
* Prepare for setting the window title and ToC.
* Return a function to actually set them.
*/
protected prepareTitleAndToc(targetElem: HTMLElement, docId: string): () => void {
const titleEl = targetElem.querySelector('h1');
const hasToc = !!titleEl && !/no-?toc/i.test(titleEl.className);
if (hasToc) {
titleEl.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>');
}
return () => {
this.tocService.reset();
let title = '';
// Only create ToC for docs with an `<h1>` heading.
// If you don't want a ToC, add "no-toc" class to `<h1>`.
if (titleEl) {
title = (typeof titleEl.innerText === 'string') ? titleEl.innerText : titleEl.textContent;
if (hasToc) {
this.tocService.genToc(targetElem, docId);
}
}
this.titleService.setTitle(title ? `Angular - ${title}` : 'Angular');
};
}
/**
* Add doc content to host element and build it out with embedded components.
*/
protected render(doc: DocumentContents): Observable<void> {
let addTitleAndToc: () => void;
return this.void$
// Security: `doc.contents` is always authored by the documentation team
// and is considered to be safe.
.do(() => this.nextViewContainer.innerHTML = doc.contents || '')
.do(() => addTitleAndToc = this.prepareTitleAndToc(this.nextViewContainer, doc.id))
.switchMap(() => this.embedComponentsService.embedInto(this.nextViewContainer))
.do(() => this.docReady.emit())
.do(() => this.destroyEmbeddedComponents())
.do(componentRefs => this.embeddedComponentRefs = componentRefs)
.switchMap(() => this.swapViews(addTitleAndToc))
.do(() => this.docRendered.emit())
.catch(err => {
const errorMessage = (err instanceof Error) ? err.stack : err;
this.logger.error(`[DocViewer] Error preparing document '${doc.id}': ${errorMessage}`);
this.nextViewContainer.innerHTML = '';
return this.void$;
});
}
/**
* Swap the views, removing `currViewContainer` and inserting `nextViewContainer`.
* (At this point all content should be ready, including having loaded and instantiated embedded
* components.)
*
* Optionally, run a callback as soon as `nextViewContainer` has been inserted, but before the
* entering animation has been completed. This is useful for work that needs to be done as soon as
* the element has been attached to the DOM.
*/
protected swapViews(onInsertedCb = () => undefined): Observable<void> {
const raf$ = new Observable<void>(subscriber => {
const rafId = requestAnimationFrame(() => {
subscriber.next();
subscriber.complete();
});
return () => cancelAnimationFrame(rafId);
});
// Get the actual transition duration (taking global styles into account).
// According to the [CSSOM spec](https://drafts.csswg.org/cssom/#serializing-css-values),
// `time` values should be returned in seconds.
const getActualDuration = (elem: HTMLElement) => {
const cssValue = getComputedStyle(elem).transitionDuration;
const seconds = Number(cssValue.replace(/s$/, ''));
return 1000 * seconds;
};
const animateProp =
(elem: HTMLElement, prop: string, from: string, to: string, duration = 200) => {
const animationsDisabled = !DocViewerComponent.animationsEnabled
|| this.hostElement.classList.contains(NO_ANIMATIONS);
elem.style.transition = '';
return animationsDisabled
? this.void$.do(() => elem.style[prop] = to)
: this.void$
// In order to ensure that the `from` value will be applied immediately (i.e.
// without transition) and that the `to` value will be affected by the
// `transition` style, we need to ensure an animation frame has passed between
// setting each style.
.switchMap(() => raf$).do(() => elem.style[prop] = from)
.switchMap(() => raf$).do(() => elem.style.transition = `all ${duration}ms ease-in-out`)
.switchMap(() => raf$).do(() => elem.style[prop] = to)
.switchMap(() => timer(getActualDuration(elem))).switchMap(() => this.void$);
};
const animateLeave = (elem: HTMLElement) => animateProp(elem, 'opacity', '1', '0.1');
const animateEnter = (elem: HTMLElement) => animateProp(elem, 'opacity', '0.1', '1');
let done$ = this.void$;
if (this.currViewContainer.parentElement) {
done$ = done$
// Remove the current view from the viewer.
.switchMap(() => animateLeave(this.currViewContainer))
.do(() => this.currViewContainer.parentElement.removeChild(this.currViewContainer))
.do(() => this.docRemoved.emit());
}
return done$
// Insert the next view into the viewer.
.do(() => this.hostElement.appendChild(this.nextViewContainer))
.do(() => onInsertedCb())
.do(() => this.docInserted.emit())
.switchMap(() => animateEnter(this.nextViewContainer))
// Update the view references and clean up unused nodes.
.do(() => {
const prevViewContainer = this.currViewContainer;
this.currViewContainer = this.nextViewContainer;
this.nextViewContainer = prevViewContainer;
this.nextViewContainer.innerHTML = ''; // Empty to release memory.
});
}
}

View File

@ -12,7 +12,7 @@
</div>
<p>
Powered by Google ©2010-2017.
Powered by Google ©2010-2018.
Code licensed under an <a href="license" title="License text" >MIT-style License</a>.
Documentation licensed under
<a href="http://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.

View File

@ -36,7 +36,7 @@ describe('SearchBoxComponent', () => {
describe('initialisation', () => {
it('should get the current search query from the location service',
inject([LocationService], (location: MockLocationService) => fakeAsync(() => {
fakeAsync(inject([LocationService], (location: MockLocationService) => {
location.search.and.returnValue({ search: 'initial search' });
component.ngOnInit();
expect(location.search).toHaveBeenCalled();

View File

@ -11,8 +11,9 @@ interface StringMap { [index: string]: string; }
export function getAttrs(el: HTMLElement | ElementRef): StringMap {
const attrs: NamedNodeMap = el instanceof ElementRef ? el.nativeElement.attributes : el.attributes;
const attrMap = {};
Object.keys(attrs).forEach(key =>
attrMap[(attrs[key] as Attr).name.toLowerCase()] = (attrs[key] as Attr).value);
for (const attr of attrs as any /* cast due to https://github.com/Microsoft/TypeScript/issues/2695 */) {
attrMap[attr.name.toLowerCase()] = attr.value;
}
return attrMap;
}

View File

@ -1,6 +1,6 @@
import { InjectionToken, Inject, Injectable } from '@angular/core';
import { of } from 'rxjs/observable/of';
import { MatIconRegistry } from '@angular/material';
import { MatIconRegistry } from '@angular/material/icon';
import { HttpClient } from '@angular/common/http';
import { DomSanitizer } from '@angular/platform-browser';
@ -27,19 +27,6 @@ interface SvgIconMap {
[iconName: string]: SVGElement;
}
// <hack-alert>
// @angular/material's `MdIconRegitry` currently (v2.0.0-beta.8) requires an instance of `Http`
// (from @angular/http). It is only used to [get some text content][1], so we can create a wrapper
// around `HttpClient` and pretend it is `Http`.
// [1]: https://github.com/angular/material2/blob/2.0.0-beta.8/src/lib/icon/icon-registry.ts#L465-L466
// </hack-alert>
function createFakeHttp(http: HttpClient): any {
return {
get: (url: string) => http.get(url, {responseType: 'text'})
.map(data => ({text: () => data}))
};
}
/**
* A custom replacement for Angular Material's `MdIconRegistry`, which allows
* us to provide preloaded icon SVG sources.
@ -49,7 +36,7 @@ export class CustomIconRegistry extends MatIconRegistry {
private preloadedSvgElements: SvgIconMap = {};
constructor(http: HttpClient, sanitizer: DomSanitizer, @Inject(SVG_ICONS) svgIcons: SvgIconInfo[]) {
super(createFakeHttp(http), sanitizer);
super(http, sanitizer);
this.loadSvgElements(svgIcons);
}

View File

@ -17,7 +17,7 @@ export class ScrollService {
// at the top (e.g. toolbar) + some margin
get topOffset() {
if (!this._topOffset) {
const toolbar = this.document.querySelector('mat-toolbar.app-toolbar');
const toolbar = this.document.querySelector('.app-toolbar');
this._topOffset = (toolbar && toolbar.clientHeight || 0) + topMargin;
}
return this._topOffset;

View File

@ -100,7 +100,7 @@ a {
text-decoration: none;
}
.mat-toolbar-row a {
.app-toolbar a {
font-size: 16px;
font-weight: 400;
color: white;

View File

@ -0,0 +1,4 @@
.no-animations aio-doc-viewer > * {
// Disable view transition animations.
transition: none !important;
}

View File

@ -4,6 +4,7 @@
@import 'api-page';
@import 'content-layout';
@import 'doc-viewer';
@import 'footer';
@import 'layout-global';
@import 'marketing-layout';

View File

@ -1,3 +1,92 @@
// VARIABLES
$hamburgerShownMargin: 0;
$hamburgerHiddenMargin: 0 24px 0 -88px;
// DOCS PAGE / STANDARD: TOPNAV TOOLBAR FIXED
mat-toolbar.mat-toolbar {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 10;
padding: 0 16px 0 0;
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.30);
mat-icon {
color: $white;
}
}
// HOME PAGE OVERRIDE: TOPNAV TOOLBAR
aio-shell.page-home mat-toolbar.mat-toolbar {
background-color: $blue;
@media (min-width: 481px) {
&:not(.transitioning) {
background-color: transparent;
transition: background-color .2s linear;
}
}
}
// MARKETING PAGES OVERRIDE: TOPNAV TOOLBAR AND HAMBURGER
aio-shell.page-home mat-toolbar.mat-toolbar,
aio-shell.page-features mat-toolbar.mat-toolbar,
aio-shell.page-events mat-toolbar.mat-toolbar,
aio-shell.page-resources mat-toolbar.mat-toolbar {
box-shadow: none;
// FIXED TOPNAV TOOLBAR FOR SMALL MOBILE
@media (min-width: 481px) {
position: absolute;
}
}
// DOCS PAGES OVERRIDE: HAMBURGER
aio-shell.folder-api mat-toolbar.mat-toolbar,
aio-shell.folder-docs mat-toolbar.mat-toolbar,
aio-shell.folder-guide mat-toolbar.mat-toolbar,
aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
@media (min-width: 992px) {
.hamburger.mat-button {
// Hamburger shown on non-marketing pages on large screens.
margin: $hamburgerShownMargin;
}
}
}
// HAMBURGER BUTTON
.hamburger.mat-button {
height: 100%;
margin: $hamburgerShownMargin;
padding: 0;
transition-duration: .4s;
transition-property: color, margin;
transition-timing-function: cubic-bezier(.25, .8, .25, 1);
@media (min-width: 992px) {
// Hamburger hidden by default on large screens.
// (Will be shown per doc.)
margin: $hamburgerHiddenMargin;
}
&:hover {
color: $offwhite;
}
& .mat-icon {
color: white;
position: inherit;
}
}
// HOME NAV-LINK
.nav-link.home img {
position: relative;
margin-top: -21px;
@ -12,6 +101,8 @@
}
}
// TOP MENU
aio-top-menu {
display: flex;
flex-direction: row;
@ -56,55 +147,6 @@ aio-top-menu {
}
}
// HOME PAGE OVERRIDE: TOPNAV TOOLBAR HAMBURGER MENU
aio-shell.page-home mat-toolbar.app-toolbar.mat-toolbar {
background-color: transparent;
transition: background-color .2s linear .3s;
@media (max-width: 480px) {
background-color: $blue;
}
}
// DOCS PAGE / STANDARD: TOPNAV TOOLBAR FIXED
mat-toolbar.mat-toolbar {
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 10;
padding: 0 16px 0 0;
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.30);
mat-icon {
color: $white;
}
}
// MARKETING PAGES OVERRIDE: TOPNAV TOOLBAR AND HAMBURGER
aio-shell.page-home mat-toolbar.mat-toolbar,
aio-shell.page-features mat-toolbar.mat-toolbar,
aio-shell.page-events mat-toolbar.mat-toolbar,
aio-shell.page-resources mat-toolbar.mat-toolbar {
// FIXED TOPNAV TOOLBAR FOR SMALL MOBILE
@media (min-width: 481px) {
position: absolute;
}
@media (min-width: 992px) {
button.hamburger {
margin: 0 24px 0 -88px;
}
}
}
// REMOVE BOX SHADOW ON CERTAIN MARKETING PAGES
aio-shell.page-home mat-toolbar.mat-toolbar,
aio-shell.page-events mat-toolbar.mat-toolbar,
aio-shell.page-resources mat-toolbar.mat-toolbar {
box-shadow: none;
}
// SEARCH BOX
aio-search-box.search-container {

View File

@ -1,29 +0,0 @@
.hamburger {
transition-duration: 150ms;
transition-property: background-color, color;
transition-timing-function: ease-in-out;
&:hover {
color: $lightgray;
}
}
.hamburger.mat-button {
height: 100%;
margin: 0;
padding: 0;
&:not(.starting) {
transition-duration: .4s;
transition-property: color, margin;
transition-timing-function: cubic-bezier(.25, .8, .25, 1);
}
}
.hamburger.mat-button:hover {
color: $offwhite;
}
.hamburger .mat-icon {
position: inherit;
color: white;
}

View File

@ -16,7 +16,6 @@
@import 'edit-page-cta';
@import 'features';
@import 'filetree';
@import 'hamburger';
@import 'heading-anchors';
@import 'hr';
@import 'images';

View File

@ -0,0 +1,88 @@
import { Component, ComponentRef, NgModule, ViewChild } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { Observable } from 'rxjs/Observable';
import { DocumentContents } from 'app/documents/document.service';
import { EmbedComponentsService } from 'app/embed-components/embed-components.service';
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
import { Logger } from 'app/shared/logger.service';
import { TocService } from 'app/shared/toc.service';
import { MockLogger } from 'testing/logger.service';
////////////////////////////////////////////////////////////////////////////////////////////////////
/// `TestDocViewerComponent` (for exposing internal `DocViewerComponent` methods as public). ///
/// Only used for type-casting; the actual implementation is irrelevant. ///
////////////////////////////////////////////////////////////////////////////////////////////////////
export class TestDocViewerComponent extends DocViewerComponent {
embeddedComponentRefs: ComponentRef<any>[];
currViewContainer: HTMLElement;
nextViewContainer: HTMLElement;
destroyEmbeddedComponents(): void { return null as any; }
prepareTitleAndToc(targetElem: HTMLElement, docId: string): () => void { return null as any; }
render(doc: DocumentContents): Observable<void> { return null as any; }
swapViews(onInsertedCb?: () => void): Observable<void> { return null as any; }
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// `TestModule` and `TestParentComponent`. ///
////////////////////////////////////////////////////////////////////////////////////////////////////
// Test parent component.
@Component({
selector: 'aio-test',
template: '<aio-doc-viewer [doc]="currentDoc">Test Component</aio-doc-viewer>',
})
export class TestParentComponent {
currentDoc: DocumentContents;
@ViewChild(DocViewerComponent) docViewer: DocViewerComponent;
}
// Mock services.
export class MockEmbedComponentsService {
embedInto = jasmine.createSpy('EmbedComponentsService#embedInto');
}
export class MockTitle {
setTitle = jasmine.createSpy('Title#reset');
}
export class MockTocService {
genToc = jasmine.createSpy('TocService#genToc');
reset = jasmine.createSpy('TocService#reset');
}
@NgModule({
declarations: [
DocViewerComponent,
TestParentComponent,
],
providers: [
{ provide: Logger, useClass: MockLogger },
{ provide: EmbedComponentsService, useClass: MockEmbedComponentsService },
{ provide: Title, useClass: MockTitle },
{ provide: TocService, useClass: MockTocService },
],
})
export class TestModule { }
////////////////////////////////////////////////////////////////////////////////////////////////////
/// An observable with spies to test subscribing/unsubscribing. ///
////////////////////////////////////////////////////////////////////////////////////////////////////
export class ObservableWithSubscriptionSpies<T = void> extends Observable<T> {
unsubscribeSpies: jasmine.Spy[] = [];
subscribeSpy = spyOn(this, 'subscribe').and.callFake((...args) => {
const subscription = super.subscribe(...args);
const unsubscribeSpy = spyOn(subscription, 'unsubscribe').and.callThrough();
this.unsubscribeSpies.push(unsubscribeSpy);
return subscription;
});
constructor(subscriber = () => undefined) { super(subscriber); }
}

View File

@ -0,0 +1,138 @@
import {
Component, ComponentFactoryResolver, ComponentRef, CompilerFactory, ElementRef, NgModule,
NgModuleFactoryLoader, OnInit, Type, ViewChild, getPlatform
} from '@angular/core';
import {
ComponentsOrModulePath, EMBEDDED_COMPONENTS, EmbedComponentsService, EmbeddedComponentFactory,
WithEmbeddedComponents
} from 'app/embed-components/embed-components.service';
////////////////////////////////////////////////////////////////////////////////////////////////////
/// `TestEmbedComponentsService` (for exposing internal methods as public). ///
/// Only used for type-casting; the actual implementation is irrelevant. ///
////////////////////////////////////////////////////////////////////////////////////////////////////
export class TestEmbedComponentsService extends EmbedComponentsService {
componentFactories: Map<string, EmbeddedComponentFactory>;
createComponentFactories(components: Type<any>[], resolver: ComponentFactoryResolver): void { return null as any; }
createComponents(elem: HTMLElement): ComponentRef<any>[] { return null as any; }
prepareComponentFactories(compsOrPath: ComponentsOrModulePath): Promise<void> { return null as any; }
selectorToContentPropertyName(selector: string): string { return null as any; }
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Mock `EmbeddedModule` and test components. ///
////////////////////////////////////////////////////////////////////////////////////////////////////
// Test embedded components.
@Component({
selector: 'aio-eager-foo',
template: `Eager Foo Component`,
})
class EagerFooComponent { }
@Component({
selector: 'aio-eager-bar',
template: `
<hr>
<h2>Eager Bar Component</h2>
<p #content></p>
<hr>
`,
})
class EagerBarComponent implements OnInit {
@ViewChild('content') contentRef: ElementRef;
constructor(public elementRef: ElementRef) { }
// Project content in `ngOnInit()` just like in `CodeExampleComponent`.
ngOnInit() {
// Security: This is a test component; never deployed.
this.contentRef.nativeElement.innerHTML = this.elementRef.nativeElement.aioEagerBarContent;
}
}
@Component({
selector: 'aio-lazy-foo',
template: `Lazy Foo Component`,
})
class LazyFooComponent { }
@Component({
selector: 'aio-lazy-bar',
template: `
<hr>
<h2>Lazy Bar Component</h2>
<p #content></p>
<hr>
`,
})
class LazyBarComponent implements OnInit {
@ViewChild('content') contentRef: ElementRef;
constructor(public elementRef: ElementRef) { }
// Project content in `ngOnInit()` just like in `CodeExampleComponent`.
ngOnInit() {
// Security: This is a test component; never deployed.
this.contentRef.nativeElement.innerHTML = this.elementRef.nativeElement.aioLazyBarContent;
}
}
// Export test embedded selectors and components.
export const testEagerEmbeddedSelectors = ['aio-eager-foo', 'aio-eager-bar'];
export const testEagerEmbeddedComponents = [EagerFooComponent, EagerBarComponent];
export const testLazyEmbeddedSelectors = ['aio-lazy-foo', 'aio-lazy-bar'];
export const testLazyEmbeddedComponents = [LazyFooComponent, LazyBarComponent];
// Export mock `EmbeddedModule` and path.
export const mockEmbeddedModulePath = 'mock/mock-embedded#MockEmbeddedModule';
@NgModule({
declarations: [testLazyEmbeddedComponents],
entryComponents: [testLazyEmbeddedComponents],
})
class MockEmbeddedModule implements WithEmbeddedComponents {
embeddedComponents = testLazyEmbeddedComponents;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// `TestModule`. ///
////////////////////////////////////////////////////////////////////////////////////////////////////
// Mock services.
export class MockNgModuleFactoryLoader implements NgModuleFactoryLoader {
loadedPaths: string[] = [];
load(path: string) {
this.loadedPaths.push(path);
const platformRef = getPlatform();
const compilerFactory = platformRef.injector.get(CompilerFactory) as CompilerFactory;
const compiler = compilerFactory.createCompiler([]);
return compiler.compileModuleAsync(MockEmbeddedModule);
}
}
@NgModule({
providers: [
EmbedComponentsService,
{ provide: NgModuleFactoryLoader, useClass: MockNgModuleFactoryLoader },
{
provide: EMBEDDED_COMPONENTS,
useValue: {
[testEagerEmbeddedSelectors.join(',')]: testEagerEmbeddedComponents,
[testLazyEmbeddedSelectors.join(',')]: mockEmbeddedModulePath,
},
},
],
declarations: [testEagerEmbeddedComponents],
entryComponents: [testEagerEmbeddedComponents],
})
export class TestModule { }

View File

@ -1,6 +1,7 @@
{
"scripts": [
{ "name": "ng", "command": "ng" },
{ "name": "build", "command": "ng build" },
{ "name": "start", "command": "ng serve" },
{ "name": "test", "command": "ng test" },
{ "name": "lint", "command": "ng lint" },

View File

@ -54,6 +54,19 @@ class ExampleZipper {
}
}
// rename a custom main.ts or index.html file
_renameFile(file) {
if (/src\/main[-.]\w+\.ts$/.test(file)) {
return 'src/main.ts';
}
if (/src\/index[-.]\w+\.html$/.test(file)) {
return 'src/index.html';
}
return file;
}
_zipExample(configFileName, sourceDirName, outputDirName) {
let json = require(configFileName, 'utf-8');
const basePath = json.basePath || '';
@ -79,12 +92,16 @@ class ExampleZipper {
'tslint.json',
'karma-test-shim.js',
'karma.conf.js',
'tsconfig.json',
'src/testing/**/*',
'src/.babelrc',
'src/favicon.ico',
'src/typings.d.ts'
'src/polyfills.ts',
'src/typings.d.ts',
'src/environments/**/*',
'src/tsconfig.*'
];
var defaultExcludes = [
var alwaysExcludes = [
'!**/bs-config.e2e.json',
'!**/*plnkr.*',
'!**/*zipper.*',
@ -132,13 +149,14 @@ class ExampleZipper {
}
});
Array.prototype.push.apply(gpaths, defaultExcludes);
Array.prototype.push.apply(gpaths, alwaysExcludes);
let fileNames = globby.sync(gpaths, { ignore: ['**/node_modules/**']});
let zip = this._createZipArchive(outputFileName);
fileNames.forEach((fileName) => {
let relativePath = path.relative(exampleDirName, fileName);
relativePath = this._renameFile(relativePath);
let content = fs.readFileSync(fileName, 'utf8');
let extn = path.extname(fileName).substr(1);
// if we don't need to clean up the file then we can do the following.

View File

@ -200,8 +200,10 @@ function runProtractorAoT(appDir, outputFile) {
// All protractor output is appended to the outputFile.
// CLI version
function runE2eTestsCLI(appDir, outputFile) {
// --preserve-symlinks is needed due the symlinked node_modules in each example
const e2eSpawn = spawnExt('yarn', ['e2e', '--preserve-symlinks'], { cwd: appDir });
// `--preserve-symlinks` is needed due the symlinked `node_modules/` in each example.
// `--no-webdriver-update` is needed to preserve the ChromeDriver version already installed.
const args = ['e2e', '--preserve-symlinks', '--no-webdriver-update'];
const e2eSpawn = spawnExt('yarn', args, { cwd: appDir });
return e2eSpawn.promise.then(
function () {
fs.appendFileSync(outputFile, `Passed: ${appDir}\n\n`);

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