Compare commits

...

120 Commits

Author SHA1 Message Date
663aba5775 release: cut the v8.2.12 release 2019-10-23 15:00:08 -07:00
2d465ee37b feat(docs-infra): implement figure styles (#33259)
PR#28396 originally addressed an update via issue #23983 to make images more visible with a white background (implementation of gray "lightbox").

This PR implements those styles defined in PR#28396.

PR Close #33259
2019-10-23 12:59:35 -07:00
70238f7d7d docs: fix spelling error in angular component (#32971)
change component class section of the docs

Closes #32948

PR Close #32971
2019-10-23 11:46:42 -07:00
704e3543dc docs: Fix appHighlightColor typo (#33331)
In the example, there's no directive nor input that's named `appHighlightColor`.
It should be `appHighlight`, referring to the input binding.

PR Close #33331
2019-10-23 11:08:19 -07:00
98e71d31db build(docs-infra): upgrade cli command docs sources to 5bf754195 (#33350)
Updating [angular#8.2.x](https://github.com/angular/angular/tree/8.2.x) from [cli-builds#8.3.x](https://github.com/angular/cli-builds/tree/8.3.x).

##
Relevant changes in [commit range](e21aeeecd...5bf754195):

**Modified**
- help/update.json

##

PR Close #33350
2019-10-23 09:59:37 -07:00
7478d0cf4d test(docs-infra): disable es5 size tracking in aio tests (#33356)
PR Close #33356
2019-10-23 09:58:13 -07:00
ef5c0dae7d build: fix gulp setup not working with node v12 (#33348)
It looks like the gulp setup does not work with NodeJS v12. This
is because we still use gulp for a few tasks, but gulp v3 is not
compatible with NodeJS v12. We had a similar issue for NodeJS v12,
but worked around it by updating the `natives` module version.

To actually solve this in a more future-proof way, without
updating or removing Gulp (for now), we just overwrite the
`graceful-fs` version. The latest version of `graceful-fs`
does no longer depend on the `natives` package and therefore
works properly with NodeJS >= v10.

PR Close #33348
2019-10-23 09:16:21 -07:00
57c3a238ea fix(docs-infra): scroll to top when navigating to new page via address bar (#33344)
Previously, when navigating to a new page via a link, the scroll
position was correctly restored to 0, but navigating to a new page via
typing the URL in the browser address bar keeps the old scroll position.

This commit ensures that the scroll position is restored to 0 whenever
the `ScrollService` is instantiated anew (i.e. new page navigation). The
old behavior of retaining the scroll position on reload is kept by
storing the old URL when leaving a page and only applying the stored
scroll position if the new URL matches the stored one.

Fixes #33260

PR Close #33344
2019-10-23 09:12:04 -07:00
bb284066ca build: update bazel remote build config and documentation (#33304)
PR Close #33304
2019-10-21 16:52:46 -04:00
fc147289a9 docs: clarrify use of sw behind redirect (#32915)
Closes #30684

PR Close #32915
2019-10-21 16:50:52 -04:00
3f3e1bfce9 docs: clarify steps in Hosting on Firebase (#33089)
PR Close #33089
2019-10-21 16:07:05 -04:00
2b02e87f4a docs(forms): fix grammar and add clarification to setValue docs (#33126)
Changed `setValue` documentation for throwing an error as it contained a grammar
mistake and also may have caused ambiguity around when exactly the
method would throw.

PR Close #33126
2019-10-21 15:58:39 -04:00
88869a2965 docs: update deployers table with ngx-deploy-npm (#33269)
ngx-deploy-npm is a custom deployer to publish Angular libraries to NPM

PR Close #33269
2019-10-21 11:28:21 -04:00
958aba9391 docs: edit and organize language service doc (#33202)
PR Close #33202
2019-10-18 18:19:06 -04:00
d9581bd6a4 ci: fix dev-infra incorrectly matching all ".bzl" files as codeowner (#33249)
The `dev-infra-framework` codeowners section is located after
all the codeowner sections for the individual framework packages.

This means that for certain wildcards, like `*.bzl` take precedence
over the individual package sections (like of `/packages/bazel/**`).
This can be observed in the following PR where `dev-infra` got requested
for file changes to `@angular/bazel`. #32955.

To fix this, we move the `dev-infra-framework` section before all
individual framework package sections. This means that we only match
files which are not matched by other patterns.

Additionally a pattern for `*.BAZEL` has been added to the dev-infra
section. This helps getting changes to `BUILD.bazel` files through
easier as dev-infra can review those (Note: only for bazel files which
are not matched by other patterns).

PR Close #33249
2019-10-18 16:27:30 -04:00
19ada81a08 build: fix tslint issue with String instantiation 2019-10-18 11:47:39 -07:00
3d60f2cefc docs: clarify instructions on where to create the proxy.conf.json file (#33244)
PR Close #33244
2019-10-18 14:40:58 -04:00
f13e40db1b docs: change "patchMatch" to "pathMatch" (#32935)
PR Close #32935
2019-10-18 11:31:54 -04:00
fab6ea0109 docs: correct vscode clang-format setup instructions (#33190)
PR Close #33190
2019-10-17 14:17:56 -04:00
05a1cebceb style: enforce disallowance of String, Number and Boolean constructors (#33210)
The `no-construct` tslint rule partially enforces the style guide rule https://google.github.io/styleguide/jsguide.html#disallowed-features-wrapper-objects

PR Close #33210
2019-10-17 14:12:01 -04:00
1281bef896 docs: fix code-example displayed path (#33228)
PR Close #33228
2019-10-17 14:06:41 -04:00
37cbcfaa71 fix(upgrade): remove unused version export (#33180)
In some module systems (Closure), it's illegal to mutate an export.
This mutated export isn't used anyway, so we can just remove it.

PR Close #33180
2019-10-16 16:39:12 -04:00
ef0b8f4a87 docs: fix typo in getting started guide (#33185)
Fixes #33181

PR Close #33185
2019-10-16 10:23:03 -04:00
8bc2f0647b release: cut the v8.2.11 release 2019-10-15 22:15:08 +00:00
b71ccc2954 docs: fix JDK url (#33168)
PR Close #33168
2019-10-15 16:53:31 +00:00
2daa838a04 docs: Update link (#33166)
Update multi-project workspace link

weblink on the angular.io website was not working, therefore, updated the multi-project workspace link.
PR Close #33166
2019-10-15 16:52:31 +00:00
a2716acf4c fix(service-worker): continue serving api requests on cache failure (#33165)
When responses are cached ok during sw initialization,
but caching throws an error when handling api response,
this response never gets to client. Fix response
delivery by catching errors, add logging and 2 test cases.
Same changes for master branch in PR #32996

Fixes #21412

PR Close #33165
2019-10-15 16:51:16 +00:00
7a0cc534df refactor(service-worker): make signatures of caching methods compatible (#32996) (#33165)
Make safe caching and unsafe caching methods compatible so they can be
swapped. Gives more flexibility when writing http response processing
code.

PR Close #32996

(cherry picked from commit 1353afc2b1)

PR Close #33165
2019-10-15 16:51:16 +00:00
5f52e63857 docs: use neutral domain name in i18n example (#33146)
PR Close #33146
2019-10-15 16:50:02 +00:00
9409dce93c docs: correct matcher description (#32970)
PR Close #32970
2019-10-14 23:47:30 +00:00
2fa788c9e6 docs: clarify static router-outlet name attribute (#32973)
PR Close #32973
2019-10-14 23:47:02 +00:00
73e667f61f docs: a typo fixed in the upgrade guide (#33054)
PR Close #33054
2019-10-14 23:46:23 +00:00
a7d5d33f0a docs: clarify http header api doc (#33038)
PR Close #33038
2019-10-14 23:45:46 +00:00
7e511e78cd docs: fix typo in guide (#33155)
PR Close #33155
2019-10-14 23:43:30 +00:00
9e76a38073 build: using the xlarge resource class for zonejs tests on CI (#33148)
Fixes #33147

PR Close #33148
2019-10-14 20:13:26 +00:00
242981963e docs: add command for creating angular workspace (#32804)
Improved documentation

Add command to create Angular workspace, it is required for executing `ng generate library <my-lib>` .

PR Close #32804
2019-10-14 16:32:47 +00:00
970df9ebaf docs: add note to build Angular locally (#31053)
I got the compiled failed error when I followed the tutorial, which should be informed in advance as there are people who might not know the dependency in the package.json if they have no former relevant experience.

PR Close #31053
2019-10-14 16:31:53 +00:00
4c7e7fbd09 docs(router): Update description and example for Resolve interface (#31810)
PR Close #31810
2019-10-10 15:28:04 -07:00
5f78456170 docs(common): Fixes typo (#33003)
PR Close #33003
2019-10-10 13:55:41 -07:00
36fd063737 docs: fix typo in guide (#32893)
PR Close #32893
2019-10-10 13:54:59 -07:00
c1b7f0370e docs: changed 'function' word in to 'steps' (#32211)
changed word for better method's interpretation.

PR Close #32211
2019-10-10 13:54:24 -07:00
882a9e3856 docs: fixed a misleading sentence (#32211)
Reading this sentence, make the reader think that the second argument strictly accepts one single animate, which is not
PR Close #32211
2019-10-10 13:54:24 -07:00
362b3e4d03 release: cut the v8.2.10 release 2019-10-09 13:55:51 -07:00
2952ea57a5 docs: correct sentence in AoT compiler guide (#33020)
PR Close #33020
2019-10-07 13:13:58 -07:00
3541e590f4 docs: fix accessibility lint rules (#32661)
Add and fix accessibility rules, bump codelyzer to support pseudo events
for template-click-events-have-key-events rule.

PR Close #32661
2019-10-07 11:22:50 -07:00
8ef0ae3561 docs: add dotnettricks to training (#32771)
PR Close #32771
2019-10-07 11:03:04 -07:00
c3ff66c1ba docs: add missing parenthesis (#31041)
PR Close #31041
2019-10-07 11:02:34 -07:00
a1d9848456 build(docs-infra): update in-memory-we-api and karma-jasmine-html-reporter version (#32892)
The `karma-jasmine-html-reporter` update also includes a fix for
taras42/karma-jasmine-html-reporter#31.

Fixes #29802

PR Close #32892
2019-10-07 10:51:20 -07:00
a3482f7076 refactor(forms): refactor Validators.email() regexp for easier comparison with WHATWG version (#32961)
As mentioned in the previous commit, the regexp used by
`Validators.email()` is a slightly enhanced version of the
[WHATWG one](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address).

This commit refactors the regexp (without changing its behavior) to make
it more closely match the format of WHATWG version, so that it is easier
for people to compare it against the WHATWG one and understand the
differences.

The main changes were:
- Changing the order of characters/character classes inside `[...]`;
  e.g. `[A-Za-z]` --> `[a-zA-Z]`
- Mark all groups as non-capturing (since we do not use the captured
  values); e.g. `(foo)` --> `(?:foo)`
  (This could theoretically also have a positive performance impact, but
  I suspect JavaScript engines are already optimizing away capturing
  groups when they are not used.)

PR Close #32961
2019-10-07 10:51:01 -07:00
006af0b985 docs(forms): expand e-mail validation description (#32961)
Previously, there was no documentation of what `Validators.email()`
expects as a valid e-mail address, making it difficult for people to
determine whether it covers their requirements or not. Even more so that
the used pattern slightly deviates from the
[WHATWG version](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address).
One's only option was to look at the source code and try to decipher the
regexp pattern.

This commit adds a high-level description of the validator and mentions
its similarity to and differences from the WHATWG version. It also adds
a brief explanation of the regexp's behavior and references for more
information in the source code to provide more context to
maintainers/users trying to understand the implementation in the future.

Fixes #18985
Fixes #25186
Closes #32747

PR Close #32961
2019-10-07 10:51:01 -07:00
9dc4815e39 ci: shorten the display name of init_environment steps that are shown in all jobs (#32998)
The `init_environment` custom command is used in almost all other jobs.
The this is implemented in CircleCI is that the steps of the command are
inlined into the other jobs.

Some of the `init_environment` commands are quite long and (since the
default display name for a step is its command) they clutter the
CircleCI UI. Additionally, multiple related commands are shown as
separate steps, which makes it more difficult to get to the actual steps
of a job.

This commit improves this by:
1. Defining explicit, short names for steps with long commands.
2. Grouping multiple related steps into one.

PR Close #32998
2019-10-04 08:39:21 -07:00
4263d9ea0d ci: refactor notification commands into a single parametrized one (#32745) (#32982)
notify_caretaker_on_fail and notify_dev_infra_on_fail are the same, except for the url they notify.

PR Close #32745

PR Close #32982
2019-10-03 14:14:56 -07:00
30253a7df3 ci: use CircleCI parameterized jobs (#32745) (#32982)
Parameterized jobs lets us reduce duplication of very similar jobs.

See https://circleci.com/docs/2.0/reusing-config/#authoring-parameterized-jobs for more info.

PR Close #32745

PR Close #32982
2019-10-03 14:14:56 -07:00
16b83e8e2f ci: use CircleCI commands (#32745) (#32982)
When we needed to run multiple commands in a reusable fashion, we needed to make a giant run block with multiple things inside. Using custom commands gives us a better way to do this.

See https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-commands for more info.

PR Close #32745

PR Close #32982
2019-10-03 14:14:55 -07:00
e0c10632ea ci: keep cache key fallback in a var (#32745) (#32982)
This way it's right next to the original key and it's easier to keep them in sync.

PR Close #32745

PR Close #32982
2019-10-03 14:14:55 -07:00
686b62129c ci: use CircleCI executors (#32745) (#32982)
Executors let you define custom execution contexts for jobs.

See https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-executors for more information.

PR Close #32745

PR Close #32982
2019-10-03 14:14:55 -07:00
dd2587d9e5 build: create script for setting up RBE in local dev environment (#31200)
PR Close #31200
2019-10-03 12:17:57 -07:00
2742649a52 docs(core): mark EventEmitter#__isAsync as internal to hide from API docs (#31378)
The `__isAsync` property is not part of the public API and should not
appear in the API docs.

PR Close #31378
2019-10-03 10:24:35 -07:00
eb0461d2d4 fix(docs-infra): ignore ng*Def members in API docs (#31378)
`ng*Def` properties (such as `ngInjectorDef`) are not considered part of
the public API and should not appear in the API docs. This commit adds a
filter to remove these properties from the docs metadata.

PR Close #31378
2019-10-03 10:24:35 -07:00
e24393c35b test(upgrade): add unit tests for AngularJSUrlCodec's parse method (#32976)
Add unit test coverage for new logic added in #32874 and for existing
logic that was untested.

test(upgrade): add unit tests for AngularJSUrlCodec's parse method

Add additional coverage and fix spacing

test(upgrade): add unit tests for AngularJSUrlCodec's parse method

Add unit test coverage for new logic added in #32874 and for existing
logic that was untested.

test(upgrade): add unit tests for AngularJSUrlCodec's parse method

Add additional coverage and fix spacing

test(upgrade): add unit tests for AngularJSUrlCodec's parse method

Add unit test coverage for new logic added in #32874 and for existing
logic that was untested.

test(upgrade): add unit tests for AngularJSUrlCodec's parse method

Add unit test coverage for new logic added in #32874 and for existing
logic that was untested.

test(upgrade): add unit tests for AngularJSUrlCodec's parse method

Add additional coverage and fix spacing

test(upgrade): add unit tests for AngularJSUrlCodec's parse method

Add unit test coverage for new logic added in #32874 and for existing
logic that was untested.

PR Close #32976
2019-10-03 09:55:27 -07:00
8237e958a6 docs: fix typo in attribute-directives.md (#32943)
Fixes #32924

PR Close #32943
2019-10-02 14:30:51 -07:00
9ba898d588 docs: remove Renderer2 section (#32972)
PR Close #32972
2019-10-02 13:47:50 -07:00
cd1b0c1b1f Revert "docs: move renderer2 deprecation guide into own file (#32626)" (#32972)
This reverts commit 222af38462.

PR Close #32972
2019-10-02 13:47:50 -07:00
06072e0062 Revert "docs: add undecorated classes migration faq (#32478)" (#32972)
This reverts commit bd679581e2.

PR Close #32972
2019-10-02 13:47:49 -07:00
288e0ef7b6 Revert "docs: add dynamic queries flag migration documentation (#32582)" (#32972)
This reverts commit 206fb82330.

PR Close #32972
2019-10-02 13:47:49 -07:00
88ad5052bf docs: add comment about newEvent utility function (#32001)
PR Close #32001
2019-10-02 13:28:39 -07:00
7e6644a25a docs(docs-infra): use recommended type assertion (#31042)
Angular uses tslint:recommended by default. The default for no-angle-bracket-type-assertion is true
See https://github.com/palantir/tslint/blob/master/src/configs/recommended.ts#L69

PR Close #31042
2019-10-02 13:25:40 -07:00
d533d15001 docs: add ngOnInit description (#32789)
PR Close #32789
2019-10-02 13:24:31 -07:00
d0abf1bc54 refactor(bazel): ng_module action description should include compile mode (#32955)
Similarly to `ts_library` compilation actions, the `ng_module` compile action should include
the current compile mode in the action description. This makes it consistent with `ts_library`
targets and also avoids confusion when both output flavors are requested.

Currently when both output flavors are requested (e.g. in the `ng_package` rule), both
devmode and prodmode compilations have the same action name. This is confusing and
looks like the given target is built *twice* due to a bug (which is obviously not the case though)

PR Close #32955
2019-10-02 13:21:40 -07:00
56ac18ea8c docs: fix stackblitz example polyfills (#32969)
PR Close #32969
2019-10-02 13:20:38 -07:00
11d3b19b32 release: cut the v8.2.9 release 2019-10-02 11:15:13 -07:00
d96167fc54 docs: add lib build note (#32940)
PR Close #32940
2019-10-02 10:59:06 -07:00
68fed2fc28 docs(router): fix typo for "urlUpdateStrategy" (#32960)
PR Close #32960
2019-10-02 10:58:41 -07:00
57457fb7c7 fix(upgrade): fix AngularJsUrlCodec to support Safari (#32959)
Safari throws an error when the new URL() constructor is called with an
undefined base. This change checks whether the base is undefined and
then calls the corresponding version of the URL constructor.

fix(upgrade): simplify solution by replacing undefined with ''

Co-Authored-By: Pete Bacon Darwin <pete@bacondarwin.com>

Simplify solution by replacing undefined with ''

Co-Authored-By: Pete Bacon Darwin <pete@bacondarwin.com>

fix(upgrade): Avoid passing an empty string as the base as well.

Browsers other than Safari may have issues with the empty string.

PR Close #32959
2019-10-02 10:00:48 -07:00
a2fc5774a7 docs: added '>' to ng-template closing tag. (#32939)
Fixes #32931

PR Close #32939
2019-10-02 09:44:24 -07:00
c6041b985e fix(docs-infra): show hamburger button on getting started guide (#32941)
In 1c3ee4190, the `getting-started` guide/tutorial was renamed to
`start`, but the corresponding CSS class that controls the display of
the top-left hamburger (and it automatically derived based on the URL)
was not updated.

This commit updates the class to ensure that the hamburger is not hidden
when navigating to the getting started guide.

PR Close #32941
2019-10-02 09:20:23 -07:00
b45d2e0fb1 docs: add dynamic queries flag migration documentation (#32582)
PR Close #32582
2019-10-01 17:19:06 -07:00
42f7419522 docs: emphasize headers for code examples (#32938)
PR Close #32938
2019-10-01 14:45:30 -07:00
32f5241598 docs: fix capitalization in elements docs (#32917) (#32936)
PR Close #32917

PR Close #32936
2019-10-01 14:44:50 -07:00
06e72721ca docs: clarify http response type (#32817)
PR Close #32817
2019-10-01 11:00:36 -07:00
3a80069f65 docs: fix typo in creating-libraries (#32930)
PR Close #32930
2019-10-01 10:59:02 -07:00
0818e2c799 docs(service-worker): add info about recovering from degraded Driver state (#32682)
Related to #31865

PR Close #32682
2019-09-30 14:15:35 -07:00
7b8b2d4622 docs: update inline lazy loading example to use the import syntax (#32260)
PR Close #32260
2019-09-30 12:06:02 -07:00
f6526a0872 docs: add filename headers to code examples (#32701)
PR Close #32701
2019-09-30 10:28:21 -07:00
0984022192 docs: add prompts section to schematic guide (#32728)
PR Close #32728
2019-09-30 10:27:39 -07:00
bb7603104f docs: removed duplicate "compilerOptions" property (#32449) (#32911)
PR Close #32449

PR Close #32911
2019-09-30 10:26:21 -07:00
bdea243fed docs: add missing space between "Component" and "will" in guide (#32885)
Closes angular/angular#32873

PR Close #32885
2019-09-27 15:59:29 -07:00
2397144e01 docs(router): make InitialNavigation part of the public API (#32707)
`InitialNavigation` is used in `ExtraOptions`, which is already part of
the public API. Thus, `InitialNavigation` should be too. Not publicly
exporting it from `router/index.ts` seems an omission, since the type is
already annotated with the `@publicApi` JSDoc tag.

By publicly exporting `InitialNavigation`, it will also correctly appear
in the API docs on angular.io.

PR Close #32707
2019-09-27 14:21:14 -07:00
466c754cc8 docs: fix typo in deployment guide (#32777)
The typo causes most of the callout to not appear on the page. The issue 
appears at the bottom of the "Lazy loading" section on the live site:
https://angular.io/guide/deployment#lazy-loading
PR Close #32777
2019-09-27 10:32:38 -07:00
45fee069d7 docs: correct spelling of HTMLTableCellElement in Template Syntax (#32805)
Spelling Correction in Docs

Corrected spelling; From `HTMLTableCellElment` to  `HTMLTableCellElement`.

PR Close #32805
2019-09-27 10:03:53 -07:00
432f7ce2a0 docs: fix sentence phrasing (#32846)
`In this section, you'll add a "Buy" button the product details page.` -> `In this section, you'll add a "Buy" button to the product details page.`
PR Close #32846
2019-09-27 10:03:23 -07:00
e8c34c47af docs: clarify sentence in lifecycle hooks doc (#32860)
PR Close #32860
2019-09-27 10:02:54 -07:00
e81fc14da1 fix(docs-infra): correctly run on IE11 (#32871)
Since 007282d2b, [TocService][1] uses [forEach()][2] to iterate over a
`NodeList`. This breaks in IE11, which does not support
`NodeList#forEach()`.

This commit wraps the returned `NodeList` in a regular `Array` to have
access to array methods (such as `forEach()`).

Fixes #32832

[1]: https://github.com/angular/angular/blob/fbad4ff65/aio/src/app/shared/toc.service.ts#L68
[2]: https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach

PR Close #32871
2019-09-26 17:05:39 -07:00
d5c210a2e1 docs: added link to ngRuAir podcast (#32223)
PR Close #32223
2019-09-26 07:37:55 -07:00
7dbde6f570 docs: remove extra parenthesis from inline animations snippet (#32735)
PR Close #32735
2019-09-25 12:05:22 -07:00
78de6211e3 release: cut the v8.2.8 release 2019-09-25 10:08:33 -07:00
104c786b90 docs: removed duplicated spaces in upgrade guide (#32813)
PR Close #32813
2019-09-24 14:01:43 -07:00
9680f4c991 docs: fix typo in upgrade guide (#32807)
PR Close #32807
2019-09-24 14:01:19 -07:00
e63390daf8 docs: correct default for router extra option (#32702)
PR Close #32702
2019-09-23 14:24:21 -07:00
af99f9e98e docs: edit copy on getting started step 1 (#32773)
PR Close #32773
2019-09-19 14:20:51 -07:00
8398f9d54c docs: edit copy on first step of getting started (#32751)
PR Close #32751
2019-09-18 13:37:00 -07:00
da5d91b97b release: cut the v8.2.7 release 2019-09-18 10:01:57 -07:00
ba1ef6b1a5 fix(bazel): ng_package(data) should support non-text files (#32721)
PR Close #32721
2019-09-17 15:58:43 -07:00
acebf6464e ci: build bazel saucelabs tests remotely (#32719)
PR Close #32719
2019-09-17 15:14:18 -07:00
e367aa2ca5 docs: Typo fixes in hierarchical DI guide (#32536)
Introduce minor typo fixes in the guide for hierarchical dependency injection

PR Close #32536
2019-09-17 13:37:54 -07:00
0ee2b755e2 ci: use automatic strategy selection for AngularTemplateCompile and TypeScriptCompile (#32708)
PR Close #32708
2019-09-16 16:35:54 -07:00
f213c7a643 docs: add autoprefixer backward compatability instruction (#32109)
PR Close #32109
2019-09-16 10:48:03 -07:00
947c076ff2 fix(compiler-cli): fix typo in diagnostic template info. (#32684)
Fixes #32662

PR Close #32684
2019-09-16 08:59:49 -07:00
6600bea815 ci: run ts-api-guardian test remotely on CI (#32677)
PR Close #32677
2019-09-13 15:13:45 -07:00
6cd61aeb1c ci: add @josephperrott to global approvers in CODEOWNERS (#32667)
This change aims to align the documented members in `CODEOWNERS` with
the actual members of the secret `framework-global-approvers` GitHub
team.

PR Close #32667
2019-09-13 08:55:57 -07:00
581b991432 docs: add undecorated classes migration faq (#32478)
PR Close #32478
2019-09-12 12:16:15 -07:00
4014aab300 docs: move renderer2 deprecation guide into own file (#32626)
PR Close #32626
2019-09-12 10:43:01 -07:00
b523844966 build(docs-infra): upgrade cli command docs sources to e21aeeecd (#32635)
Updating [angular#8.2.x](https://github.com/angular/angular/tree/8.2.x) from [cli-builds#8.3.x](https://github.com/angular/cli-builds/tree/8.3.x).

##
Relevant changes in [commit range](6734efe52...e21aeeecd):

**Modified**
- help/generate.json

##

PR Close #32635
2019-09-12 10:32:10 -07:00
8d322c89b7 docs: modify lazy-load module instructions for new cli flag (#32588)
PR Close #32588
2019-09-12 10:23:20 -07:00
08a7c6f0b5 ci: update codeowners to reflect GitHub team members (#32569)
PR Close #32569
2019-09-11 20:26:29 -04:00
31f06ee3f9 build: enforce proper compile mode for size-tracking test (#32613)
Ensures that the "core_all:size_test" target runs with "--define=compile=aot".
This is necessary because we don't run this test on CI currently, but if we run
it manually, we need to ensure that it runs with Ivy for proper size comparisons.

PR Close #32613
2019-09-11 20:22:29 -04:00
1c5b157f10 fix(language-service): cache module resolution (#32483)
This is a patch PR for https://github.com/angular/angular/pull/32479

This PR fixes a critical performance issue where the language
service makes a MASSIVE number of filesystem calls when performing
module resolution.
This is because there is no caching. To make matters worse, module
resolution is performed for every program change (which means every
few keystrokes trigger a massive number of fs calls).

PR Close #32483
2019-09-11 20:21:55 -04:00
38fe47316c docs: fix doc links to current version (#32566)
PR Close #32566
2019-09-11 19:27:01 -04:00
9488da0d0a docs: add header to code examples (#32563)
PR Close #32563
2019-09-11 19:26:20 -04:00
5a8c560373 docs: update CHANGELOG to include default message for 8.2.6 2019-09-11 14:06:37 -07:00
141 changed files with 2671 additions and 1502 deletions

View File

@ -71,11 +71,26 @@ test --test_output=errors
# any bazel target. This is a temporary flag until codebase is permanently switched to Ivy.
build --define=compile=legacy
###############################
# Remote Build Execution support
# Turn on these settings with
# --config=remote
###############################
#######################
# Remote HTTP Caching #
#######################
build --remote_http_cache=https://storage.googleapis.com/angular-team-cache
build --remote_accept_cached=true
build --remote_upload_local_results=false
######################################
# Remote HTTP Caching writes support #
# Turn on these settings with #
# --config=-http-caching #
######################################
build:remote-http-caching --remote_upload_local_results=true
build:remote-http-caching --google_default_credentials
##################################
# Remote Build Execution support #
# Turn on these settings with #
# --config=remote #
##################################
# Load default settings for Remote Build Execution.
import %workspace%/third_party/github.com/bazelbuild/bazel-toolchains/bazelrc/.bazelrc.notoolchain
@ -83,6 +98,7 @@ import %workspace%/third_party/github.com/bazelbuild/bazel-toolchains/bazelrc/.b
# Increase the default number of jobs by 50% because our build has lots of
# parallelism
build:remote --jobs=150
build:remote --google_default_credentials
# Toolchain and platform related flags
build:remote --host_javabase=@rbe_ubuntu1604_angular//java:jdk
@ -96,21 +112,10 @@ build:remote --extra_execution_platforms=//tools:rbe_ubuntu1604-angular
build:remote --host_platform=//tools:rbe_ubuntu1604-angular
build:remote --platforms=//tools:rbe_ubuntu1604-angular
# Remote instance.
# Remote instance and caching
build:remote --remote_instance_name=projects/internal-200822/instances/default_instance
build:remote --project_id=internal-200822
# Remote caching
build:remote --remote_cache=remotebuildexecution.googleapis.com
# By default, do not accept remote cache, to be set to true for CI based on environment
build:remote --remote_accept_cached=false
# By default, do not upload local results to cache, to be set to true for CI based on environment
build:remote --remote_upload_local_results=false
# Build Event Service Configuration
build:remote --bes_backend=buildeventservice.googleapis.com
build:remote --bes_timeout=30s
build:remote --bes_results_url="https://source.cloud.google.com/results/invocations/"
###############################
# NodeJS rules settings

View File

@ -28,14 +28,3 @@ test --flaky_test_attempts=2
# More details on failures
build --verbose_failures=true
# We have seen some flakiness in using TS workers on CircleCI
# https://angular-team.slack.com/archives/C07DT5M6V/p1562693245183400
# > failures like `ERROR: /home/circleci/ng/packages/core/test/BUILD.bazel:5:1:
# > Compiling TypeScript (devmode) //packages/core/test:test_lib failed: Worker process did not return a WorkResponse:`
# > I saw that issue a couple times today.
# > Example job: https://circleci.com/gh/angular/angular/385517
# We expect that TypeScript compilations will parallelize wider than the number of local cores anyway
# so we should saturate remote workers with TS compilations
build --strategy=AngularTemplateCompile=local
build --strategy=TypeScriptCompile=local

View File

@ -7,158 +7,162 @@
# To validate changes, use an online parser, eg.
# http://yaml-online-parser.appspot.com/
# Note that the browser docker image comes with Chrome and Firefox preinstalled. This is just
# needed for jobs that run tests without Bazel. Bazel runs tests with browsers that will be
# fetched by the Webtesting rules. Therefore for jobs that run tests with Bazel, we don't need a
# docker image with browsers pre-installed.
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
# **NOTE 3**: If you change the version of the `*-browsers` docker image, make sure the
# `CI_CHROMEDRIVER_VERSION_ARG` env var (in `.circleci/env.sh`) points to a ChromeDriver
# version that is compatible with the Chrome version in the image.
var_1: &default_docker_image circleci/node:10.16@sha256:75c05084fff4afa3683a03c5a04a4a3ad95c536ff2439d8fe14e7e1f5c58b09a
var_2: &browsers_docker_image circleci/node:10.16-browsers@sha256:d2a96fe1cbef51257ee626b5f645e64dade3e886f00ba9cb7e8ea65b4efe8db1
# CircleCI configuration version
# Version 2.1 allows for extra config reuse features
# https://circleci.com/docs/2.0/reusing-config/#getting-started-with-config-reuse
version: 2.1
# We don't want to include the current branch name in the cache key because that would prevent
# PRs from being able to restore the cache since the branch names are always different for PRs.
# The cache key should only consist of dynamic values that change whenever something in the
# cache changes. For example:
# 1) yarn lock file changes --> cached "node_modules" are different.
# 2) bazel repository definitions change --> cached bazel repositories are different.
# **NOTE 1 **: If you change the cache key prefix, also sync the restore_cache fallback to match.
# **NOTE 1 **: If you change the cache key prefix, also sync the cache_key_fallback to match.
# **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks.
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
var_3: &cache_key v3-angular-node-10.16-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
# Initializes the CI environment by setting up common environment variables.
var_4: &init_environment
run:
name: Initializing environment (setting up variables, overwriting Yarn)
# Overwrite the yarn installed in the docker container with our own version.
command: |
./.circleci/env.sh
ourYarn=$(realpath ./third_party/github.com/yarnpkg/yarn/releases/download/v1.17.3/bin/yarn.js)
sudo chmod a+x $ourYarn
sudo ln -fs $ourYarn /usr/local/bin/yarn
echo "Yarn version: $(yarn --version)"
# Add GitHub to known hosts.
mkdir -p ~/.ssh
echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts
# use git+ssh instead of https
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
git config --global gc.auto 0 || true
var_5: &setup_bazel_remote_execution
run:
name: "Setup bazel RBE remote execution"
command: |
# We need ensure that the same default digest is used for encoding and decoding
# with openssl. Openssl versions might have different default digests which can
# cause decryption failures based on the openssl version. https://stackoverflow.com/a/39641378/4317734
openssl aes-256-cbc -d -in .circleci/gcp_token -md md5 -k "$CI_REPO_NAME" -out /home/circleci/.gcp_credentials
echo "export GOOGLE_APPLICATION_CREDENTIALS=/home/circleci/.gcp_credentials" >> $BASH_ENV
./.circleci/setup-rbe.sh .bazelrc.user
# Settings common to each job
var_6: &job_defaults
working_directory: ~/ng
docker:
- image: *default_docker_image
# After checkout, rebase on top of target branch.
var_7: &post_checkout
run:
name: Rebase PR on target branch
command: >
if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
# User is required for rebase.
git config user.name "angular-ci"
git config user.email "angular-ci"
# Rebase PR on top of target branch.
node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER}
else
echo "This build is not over a PR, nothing to do."
fi
var_8: &yarn_install
run:
name: Running Yarn install
command: |
# Yarn's requests sometimes take more than 10mins to complete.
# Print something to stdout, to prevent CircleCI from failing due to not output.
while true; do sleep 60; echo "[`date`] Keeping alive..."; done &
KEEP_ALIVE_PID=$!
yarn install --frozen-lockfile --non-interactive
kill $KEEP_ALIVE_PID
var_9: &setup_circleci_bazel_config
run:
name: Setting up CircleCI bazel configuration
command: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
var_10: &restore_cache
restore_cache:
keys:
- *cache_key
# This fallback should be the cache_key without variables.
- v3-angular-node-10.16-
# Branch filter that can be specified for jobs that should only run on publish branches
# (e.g. master or the patch branch)
var_11: &publish_branches_filter
branches:
only:
- master
# e.g. 7.0.x, 7.1.x, etc.
- /\d+\.\d+\.x/
# Workspace initially persisted by the `install` job, and then enhanced by `test_aio` and
# `build-npm-packages`.
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
# https://circleci.com/blog/deep-diving-into-circleci-workspaces/
var_12: &attach_workspace
attach_workspace:
at: ~/
var_13: &notify_caretaker_on_fail
run:
when: on_fail
name: Notify caretaker about failure
# `$SLACK_CARETAKER_WEBHOOK_URL` is a secret env var defined in CircleCI project settings.
# The URL comes from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
command: |
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job for $CIRCLE_BRANCH branch failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
curl --request POST --header "Content-Type: application/json" --data "$notificationJson" $SLACK_CARETAKER_WEBHOOK_URL
var_14: &notify_dev_infra_on_fail
run:
when: on_fail
name: Notify dev-infra about failure
# `$SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL` is a secret env var defined in CircleCI project settings.
# The URL comes from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
command: |
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job for $CIRCLE_BRANCH branch failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
curl --request POST --header "Content-Type: application/json" --data "$notificationJson" $SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
var_4: &cache_key_fallback v3-angular-node-10.16-
# Cache key for the Material unit tests job. **Note** when updating the SHA in the cache keys,
# also update the SHA for the "MATERIAL_REPO_COMMIT" environment variable.
var_15: &material_unit_tests_cache_key v4-angular-material-18b9ef3f5529f0fa8f034944681486447af7b879
var_16: &material_unit_tests_cache_key_short v4-angular-material
var_5: &material_unit_tests_cache_key v4-angular-material-18b9ef3f5529f0fa8f034944681486447af7b879
var_6: &material_unit_tests_cache_key_fallback v4-angular-material-
version: 2
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
# `build-ivy-npm-packages`.
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
# https://circleci.com/blog/deep-diving-into-circleci-workspaces/
var_7: &workspace_location ~/
# Executor Definitions
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-executors
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
# **NOTE 3**: If you change the version of the `*-browsers` docker image, make sure the
# `CI_CHROMEDRIVER_VERSION_ARG` env var (in `.circleci/env.sh`) points to a ChromeDriver
# version that is compatible with the Chrome version in the image.
executors:
default-executor:
parameters:
resource_class:
type: string
default: medium
docker:
- image: circleci/node:10.16@sha256:75c05084fff4afa3683a03c5a04a4a3ad95c536ff2439d8fe14e7e1f5c58b09a
resource_class: << parameters.resource_class >>
working_directory: ~/ng
browsers-executor:
parameters:
resource_class:
type: string
default: medium
docker:
# The browser docker image comes with Chrome and Firefox preinstalled. This is just
# needed for jobs that run tests without Bazel. Bazel runs tests with browsers that will be
# fetched by the Webtesting rules. Therefore for jobs that run tests with Bazel, we don't need a
# docker image with browsers pre-installed.
- image: circleci/node:10.16-browsers@sha256:d2a96fe1cbef51257ee626b5f645e64dade3e886f00ba9cb7e8ea65b4efe8db1
resource_class: << parameters.resource_class >>
working_directory: ~/ng
# Command Definitions
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-commands
commands:
custom_attach_workspace:
description: Attach workspace at a predefined location
steps:
- attach_workspace:
at: *workspace_location
# Initializes the CI environment by setting up common environment variables.
init_environment:
description: Initializing environment (setting up variables, overwriting Yarn)
steps:
- run: ./.circleci/env.sh
- run:
# Overwrite the yarn installed in the docker container with our own version.
name: Overwrite yarn with our own version
command: |
ourYarn=$(realpath ./third_party/github.com/yarnpkg/yarn/releases/download/v1.17.3/bin/yarn.js)
sudo chmod a+x $ourYarn
sudo ln -fs $ourYarn /usr/local/bin/yarn
- run: echo "Yarn version $(yarn --version)"
- run:
# Configure git as the CircleCI `checkout` command does.
# This is needed because we only checkout on the setup job.
# Add GitHub to known hosts
name: Configure git
command: |
mkdir -p ~/.ssh
echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
git config --global gc.auto 0 || true
setup_circleci_bazel_config:
description: Set up CircleCI bazel configuration
steps:
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
setup_bazel_rbe:
description: Setup bazel RBE remote execution
steps:
# We need ensure that the same default digest is used for encoding and decoding
# with openssl. Openssl versions might have different default digests which can
# cause decryption failures based on the openssl version. https://stackoverflow.com/a/39641378/4317734
- run: openssl aes-256-cbc -d -in .circleci/gcp_token -md md5 -k "$CI_REPO_NAME" -out /home/circleci/.gcp_credentials
- run: echo "export GOOGLE_APPLICATION_CREDENTIALS=/home/circleci/.gcp_credentials" >> $BASH_ENV
- run: ./.circleci/setup-rbe.sh .bazelrc.user
notify_webhook_on_fail:
description: Notify a webhook about failure
parameters:
# `webhook_url_env_var` are secret env vars defined in CircleCI project settings.
# The URLs come from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
webhook_url_env_var:
type: env_var_name
steps:
- run:
when: on_fail
command: |
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job for $CIRCLE_BRANCH branch failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
curl --request POST --header "Content-Type: application/json" --data "$notificationJson" ${<< parameters.webhook_url_env_var >>}
# Job definitions
# Jobs can include parameters that are passed in the workflow job invocation.
# https://circleci.com/docs/2.0/reusing-config/#authoring-parameterized-jobs
jobs:
setup:
<<: *job_defaults
executor: default-executor
steps:
- checkout
- *post_checkout
- run:
name: Rebase PR on target branch
# After checkout, rebase on top of target branch.
command: >
if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
# User is required for rebase.
git config user.name "angular-ci"
git config user.email "angular-ci"
# Rebase PR on top of target branch.
node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER}
else
echo "This build is not over a PR, nothing to do."
fi
# This cache is saved in the build-npm-packages so that Bazel cache is also included.
- *restore_cache
- *init_environment
- *yarn_install
- restore_cache:
keys:
- *cache_key
- *cache_key_fallback
- init_environment
- run:
name: Running Yarn install
command: yarn install --frozen-lockfile --non-interactive
# Yarn's requests sometimes take more than 10mins to complete.
no_output_timeout: 45m
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
# Make the bazel directories and add a file to them if they don't exist already so that
# persist_to_workspace does not fail.
@ -168,19 +172,18 @@ jobs:
touch ~/bazel_repository_cache/MARKER
fi
# Persist any changes at this point to be reused by further jobs.
# **NOTE 1 **: Folders persisted here should be kept in sync with `var_13: &attach_workspace`.
# **NOTE 2 **: To add new content to the workspace, always persist on the same root.
# **NOTE**: To add new content to the workspace, always persist on the same root.
- persist_to_workspace:
root: ~/
root: *workspace_location
paths:
- ./ng
- ./bazel_repository_cache
lint:
<<: *job_defaults
executor: default-executor
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
- run: 'yarn bazel:format -mode=check ||
(echo "BUILD files not formatted. Please run ''yarn bazel:format''" ; exit 1)'
@ -192,25 +195,29 @@ jobs:
- run: node tools/verify-codeownership
test:
<<: *job_defaults
resource_class: xlarge
executor:
name: default-executor
resource_class: xlarge
steps:
- *attach_workspace
- *init_environment
- *setup_circleci_bazel_config
- custom_attach_workspace
- init_environment
- setup_circleci_bazel_config
# Setup remote execution and run RBE-compatible tests.
- *setup_bazel_remote_execution
- run: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
- setup_bazel_rbe
- run:
command: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
no_output_timeout: 20m
# Temporary job to test what will happen when we flip the Ivy flag to true
test_ivy_aot:
<<: *job_defaults
resource_class: xlarge
executor:
name: default-executor
resource_class: xlarge
steps:
- *attach_workspace
- *init_environment
- *setup_circleci_bazel_config
- *setup_bazel_remote_execution
- custom_attach_workspace
- init_environment
- setup_circleci_bazel_config
- setup_bazel_rbe
# We need to explicitly specify the --symlink_prefix option because otherwise we would
# not be able to easily find the output bin directory when uploading artifacts for size
@ -242,15 +249,17 @@ jobs:
#
# NOTE: This is currently limited to master builds only. See the `default_workflow` configuration.
test_saucelabs_bazel:
<<: *job_defaults
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
# container for this job. This is necessary because we launch a lot of browsers concurrently
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
resource_class: xlarge
executor:
name: default-executor
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
# container for this job. This is necessary because we launch a lot of browsers concurrently
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
resource_class: xlarge
steps:
- *attach_workspace
- *init_environment
- *setup_circleci_bazel_config
- custom_attach_workspace
- init_environment
- setup_circleci_bazel_config
- setup_bazel_rbe
- run:
name: Run Bazel tests in saucelabs
# All web tests are contained within a single //:test_web_all target for Saucelabs
@ -265,16 +274,16 @@ jobs:
--username $SAUCE_USERNAME \
--key $(echo $SAUCE_ACCESS_KEY | rev) \
yarn bazel test //:test_web_all
- *notify_dev_infra_on_fail
no_output_timeout: 20m
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
test_aio:
<<: *job_defaults
docker:
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
- image: *browsers_docker_image
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
executor: browsers-executor
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
# Build aio
- run: yarn --cwd aio build --progress=false
# Lint the code
@ -293,27 +302,27 @@ jobs:
- run: yarn --cwd aio redirects-test
deploy_aio:
<<: *job_defaults
docker:
# Needed because before deploying the deploy-production script runs the PWA score tests.
- image: *browsers_docker_image
executor: browsers-executor
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
# Deploy angular.io to production (if necessary)
- run: setPublicVar_CI_STABLE_BRANCH
- run: yarn --cwd aio deploy-production
test_aio_local:
<<: *job_defaults
docker:
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
- image: *browsers_docker_image
parameters:
ivy:
type: boolean
default: false
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
executor: browsers-executor
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
# Build aio (with local Angular packages)
- run: yarn --cwd aio build-local-ci
- run: yarn --cwd aio build-local<<# parameters.ivy >>-with-ivy<</ parameters.ivy >>-ci
# Run unit tests
- run: yarn --cwd aio test --progress=false --watch=false
# Run e2e tests
@ -321,32 +330,13 @@ jobs:
# Run PWA-score tests
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
# Check the bundle sizes.
- run: yarn --cwd aio payload-size aio-local
test_aio_local_ivy:
<<: *job_defaults
docker:
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
- image: *browsers_docker_image
steps:
- *attach_workspace
- *init_environment
# Build aio with Ivy (using local Angular packages)
- run: yarn --cwd aio build-with-ivy-ci
# Run unit tests
- run: yarn --cwd aio test --progress=false --watch=false
# Run e2e tests
- run: yarn --cwd aio e2e --configuration=ci
# Run PWA-score tests
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
# Check the bundle sizes.
- run: yarn --cwd aio payload-size aio-local-ivy
- run: yarn --cwd aio payload-size aio-local<<# parameters.ivy >>-ivy<</ parameters.ivy >>
test_aio_tools:
<<: *job_defaults
executor: default-executor
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
# Install
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
- run: yarn --cwd aio extract-cli-command-docs
@ -355,56 +345,42 @@ jobs:
- run: ./aio/aio-builds-setup/scripts/test.sh
test_docs_examples:
<<: *job_defaults
docker:
parameters:
ivy:
type: boolean
default: false
executor:
# Needed because the example e2e tests depend on Chrome.
- image: *browsers_docker_image
parallelism: 4
resource_class: xlarge
steps:
- *attach_workspace
- *init_environment
# Install aio
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
# Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
# Since the parallelism is set to "3", there will be three parallel CircleCI containers
# with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument.
- run: yarn --cwd aio example-e2e --setup --local --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
test_docs_examples_ivy:
<<: *job_defaults
docker:
# Needed because the example e2e tests depend on Chrome.
- image: *browsers_docker_image
resource_class: xlarge
# We increase the parallelism here to five while the "test_docs_examples" job runs with
# a parallelism of four. This is necessary because this job also need to run NGCC which
# takes up more time and we don't want these jobs to impact the overall CI turnaround.
name: browsers-executor
resource_class: xlarge
parallelism: 5
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
# Install aio
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
# Rename the Ivy packages dist folder to "dist/packages-dist" as the AIO
# package installer picks up the locally built packages from that location.
# *Note*: We could also adjust the packages installer, but given we won't have
# two different folders of Angular distributions in the future, we should keep
# the packages installer unchanged.
- run: mv dist/packages-dist-ivy-aot dist/packages-dist
# Run examples tests with ivy. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
# Since the parallelism is set to "3", there will be three parallel CircleCI containers
# with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument.
- run: yarn --cwd aio example-e2e --setup --local --ivy --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
- when:
condition: << parameters.ivy >>
steps:
# Rename the Ivy packages dist folder to "dist/packages-dist" as the AIO
# package installer picks up the locally built packages from that location.
# *Note*: We could also adjust the packages installer, but given we won't have
# two different folders of Angular distributions in the future, we should keep
# the packages installer unchanged.
- run: mv dist/packages-dist-ivy-aot dist/packages-dist
# Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
# Since the parallelism is set to "5", there will be five parallel CircleCI containers.
# with either "0", "1", etc as node index. This can be passed to the "--shard" argument.
- run: yarn --cwd aio example-e2e --setup --local <<# parameters.ivy >>--ivy<</ parameters.ivy >> --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
aio_preview:
<<: *job_defaults
executor: default-executor
environment:
AIO_SNAPSHOT_ARTIFACT_PATH: &aio_preview_artifact_path 'aio/tmp/snapshot.tgz'
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
- run: ./aio/scripts/build-artifacts.sh $AIO_SNAPSHOT_ARTIFACT_PATH $CI_PULL_REQUEST $CI_COMMIT
- store_artifacts:
path: *aio_preview_artifact_path
@ -415,13 +391,11 @@ jobs:
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
test_aio_preview:
<<: *job_defaults
docker:
# Needed because the test-preview script runs e2e tests and the PWA score test with Chrome.
- image: *browsers_docker_image
# Needed because the test-preview script runs e2e tests and the PWA score test with Chrome.
executor: browsers-executor
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
- run:
name: Wait for preview and run tests
@ -437,19 +411,20 @@ jobs:
# Build the view engine npm packages. No new jobs should depend on this.
build-npm-packages:
<<: *job_defaults
resource_class: xlarge
executor:
name: default-executor
resource_class: xlarge
steps:
- *attach_workspace
- *init_environment
- *setup_circleci_bazel_config
- *setup_bazel_remote_execution
- custom_attach_workspace
- init_environment
- setup_circleci_bazel_config
- setup_bazel_rbe
- run: scripts/build-packages-dist.sh
# Save the npm packages from //packages/... for other workflow jobs to read
- persist_to_workspace:
root: ~/
root: *workspace_location
paths:
- ng/dist/packages-dist
@ -463,19 +438,20 @@ jobs:
# Build the ivy npm packages.
build-ivy-npm-packages:
<<: *job_defaults
resource_class: xlarge
executor:
name: default-executor
resource_class: xlarge
steps:
- *attach_workspace
- *init_environment
- *setup_circleci_bazel_config
- *setup_bazel_remote_execution
- custom_attach_workspace
- init_environment
- setup_circleci_bazel_config
- setup_bazel_rbe
- run: scripts/build-ivy-npm-packages.sh
# Save the npm packages from //packages/... for other workflow jobs to read
- persist_to_workspace:
root: ~/
root: *workspace_location
paths:
- ng/dist/packages-dist-ivy-aot
@ -486,18 +462,17 @@ jobs:
# need to re-run manually should be alleviated.
# See comments inside the integration/run_tests.sh script.
integration_test:
<<: *job_defaults
parallelism: 4
docker:
executor:
# Needed because the integration tests expect Chrome to be installed (e.g cli-hello-world)
- image: *browsers_docker_image
# Note: we run Bazel in one of the integration tests, and it can consume >2G
# of memory. Together with the system under test, this can exhaust the RAM
# on a 4G worker so we use a larger machine here too.
resource_class: xlarge
name: browsers-executor
# Note: we run Bazel in one of the integration tests, and it can consume >2G
# of memory. Together with the system under test, this can exhaust the RAM
# on a 4G worker so we use a larger machine here too.
resource_class: xlarge
parallelism: 4
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
# Runs the integration tests in parallel across multiple CircleCI container instances. The
# amount of container nodes for this job is controlled by the "parallelism" option.
- run: ./integration/run_tests.sh ${CIRCLE_NODE_INDEX} ${CIRCLE_NODE_TOTAL}
@ -505,7 +480,7 @@ jobs:
# This job updates the content of repos like github.com/angular/core-builds
# for every green build on angular/angular.
publish_snapshot:
<<: *job_defaults
executor: default-executor
steps:
# See below - ideally this job should not trigger for non-upstream builds.
# But since it does, we have to check this condition.
@ -519,8 +494,8 @@ jobs:
[[ "$CIRCLE_PROJECT_REPONAME" != "angular" ]]; then
circleci step halt
fi
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
# CircleCI has a config setting to force SSH for all github connections
# This is not compatible with our mechanism of using a Personal Access Token
# Clear the global setting
@ -534,14 +509,12 @@ jobs:
- run: ./scripts/ci/publish-build-artifacts.sh
aio_monitoring_stable:
<<: *job_defaults
docker:
# This job needs Chrome to be globally installed because the tests run with Protractor
# which does not load the browser through the Bazel webtesting rules.
- image: *browsers_docker_image
# This job needs Chrome to be globally installed because the tests run with Protractor
# which does not load the browser through the Bazel webtesting rules.
executor: browsers-executor
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
- run: setPublicVar_CI_STABLE_BRANCH
- run:
name: Check out `aio/` from the stable branch
@ -551,33 +524,36 @@ jobs:
- run:
name: Run tests against https://angular.io/
command: ./aio/scripts/test-production.sh https://angular.io/ $CI_AIO_MIN_PWA_SCORE
- *notify_caretaker_on_fail
- *notify_dev_infra_on_fail
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_CARETAKER_WEBHOOK_URL
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
aio_monitoring_next:
<<: *job_defaults
docker:
# This job needs Chrome to be globally installed because the tests run with Protractor
# which does not load the browser through the Bazel webtesting rules.
- image: *browsers_docker_image
# This job needs Chrome to be globally installed because the tests run with Protractor
# which does not load the browser through the Bazel webtesting rules.
executor: browsers-executor
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
- run:
name: Run tests against https://next.angular.io/
command: ./aio/scripts/test-production.sh https://next.angular.io/ $CI_AIO_MIN_PWA_SCORE
- *notify_caretaker_on_fail
- *notify_dev_infra_on_fail
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_CARETAKER_WEBHOOK_URL
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
legacy-unit-tests-saucelabs:
<<: *job_defaults
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
# container for this job. This is necessary because we launch a lot of browsers concurrently
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
resource_class: xlarge
executor:
name: default-executor
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
# container for this job. This is necessary because we launch a lot of browsers concurrently
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
resource_class: xlarge
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
- run:
name: Preparing environment for running tests on Saucelabs.
command: |
@ -596,10 +572,10 @@ jobs:
- run: ./scripts/saucelabs/stop-tunnel.sh
legacy-misc-tests:
<<: *job_defaults
executor: default-executor
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
- run: yarn gulp check-cycle
# TODO: disabled because the Bazel packages-dist does not seem to have map files for
# the ESM5/ES2015 output. See: https://github.com/angular/angular/issues/27966
@ -608,23 +584,22 @@ jobs:
# Job to run unit tests from angular/material2. Needs a browser since all
# component unit tests assume they're running in the browser environment.
material-unit-tests:
<<: *job_defaults
resource_class: xlarge
docker:
- image: *browsers_docker_image
executor:
name: browsers-executor
resource_class: xlarge
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
# Although RBE is configured below for the Material repo, also setup RBE in the Angular repo
# to provision Angular's GCP token into the environment variables.
- *setup_bazel_remote_execution
- setup_bazel_rbe
# Restore the cache before cloning the repository because the clone script re-uses
# the restored repository if present. This reduces the amount of times the components
# repository needs to be cloned (this is slow and increases based on commits in the repo).
- restore_cache:
keys:
- *material_unit_tests_cache_key
- *material_unit_tests_cache_key_short
- *material_unit_tests_cache_key_fallback
- run:
name: "Fetching Material repository"
command: ./scripts/ci/clone_angular_material_repo.sh
@ -647,10 +622,14 @@ jobs:
command: ./scripts/ci/run_angular_material_unit_tests.sh
test_zonejs:
<<: *job_defaults
executor:
name: default-executor
resource_class: xlarge
steps:
- *attach_workspace
- *init_environment
- custom_attach_workspace
- init_environment
- setup_circleci_bazel_config
- setup_bazel_rbe
# Install
- run: yarn --cwd packages/zone.js install --frozen-lockfile --non-interactive
# Run zone.js tools tests
@ -709,7 +688,9 @@ workflows:
- test_aio_local:
requires:
- build-npm-packages
- test_aio_local_ivy:
- test_aio_local:
name: test_aio_local_ivy
ivy: true
requires:
- build-npm-packages
- test_aio_tools:
@ -718,7 +699,9 @@ workflows:
- test_docs_examples:
requires:
- build-npm-packages
- test_docs_examples_ivy:
- test_docs_examples:
name: test_docs_examples_ivy
ivy: true
requires:
- build-ivy-npm-packages
- aio_preview:

111
.github/CODEOWNERS vendored
View File

@ -44,23 +44,19 @@
# alxhub - Alex Rickabaugh
# AndrewKushnir - Andrew Kushnir
# andrewseguin - Andrew Seguin
# benlesh - Ben Lesh
# brandonroberts - Brandon Roberts
# atscott - Andrew Scott
# devversion - Paul Gschwendtner
# filipesilva - Filipe Silva
# gkalpak - George Kalpakas
# hansl - Hans Larsen
# IgorMinar - Igor Minar
# jasonaden - Jason Aden
# jenniferfell - Jennifer Fell
# JiaLiPassion - Jia Li
# josephperrott - Joey Perrott
# kapunahelewong - Kapunahele Wong
# kara - Kara Erickson
# kyliau - Keen Yee Liau
# matsko - Matias Niemelä
# mgechev - Minko Gechev
# mhevery - Misko Hevery
# ocombe - Olivier Combe
# petebacondarwin - Pete Bacon Darwin
# pkozlowski-opensource - Pawel Kozlowski
# robwormald - Rob Wormald
@ -88,9 +84,9 @@
# (secret team to avoid review requests, it also doesn't inherit from @angular/framework because nested teams can't be secret)
#
# - IgorMinar
# - josephperrott
# - kara
# - mhevery
# - alexeagle
# ===========================================================
@ -99,8 +95,8 @@
# Used for approving minor documentation-only changes that don't require engineering review.
# (secret team to avoid review requests, it also doesn't inherit from @angular/framework because nested teams can't be secret)
#
# - brandonroberts
# - gkalpak
# - kapunahelewong
# - petebacondarwin
@ -125,10 +121,9 @@
# @angular/tools-cli
# ===========================================================
#
# - alexeagle
# - filipesilva
# - hansl
# - mgechev
# - vikerman
# ===========================================================
@ -179,8 +174,7 @@
# @angular/fw-forms
# ===========================================================
#
# - kara
# - jasonaden
# - AndrewKushnir
# ===========================================================
@ -202,7 +196,7 @@
# @angular/fw-router
# ===========================================================
#
# - jasonaden
# - atscott
# ===========================================================
@ -220,7 +214,6 @@
#
# - gkalpak
# - petebacondarwin
# - jasonaden
# ===========================================================
@ -268,8 +261,8 @@
# @angular/fw-integration
# ===========================================================
#
# - alexeagle
# - IgorMinar
# - josephperrott
# - mhevery
@ -277,7 +270,6 @@
# @angular/docs-infra
# ===========================================================
#
# - brandonroberts
# - gkalpak
# - IgorMinar
# - petebacondarwin
@ -287,8 +279,6 @@
# @angular/fw-docs-intro
# ===========================================================
#
# - jenniferfell
# - brandonroberts
# - IgorMinar
# - stephenfluin
@ -297,16 +287,15 @@
# @angular/fw-docs-observables
# ===========================================================
#
# - benlesh
# - jasonaden
# - alxhub
# ===========================================================
# @angular/fw-docs-packaging
# ===========================================================
#
# - alexeagle
# - IgorMinar
# - vikerman
# ===========================================================
@ -314,10 +303,9 @@
# ===========================================================
#
# - alan-agius4
# - alexeagle
# - hansl
# - IgorMinar
# - mgechev
# - vikerman
# ===========================================================
@ -325,11 +313,9 @@
# ===========================================================
#
# - alan-agius4
# - alexeagle
# - hansl
# - IgorMinar
# - mgechev
# - vikerman
# ===========================================================
@ -381,6 +367,43 @@
* @IgorMinar @angular/framework-global-approvers
# ================================================
# Build, CI & Dev-infra Owners
# ================================================
/* @angular/dev-infra-framework
/.buildkite/** @angular/dev-infra-framework
/.circleci/** @angular/dev-infra-framework
/.devcontainer/** @angular/dev-infra-framework
/.github/** @angular/dev-infra-framework
/.vscode/** @angular/dev-infra-framework
/docs/BAZEL.md @angular/dev-infra-framework
/packages/* @angular/dev-infra-framework
/packages/examples/test-utils/** @angular/dev-infra-framework
/packages/private/** @angular/dev-infra-framework
/scripts/** @angular/dev-infra-framework
/third_party/** @angular/dev-infra-framework
/tools/build/** @angular/dev-infra-framework
/tools/cjs-jasmine/** @angular/dev-infra-framework
/tools/gulp-tasks/** @angular/dev-infra-framework
/tools/ngcontainer/** @angular/dev-infra-framework
/tools/npm/** @angular/dev-infra-framework
/tools/npm_workspace/** @angular/dev-infra-framework
/tools/public_api_guard/** @angular/dev-infra-framework
/tools/rxjs/** @angular/dev-infra-framework
/tools/size-tracking/** @angular/dev-infra-framework
/tools/source-map-test/** @angular/dev-infra-framework
/tools/symbol-extractor/** @angular/dev-infra-framework
/tools/testing/** @angular/dev-infra-framework
/tools/ts-api-guardian/** @angular/dev-infra-framework
/tools/tslint/** @angular/dev-infra-framework
/tools/validate-commit-message/** @angular/dev-infra-framework
/tools/yarn/** @angular/dev-infra-framework
/tools/* @angular/dev-infra-framework
*.BAZEL @angular/dev-infra-framework
*.bzl @angular/dev-infra-framework
# ================================================
# @angular/animations
@ -891,42 +914,6 @@ testing/** @angular/fw-test
# ================================================
# Build, CI & Dev-infra Owners
# ================================================
/* @angular/dev-infra-framework
/.buildkite/** @angular/dev-infra-framework
/.circleci/** @angular/dev-infra-framework
/.devcontainer/** @angular/dev-infra-framework
/.github/** @angular/dev-infra-framework
/.vscode/** @angular/dev-infra-framework
/docs/BAZEL.md @angular/dev-infra-framework
/packages/* @angular/dev-infra-framework
/packages/examples/test-utils/** @angular/dev-infra-framework
/packages/private/** @angular/dev-infra-framework
/scripts/** @angular/dev-infra-framework
/third_party/** @angular/dev-infra-framework
/tools/build/** @angular/dev-infra-framework
/tools/cjs-jasmine/** @angular/dev-infra-framework
/tools/gulp-tasks/** @angular/dev-infra-framework
/tools/ngcontainer/** @angular/dev-infra-framework
/tools/npm/** @angular/dev-infra-framework
/tools/npm_workspace/** @angular/dev-infra-framework
/tools/public_api_guard/** @angular/dev-infra-framework
/tools/rxjs/** @angular/dev-infra-framework
/tools/source-map-test/** @angular/dev-infra-framework
/tools/symbol-extractor/** @angular/dev-infra-framework
/tools/testing/** @angular/dev-infra-framework
/tools/ts-api-guardian/** @angular/dev-infra-framework
/tools/tslint/** @angular/dev-infra-framework
/tools/validate-commit-message/** @angular/dev-infra-framework
/tools/yarn/** @angular/dev-infra-framework
/tools/* @angular/dev-infra-framework
*.bzl @angular/dev-infra-framework
# ================================================
# Public API
# ================================================

View File

@ -1,6 +1,64 @@
<a name="8.2.12"></a>
## [8.2.12](https://github.com/angular/angular/compare/8.2.11...8.2.12) (2019-10-23)
### Bug Fixes
* **upgrade:** remove unused version export ([#33180](https://github.com/angular/angular/issues/33180)) ([37cbcfa](https://github.com/angular/angular/commit/37cbcfa))
<a name="8.2.11"></a>
## [8.2.11](https://github.com/angular/angular/compare/8.2.10...8.2.11) (2019-10-15)
### Bug Fixes
* **service-worker:** continue serving api requests on cache failure ([#33165](https://github.com/angular/angular/issues/33165)) ([a2716ac](https://github.com/angular/angular/commit/a2716ac)), closes [#32996](https://github.com/angular/angular/issues/32996) [#21412](https://github.com/angular/angular/issues/21412)
<a name="8.2.10"></a>
## [8.2.10](https://github.com/angular/angular/compare/8.2.9...8.2.10) (2019-10-09)
This release contains various API docs improvements.
<a name="8.2.9"></a>
## [8.2.9](https://github.com/angular/angular/compare/8.2.8...8.2.9) (2019-10-02)
### Bug Fixes
* **upgrade:** fix AngularJsUrlCodec to support Safari ([#32959](https://github.com/angular/angular/issues/32959)) ([57457fb](https://github.com/angular/angular/commit/57457fb))
<a name="8.2.8"></a>
## [8.2.8](https://github.com/angular/angular/compare/8.2.7...8.2.8) (2019-09-25)
This release contains various API docs improvements.
<a name="8.2.7"></a>
## [8.2.7](https://github.com/angular/angular/compare/8.2.6...8.2.7) (2019-09-18)
### Bug Fixes
* **bazel:** ng_package(data) should support non-text files ([#32721](https://github.com/angular/angular/issues/32721)) ([ba1ef6b](https://github.com/angular/angular/commit/ba1ef6b))
* **compiler-cli:** fix typo in diagnostic template info. ([#32684](https://github.com/angular/angular/issues/32684)) ([947c076](https://github.com/angular/angular/commit/947c076)), closes [#32662](https://github.com/angular/angular/issues/32662)
* **language-service:** cache module resolution ([#32483](https://github.com/angular/angular/issues/32483)) ([1c5b157](https://github.com/angular/angular/commit/1c5b157))
<a name="8.2.6"></a>
## [8.2.6](https://github.com/angular/angular/compare/8.2.5...8.2.6) (2019-09-11)
This release contains various API docs improvements.
<a name="8.2.5"></a>

View File

@ -201,7 +201,7 @@ Must be one of the following:
* **test**: Adding missing tests or correcting existing tests
### Scope
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages.
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
The following is the list of supported scopes:

View File

@ -18,7 +18,7 @@ Here are the most important tasks you might need to use:
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
* `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
* `yarn build-with-ivy` - same as `build-local`, but in addition also turns on `ivy` mode in aio.
* `yarn build-local-with-ivy` - same as `build-local`, but in addition also turns on `ivy` mode in aio.
(Note: To turn on `ivy` mode in examples, see `yarn boilerplate:add` below.)
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.

View File

@ -206,6 +206,7 @@ function heroModuleSetup() {
nameInput.value = 'quick BROWN fOx';
// dispatch a DOM event so that Angular learns of input value change.
// use newEvent utility function (not provided by Angular) for better browser compatibility
nameInput.dispatchEvent(newEvent('input'));
// Tell Angular to update the display binding through the title pipe

View File

@ -28,7 +28,7 @@ export class KeyUpComponent_v1 {
// #docregion key-up-component-1-class
onKey(event: KeyboardEvent) { // with type info
this.values += (<HTMLInputElement>event.target).value + ' | ';
this.values += (event.target as HTMLInputElement).value + ' | ';
}
// #docregion key-up-component-1-class-no-type
}

View File

@ -1148,9 +1148,7 @@ The Angular code is shown using TypeScript.
<code-example hideCopy path="ajs-quick-reference/src/app/movie-list.component.ts" region="class"></code-example>
In Angular, you create a component class.
NOTE: If you are using TypeScript with AngularJS, you must use the `export` keyword to export the component class.
In Angular, you create a component class to contain the data model and control methods. Use the TypeScript <code>export</code> keyword to export the class so that the functionality can be imported into NgModules.
For more information, see the [Components](guide/architecture#components)
section of the [Architecture Overview](guide/architecture) page.

View File

@ -44,7 +44,7 @@ For example:
}
```
For more informaton, see the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
For more information, see the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
## Template options

View File

@ -69,8 +69,10 @@ Let's animate a simple transition that changes a single HTML element from one st
In HTML, these attributes are set using ordinary CSS styles such as color and opacity. In Angular, use the `style()` function to specify a set of CSS styles for use with animations. You can collect a set of styles in an animation state, and give the state a name, such as `open` or `closed`.
<figure>
<img src="generated/images/guide/animations/open-closed.png" alt="open and closed states">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/animations/open-closed.png" alt="open and closed states">
</div>
</figure>
### Animation state and styles
@ -93,7 +95,7 @@ In the `closed` state, shown below, the button has a height of 100 pixels, an op
In Angular, you can set multiple styles without any animation. However, without further refinement, the button instantly transforms with no fade, no shrinkage, or other visible indicator that a change is occurring.
To make the change less abrupt, we need to define an animation *transition* to specify the changes that occur between one state and another over a period of time. The `transition()` function accepts two arguments: the first argument accepts an expression that defines the direction between two transition states, and the second argument accepts an `animate()` function.
To make the change less abrupt, we need to define an animation *transition* to specify the changes that occur between one state and another over a period of time. The `transition()` function accepts two arguments: the first argument accepts an expression that defines the direction between two transition states, and the second argument accepts one or a series of `animate()` steps.
Use the `animate()` function to define the length, delay, and easing of a transition, and to designate the style function for defining styles while transitions are taking place. You can also use the `animate()` function to define the `keyframes()` function for multi-step animations. These definitions are placed in the second argument of the `animate()` function.
@ -166,8 +168,10 @@ The `trigger()` function describes the property name to watch for changes. When
In this example, we'll name the trigger `openClose`, and attach it to the `button` element. The trigger describes the open and closed states, and the timings for the two transitions.
<figure>
<img src="generated/images/guide/animations/triggering-the-animation.png" alt="triggering the animation">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/animations/triggering-the-animation.png" alt="triggering the animation">
</div>
</figure>
<div class="alert is-helpful">

View File

@ -467,7 +467,7 @@ export class AppComponent {
The collector can represent a function call or object creation with `new` as long as the syntax is valid.
The compiler, however, can later refuse to generate a call to a _particular_ function or creation of a _particular_ object.
The compiler can only create instances certain classes, supports only core decorators, and only supports calls to macros (functions or static methods) that return expressions.
The compiler can only create instances of certain classes, supports only core decorators, and only supports calls to macros (functions or static methods) that return expressions.
* New instances
The compiler only allows metadata that create instances of the class `InjectionToken` from `@angular/core`.

View File

@ -51,8 +51,10 @@ You define a component's view with its companion template. A template is a form
Views are typically arranged hierarchically, allowing you to modify or show and hide entire UI sections or pages as a unit. The template immediately associated with a component defines that component's *host view*. The component can also define a *view hierarchy*, which contains *embedded views*, hosted by other components.
<figure>
<img src="generated/images/guide/architecture/component-tree.png" alt="Component tree" class="left">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/architecture/component-tree.png" alt="Component tree" class="left">
</div>
</figure>
A view hierarchy can include views from components in the same NgModule, but it also can (and often does) include views from components that are defined in different NgModules.
@ -81,8 +83,10 @@ Angular supports *two-way data binding*, a mechanism for coordinating the parts
The following diagram shows the four forms of data binding markup. Each form has a direction: to the DOM, from the DOM, or both.
<figure>
<img src="generated/images/guide/architecture/databinding.png" alt="Data Binding" class="left">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/architecture/databinding.png" alt="Data Binding" class="left">
</div>
</figure>
This example from the `HeroListComponent` template uses three of these forms.
@ -110,14 +114,18 @@ as with event binding.
Angular processes *all* data bindings once for each JavaScript event cycle,
from the root of the application component tree through all child components.
<figure>
<img src="generated/images/guide/architecture/component-databinding.png" alt="Data Binding" class="left">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/architecture/component-databinding.png" alt="Data Binding" class="left">
</div>
</figure>
Data binding plays an important role in communication between a template and its component, and is also important for communication between parent and child components.
<figure>
<img src="generated/images/guide/architecture/parent-child-binding.png" alt="Parent/Child binding" class="left">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/architecture/parent-child-binding.png" alt="Parent/Child binding" class="left">
</div>
</figure>
### Pipes

View File

@ -35,20 +35,20 @@ Here's a simple root NgModule definition.
NgModules provide a *compilation context* for their components. A root NgModule always has a root component that is created during bootstrap, but any NgModule can include any number of additional components, which can be loaded through the router or created through the template. The components that belong to an NgModule share a compilation context.
<figure>
<img src="generated/images/guide/architecture/compilation-context.png" alt="Component compilation context" class="left">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/architecture/compilation-context.png" alt="Component compilation context" class="left">
</div>
</figure>
<br class="clear">
A component and its template together define a *view*. A component can contain a *view hierarchy*, which allows you to define arbitrarily complex areas of the screen that can be created, modified, and destroyed as a unit. A view hierarchy can mix views defined in components that belong to different NgModules. This is often the case, especially for UI libraries.
<figure>
<img src="generated/images/guide/architecture/view-hierarchy.png" alt="View hierarchy" class="left">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/architecture/view-hierarchy.png" alt="View hierarchy" class="left">
</div>
</figure>
<br class="clear">

View File

@ -70,8 +70,10 @@ When all requested services have been resolved and returned, Angular can call th
The process of `HeroService` injection looks something like this.
<figure>
<img src="generated/images/guide/architecture/injector-injects.png" alt="Service" class="left">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/architecture/injector-injects.png" alt="Service" class="left">
</div>
</figure>
### Providing services

View File

@ -113,8 +113,10 @@ To define navigation rules, you associate *navigation paths* with your component
You've learned the basics about the main building blocks of an Angular application. The following diagram shows how these basic pieces are related.
<figure>
<img src="generated/images/guide/architecture/overview2.png" alt="overview">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/architecture/overview2.png" alt="overview">
</div>
</figure>
* Together, a component and template define an Angular view.

View File

@ -142,7 +142,7 @@ Begin by adding `HostListener` to the list of imported symbols.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
Then add two eventhandlers that respond when the mouse enters or leaves,
Then add two event handlers that respond when the mouse enters or leaves,
each adorned by the `HostListener` decorator.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"></code-example>
@ -175,8 +175,10 @@ Here's the updated directive in full:
Run the app and confirm that the background color appears when
the mouse hovers over the `p` and disappears as it moves out.
<figure>
<img src="generated/images/guide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight">
</div>
</figure>
{@a bindings}
@ -247,7 +249,7 @@ You get the best of both worlds: the property name you want and the binding synt
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
Now that you're binding via the alias to the `highlightColor`, modify the `onMouseEnter()` method to use that property.
If someone neglects to bind to `appHighlightColor`, highlight the host element in red:
If someone neglects to bind to `appHighlight`, highlight the host element in red:
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"></code-example>
@ -271,8 +273,10 @@ Revise the `AppComponent.color` so that it has no initial value.
Here are the harness and directive in action.
<figure>
<img src="generated/images/guide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2">
</div>
</figure>
{@a second-property}
@ -307,8 +311,10 @@ because you made it _public_ with the `@Input` decorator.
Here's how the harness should work when you're done coding.
<figure>
<img src="generated/images/guide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight">
</div>
</figure>
## Summary

View File

@ -8,13 +8,7 @@ This page discusses build-specific configuration options for Angular projects.
You can define different named build configurations for your project, such as *stage* and *production*, with different defaults.
Each named build configuration can have defaults for any of the options that apply to the various build targets, such as `build`, `serve`, and `test`. The [Angular CLI](cli) `build`, `serve`, and `test` commands can then replace files with appropriate versions for your intended target environment.
The following figure shows how a project has multiple build targets, which can be executed using the named configurations that you define.
<figure>
<img src="generated/images/guide/build/build-config-targets.gif" alt="build configurations and targets">
</figure>
Each named configuration can have defaults for any of the options that apply to the various [builder targets](guide/glossary#target), such as `build`, `serve`, and `test`. The [Angular CLI](cli) `build`, `serve`, and `test` commands can then replace files with appropriate versions for your intended target environment.
### Configure environment-specific defaults
@ -170,8 +164,9 @@ You can also configure the `serve` command to use the targeted build configurati
```
{@a size-budgets}
{@a configure-size-budgets}
## Configure size budgets
## Configuring size budgets
As applications grow in functionality, they also grow in size.
The CLI allows you to set size thresholds in your configuration to ensure that parts of your application stay within size boundaries that you define.
@ -296,10 +291,9 @@ Autoprefixer looks for the `browserslist` configuration when it prefixes your CS
See the [browserslist repo](https://github.com/browserslist/browserslist) for more examples of how to target specific browsers and versions.
<div class="alert is-helpful">
Backward compatibility
### Backward compatibility with Lighthouse
If you want to produce a progressive web app and are using [Lighthouse](https://developers.google.com/web/tools/lighthouse/) to grade the project, add the following browserslist entry to your `package.json` file, in order to eliminate the [old flexbox](https://developers.google.com/web/tools/lighthouse/audits/old-flexbox) prefixes:
If you want to produce a progressive web app and are using [Lighthouse](https://developers.google.com/web/tools/lighthouse/) to grade the project, add the following `browserslist` entry to your `package.json` file, in order to eliminate the [old flexbox](https://developers.google.com/web/tools/lighthouse/audits/old-flexbox) prefixes:
```
"browserslist": [
@ -309,7 +303,23 @@ If you want to produce a progressive web app and are using [Lighthouse](https://
]
```
</div>
### Backward compatibility with CSS grid
CSS grid layout support in Autoprefixer, which was previously on by default, is off by default in Angular 8 and higher.
To use CSS grid with IE10/11, you must explicitly enable it using the `autoplace` option.
To do this, add the following to the top of the global styles file (or within a specific css selector scope):
```
/* autoprefixer grid: autoplace /
```
or
```
/ autoprefixer grid: no-autoplace */
```
For more information, see [Autoprefixer documentation](https://autoprefixer.github.io/).
{@a proxy}
@ -318,7 +328,7 @@ If you want to produce a progressive web app and are using [Lighthouse](https://
You can use the [proxying support](https://webpack.js.org/configuration/dev-server/#devserver-proxy) in the `webpack` dev server to divert certain URLs to a backend server, by passing a file to the `--proxy-config` build option.
For example, to divert all calls for `http://localhost:4200/api` to a server running on `http://localhost:3000/api`, take the following steps.
1. Create a file `proxy.conf.json` in the projects `src/` folder, in the same directory as `package.json`.
1. Create a file `proxy.conf.json` in your project's `src/` folder.
1. Add the following content to the new proxy file:
```

View File

@ -59,8 +59,7 @@ npm install @example/my-builder
As an example, lets create a builder that executes a shell command.
To create a builder, use the `createBuilder()` CLI Builder function, and return a `BuilderOutput` object.
```
<code-example language="typescript" header="/command/index.ts">
import { BuilderOutput, createBuilder } from '@angular-devkit/architect';
export default createBuilder(_commandBuilder);
@ -72,13 +71,13 @@ function _commandBuilder(
...
}
```
</code-example>
Now lets add some logic to it.
The following code retrieves the command and arguments from the user options, spawns the new process, and waits for the process to finish.
If the process is successful (returns a code of 0), it resolves the return value.
```
<code-example language="typescript" header="/command/index.ts">
import { BuilderOutput, createBuilder } from '@angular-devkit/architect';
import * as childProcess from 'child_process';
@ -95,7 +94,8 @@ function _commandBuilder(
});
});
}
```
</code-example>
### Handling output
@ -105,7 +105,7 @@ This also allows the builder itself to be executed in a separate process, even i
We can retrieve a Logger instance from the context.
```
<code-example language="typescript" header="/command/index.ts">
import { BuilderOutput, createBuilder, BuilderContext } from '@angular-devkit/architect';
import * as childProcess from 'child_process';
@ -130,7 +130,7 @@ function _commandBuilder(
});
}
```
</code-example>
### Progress and status reporting
@ -147,7 +147,7 @@ Use the `BuilderContext.reportStatus()` method to generate a status string of an
(Note that theres no guarantee that a long string will be shown entirely; it could be cut to fit the UI that displays it.)
Pass an empty string to remove the status.
```
<code-example language="typescript" header="/command/index.ts">
import { BuilderOutput, createBuilder, BuilderContext } from '@angular-devkit/architect';
import * as childProcess from 'child_process';
@ -174,7 +174,8 @@ function _commandBuilder(
});
});
}
```
</code-example>
## Builder input
@ -191,8 +192,7 @@ For our example builder, we expect the `options` value to be a `JsonObject` with
We can provide the following schema for type validation of these values.
<code-example language="json">
<code-example language="json" header="command/schema.json">
{
"$schema": "http://json-schema.org/schema",
"type": "object",
@ -222,7 +222,7 @@ To link our builder implementation with its schema and name, we need to create a
Create a file named `builders.json` file that looks like this.
<code-example language="json">
<code-example language="json" header="builders.json">
{
"builders": {
@ -238,7 +238,7 @@ Create a file named `builders.json` file that looks like this.
In the `package.json` file, add a `builders` key that tells the Architect tool where to find our builder definition file.
<code-example language="json">
<code-example language="json" header="package.json">
{
"name": "@example/command-runner",
@ -257,7 +257,7 @@ The first part of this is the package name (resolved using node resolution), an
Using one of our `options` is very straightforward, we did this in the previous section when we accessed `options.command`.
<code-example language="typescript">
<code-example language="typescript" header="/command/index.ts">
context.reportStatus(`Executing "${options.command}"...`);
const child = childProcess.spawn(options.command, options.args, { stdio: 'pipe' });
@ -267,14 +267,14 @@ Using one of our `options` is very straightforward, we did this in the previous
A builder must have a defined target that associates it with a specific input configuration and [project](guide/glossary#project).
Targets are defined in the `angular.json` [workspace configuration file](guide/workspace-config).
Targets are defined in the `angular.json` [CLI configuration file](guide/workspace-config).
A target specifies the builder to use, its default options configuration, and named alternative configurations.
The Architect tool uses the target definition to resolve input options for a given run.
The `angular.json` file has a section for each project, and the "architect" section of each project configures targets for builders used by CLI commands such as 'build', 'test', and 'lint'.
By default, for example, the `build` command runs the builder `@angular-devkit/build-angular:browser` to perform the build task, and passes in default option values as specified for the `build` target in `angular.json`.
By default, for example, the `build` command runs the builder `@angular-devkit/build-angular:browser` to perform the build task, and passes in default option values as specified for the `build` target in `angular.json`.
<code-example language="json">
<code-example language="json" header="angular.json">
{
"myApp": {
...
@ -361,7 +361,7 @@ npm install @example/command-runner
If we create a new project with `ng new builder-test`, the generated `angular.json` file looks something like this, with only default builder configurations.
<code-example language="json">
<code-example language="json" header="angular.json">
{
// ...
@ -413,7 +413,7 @@ We need to update the `angular.json` file to add a target for this builder to th
* The configurations key is optional, we'll leave it out for now.
<code-example language="json">
<code-example language="json" header="angular.json">
{
"projects": {
@ -493,7 +493,7 @@ Use integration testing for your builder, so that you can use the Architect sche
Heres an example of a test that runs the command builder.
The test uses the builder to run the `ls` command, then validates that it ran successfully and listed the proper files.
<code-example language="typescript">
<code-example language="typescript" header="command/index_spec.ts">
import { Architect } from '@angular-devkit/architect';
import { TestingArchitectHost } from '@angular-devkit/architect/testing';

View File

@ -50,8 +50,10 @@ and each iteration's `hero` instance to the child's `hero` property.
The running application displays three heroes:
<figure>
<img src="generated/images/guide/component-interaction/parent-to-child.png" alt="Parent-to-child">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/component-interaction/parent-to-child.png" alt="Parent-to-child">
</div>
</figure>
@ -94,8 +96,10 @@ Here's the `NameParentComponent` demonstrating name variations including a name
<figure>
<img src="generated/images/guide/component-interaction/setter.png" alt="Parent-to-child-setter">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/component-interaction/setter.png" alt="Parent-to-child-setter">
</div>
</figure>
@ -152,8 +156,10 @@ The `VersionParentComponent` supplies the `minor` and `major` values and binds b
Here's the output of a button-pushing sequence:
<figure>
<img src="generated/images/guide/component-interaction/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/component-interaction/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges">
</div>
</figure>
@ -206,8 +212,10 @@ The framework passes the event argument&mdash;represented by `$event`&mdash;to t
and the method processes it:
<figure>
<img src="generated/images/guide/component-interaction/child-to-parent.gif" alt="Child-to-parent">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/component-interaction/child-to-parent.gif" alt="Child-to-parent">
</div>
</figure>
@ -268,8 +276,10 @@ uses interpolation to display the child's `seconds` property.
Here we see the parent and child working together.
<figure>
<img src="generated/images/guide/component-interaction/countdown-timer-anim.gif" alt="countdown timer">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/component-interaction/countdown-timer-anim.gif" alt="countdown timer">
</div>
</figure>
@ -421,8 +431,10 @@ the parent `MissionControlComponent` and the `AstronautComponent` children,
facilitated by the service:
<figure>
<img src="generated/images/guide/component-interaction/bidirectional-service.gif" alt="bidirectional-service">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/component-interaction/bidirectional-service.gif" alt="bidirectional-service">
</div>
</figure>

View File

@ -1,4 +1,4 @@
# Creating Libraries
# Creating Libraries
You can create and publish new libraries to extend Angular functionality. If you find that you need to solve the same problem in more than one app (or want to share your solution with other developers), you have a candidate for a library.
@ -13,9 +13,15 @@ A simple example might be a button that sends users to your company website, tha
Use the Angular CLI to generate a new library skeleton with the following command:
<code-example language="bash">
ng new my-workspace --create-application=false
cd my-workspace
ng generate library my-lib
</code-example>
<div class="alert is-helpful">
<p>You can use the monorepo model to use the same workspace for multiple projects. See <a href="guide/file-structure#multiple-projects">Setting up for a multi-project workspace</a>.</p>
</div>
This creates the `projects/my-lib` folder in your workspace, which contains a component and a service inside an NgModule.
The workspace configuration file, `angular.json`, is updated with a project of type 'library'.
@ -188,8 +194,23 @@ You can rebuild your library whenever you make changes to it, but this extra ste
*Incremental builds* functionality improves the library-development experience.
Every time a file is changed a partial build is performed that emits the amended files.
Incremental builds can be run as a backround process in your dev environment. To take advantage of this feature add the `--watch` flag to the build command:
Incremental builds can be run as a background process in your dev environment. To take advantage of this feature add the `--watch` flag to the build command:
<code-example language="bash">
ng build my-lib --watch
</code-example>
<div class="alert is-important">
The CLI `build` command uses a different builder and invokes a different build tool for libraries than it does for applications.
* The build system for apps, `@angular-devkit/build-angular`, is based on `webpack`, and is included in all new Angular CLI projects.
* The build system for libraries is based on `ng-packagr`. It is only added to your dependencies when you add a library using `ng generate library my-lib`.
The two build systems support different things, and even where they support the same things, they do those things differently.
This means that the TypeScript source can result in different JavaScript code in a built library than it would in a built application.
For this reason, an app that depends on a library should only use TypeScript path mappings that point to the *built library*.
TypeScript path mappings should *not* point to the library source `.ts` files.
</div>

View File

@ -40,8 +40,10 @@ and the framework resolves the nested dependencies.
When all dependencies are in place, `AppComponent` displays the user information.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/logged-in-user.png" alt="Logged In User">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection-in-action/logged-in-user.png" alt="Logged In User">
</div>
</figure>
{@a service-scope}
@ -131,8 +133,10 @@ The template displays this data-bound property.
Find this example in <live-example name="dependency-injection-in-action">live code</live-example>
and confirm that the three `HeroBioComponent` instances have their own cached hero data.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/hero-bios.png" alt="Bios">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection-in-action/hero-bios.png" alt="Bios">
</div>
</figure>
{@a qualify-dependency-lookup}
@ -191,8 +195,10 @@ placing it in the `<ng-content>` slot of the `HeroBioComponent` template.
The result is shown below, with the hero's telephone number from `HeroContactComponent` projected above the hero description.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/hero-bio-and-content.png" alt="bio and contact">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection-in-action/hero-bio-and-content.png" alt="bio and contact">
</div>
</figure>
@ -221,8 +227,10 @@ When the property is marked as optional, Angular sets `loggerService` to null an
Here's `HeroBiosAndContactsComponent` in action.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/hero-bios-and-contacts.png" alt="Bios with contact into">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection-in-action/hero-bios-and-contacts.png" alt="Bios with contact into">
</div>
</figure>
@ -232,8 +240,10 @@ until it finds the logger at the `AppComponent` level.
The logger logic kicks in and the hero display updates
with the "!!!" marker to indicate that the logger was found.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/hero-bio-contact-no-host.png" alt="Without @Host">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection-in-action/hero-bio-contact-no-host.png" alt="Without @Host">
</div>
</figure>
@ -262,7 +272,7 @@ Providers can also be scoped by injector through constructor parameter decorator
</code-example>
Using the `@Self` decorator, the injector only looks at the component's injector for its providers. The `@SkipSelf` decorator allows you to skip the local injector and look up in the hierarchy to find a provider that satisfies this dependency. The `sessionStorageService` instance interacts with the `BrowserStorageService` using the `sessionStorage` browser API, while the `localStorageService` skips the local injector and uses the root `BrowserStorageService` that uses the `localStorage` browswer API.
Using the `@Self` decorator, the injector only looks at the component's injector for its providers. The `@SkipSelf` decorator allows you to skip the local injector and look up in the hierarchy to find a provider that satisfies this dependency. The `sessionStorageService` instance interacts with the `BrowserStorageService` using the `sessionStorage` browser API, while the `localStorageService` skips the local injector and uses the root `BrowserStorageService` that uses the `localStorage` browser API.
{@a component-element}
@ -294,8 +304,10 @@ first without a value (yielding the default color) and then with an assigned col
The following image shows the effect of mousing over the `<hero-bios-and-contacts>` tag.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/highlight.png" alt="Highlighted bios">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection-in-action/highlight.png" alt="Highlighted bios">
</div>
</figure>
{@a providers}
@ -347,8 +359,10 @@ You learned about some other methods in [Dependency Providers](guide/dependency-
The following `HeroOfTheMonthComponent` example demonstrates many of the alternatives and why you need them.
It's visually simple: a few properties and the logs produced by a logger.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/hero-of-month.png" alt="Hero of the month">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection-in-action/hero-of-month.png" alt="Hero of the month">
</div>
</figure>
The code behind it customizes how and where the DI framework provides dependencies.
@ -460,8 +474,10 @@ The following example puts `MinimalLogger` to use in a simplified version of `He
The `HeroOfTheMonthComponent` constructor's `logger` parameter is typed as `MinimalLogger`, so only the `logs` and `logInfo` members are visible in a TypeScript-aware editor.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/minimal-logger-intellisense.png" alt="MinimalLogger restricted API">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection-in-action/minimal-logger-intellisense.png" alt="MinimalLogger restricted API">
</div>
</figure>
@ -472,8 +488,10 @@ Behind the scenes, Angular sets the `logger` parameter to the full service regis
This is illustrated in the following image, which displays the logging date.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/date-logger-entry.png" alt="DateLoggerService entry">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection-in-action/date-logger-entry.png" alt="DateLoggerService entry">
</div>
</figure>
</div>
@ -627,8 +645,10 @@ and then pass them down to the base class through the constructor.
In this contrived example, `SortedHeroesComponent` inherits from `HeroesBaseComponent`
to display a *sorted* list of heroes.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/sorted-heroes.png" alt="Sorted Heroes">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection-in-action/sorted-heroes.png" alt="Sorted Heroes">
</div>
</figure>
The `HeroesBaseComponent` can stand on its own.

View File

@ -145,8 +145,10 @@ the same way you've done it before.
Here's *Alex* and family in action.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/alex.png" alt="Alex in action">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection-in-action/alex.png" alt="Alex in action">
</div>
</figure>
@ -201,8 +203,10 @@ which *is* what parent means.
Here's *Alice*, *Barry*, and family in action.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/alice.png" alt="Alice in action">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection-in-action/alice.png" alt="Alice in action">
</div>
</figure>

View File

@ -79,6 +79,7 @@ In the table below, you can find a list of packages which implement deployment f
| [Now](https://zeit.co/now) | [`@zeit/ng-deploy`](https://npmjs.org/package/@zeit/ng-deploy) |
| [Netlify](https://www.netlify.com/) | [`@netlify-builder/deploy`](https://npmjs.org/package/@netlify-builder/deploy) |
| [GitHub pages](https://pages.github.com/) | [`angular-cli-ghpages`](https://npmjs.org/package/angular-cli-ghpages) |
| [NPM](https://npmjs.com/) | [`ngx-deploy-npm`](https://npmjs.org/package/ngx-deploy-npm) |
If you're deploying to a self-managed server or there's no builder for your favorite cloud platform, you can either create a builder that allows you to use the `ng deploy` command, or read through this guide to learn how to manually deploy your app.
@ -304,7 +305,7 @@ Configure the Angular Router to defer loading of all other modules (and their as
or by [_lazy loading_](guide/router#asynchronous-routing "Lazy loading")
them on demand.
<div class="callout is-helpful>
<div class="callout is-helpful">
<header>Don't eagerly import something from a lazy-loaded module</header>
@ -385,8 +386,10 @@ showing exactly which classes are included in the bundle.
Here's the output for the _main_ bundle of an example app called `cli-quickstart`.
<figure>
<img src="generated/images/guide/deployment/quickstart-sourcemap-explorer.png" alt="quickstart sourcemap explorer">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/deployment/quickstart-sourcemap-explorer.png" alt="quickstart sourcemap explorer">
</div>
</figure>
{@a base-tag}

View File

@ -396,10 +396,10 @@ The following APIs have been removed starting with version 8.0.0:
| Package | API | Replacement | Notes |
| ------- | -------------- | ----------- | ----- |
| [`@angular/http`](https://v7.angular.io/api/http) | All exports | [`@angular/common/http`](https://v7.angular.io/api/common/http) | See [below](#http). |
[`@angular/http/testing`](https://v7.angular.io/api/http/testing) | All exports | [`@angular/common/http/testing`](https://v7.angular.io/api/common/http/testing) | See [below](#http). |
| `@angular/platform-browser` | [`DOCUMENT`](https://v7.angular.io/api/platform-browser/DOCUMENT) | [`DOCUMENT` in `@angular/common`](https://v7.angular.io/api/common/DOCUMENT) | Updating to version 8 with [`ng update`](cli/update) changes this automatically. |
| `@angular/core/testing` | [`TestBed.deprecatedOverrideProvider()`](https://v7.angular.io/api/core/testing/TestBed#deprecatedoverrideprovider) | [`TestBed.overrideProvider()`] (api/core/testing/TestBed#overrideprovider) | none |
| [`@angular/http`](https://v7.angular.io/api/http) | All exports | [`@angular/common/http`](api/common/http) | See [below](#http). |
[`@angular/http/testing`](https://v7.angular.io/api/http/testing) | All exports | [`@angular/common/http/testing`](api/common/http/testing) | See [below](#http). |
| `@angular/platform-browser` | [`DOCUMENT`](https://v7.angular.io/api/platform-browser/DOCUMENT) | [`DOCUMENT` in `@angular/common`](api/common/DOCUMENT) | Updating to version 8 with [`ng update`](cli/update) changes this automatically. |
| `@angular/core/testing` | [`TestBed.deprecatedOverrideProvider()`](https://v7.angular.io/api/core/testing/TestBed#deprecatedoverrideprovider) | [`TestBed.overrideProvider()`](api/core/testing/TestBed#overrideprovider) | none |
| `@angular/core/testing` | [`TestBedStatic.deprecatedOverrideProvider()`](https://v7.angular.io/api/core/testing/TestBedStatic#deprecatedoverrideprovider) | [`TestBedStatic.overrideProvider()`](api/core/testing/TestBedStatic#overrideprovider) | none |
@ -464,100 +464,3 @@ For more information about using `@angular/common/http`, see the [HttpClient gui
| --------------------- | ------------------------------------------- |
| `MockBackend` | [`HttpTestingController`](/api/common/http/testing/HttpTestingController) |
| `MockConnection` | [`HttpTestingController`](/api/common/http/testing/HttpTestingController) |
## Renderer to Renderer2 migration
### Migration Overview
The `Renderer` class has been marked as deprecated since Angular version 4. This section provides guidance on migrating from this deprecated API to the newer `Renderer2` API and what it means for your app.
### Why should I migrate to Renderer2?
The deprecated `Renderer` class has been removed in version 9 of Angular, so it's necessary to migrate to a supported API. Using `Renderer2` is the recommended strategy because it supports a similar set of functionality to `Renderer`. The API surface is quite large (with 19 methods), but the schematic should simplify this process for your applications.
### Is there action required on my end?
No. The schematic should handle most cases with the exception of `Renderer.animate()` and `Renderer.setDebugInfo()`, which already arent supported.
### What are the `__ngRendererX` methods? Why are they necessary?
Some methods either don't have exact equivalents in `Renderer2`, or they correspond to more than one expression. For example, both renderers have a `createElement()` method, but they're not equal because a call such as `renderer.createElement(parentNode, namespaceAndName)` in the `Renderer` corresponds to the following block of code in `Renderer2`:
```ts
const [namespace, name] = splitNamespace(namespaceAndName);
const el = renderer.createElement(name, namespace);
if (parentNode) {
renderer.appendChild(parentNode, el);
}
return el;
```
Migration has to guarantee that the return values of functions and types of variables stay the same. To handle the majority of cases safely, the schematic declares helper functions at the bottom of the user's file. These helpers encapsulate your own logic and keep the replacements inside your code down to a single function call. Here's an example of how the `createElement()` migration looks:
**Before:**
```ts
public createAndAppendElement() {
const el = this.renderer.createElement('span');
el.textContent = 'hello world';
return el;
}
```
**After:**
<code-example>
public createAndAppendElement() {
const el = __ngRendererCreateElement(this.renderer, this.element, 'span');
el.textContent = 'hello world';
return el;
}
// Generated code at the bottom of the file
__ngRendererCreateElement(renderer: any, parentNode: any, nameAndNamespace: any) {
const [namespace, name] = __ngRendererSplitNamespace(namespaceAndName);
const el = renderer.createElement(name, namespace);
if (parentNode) {
renderer.appendChild(parentNode, el);
}
return el;
}
__ngRendererSplitNamespace(nameAndNamespace: any) {
// returns the split name and namespace
}
</code-example>
When implementing these helper functions, the schematic ensures that they're only declared once per file and that their names are unique enough that there's a small chance of colliding with pre-existing functions in your code. The schematic also keeps their parameter types as `any` so that it doesn't have to insert extra logic that ensures that their values have the correct type.
### Im a library author. Should I run this migration?
**Library authors should definitely use this migration to move away from the `Renderer`. Otherwise, the libraries won't work with applications built with version 9.**
### Full list of method migrations
The following table shows all methods that the migration maps from `Renderer` to `Renderer2`.
|Renderer|Renderer2|
|---|---|
|`listen(renderElement, name, callback)`|`listen(renderElement, name, callback)`|
|`setElementProperty(renderElement, propertyName, propertyValue)`|`setProperty(renderElement, propertyName, propertyValue)`|
|`setText(renderNode, text)`|`setValue(renderNode, text)`|
|`listenGlobal(target, name, callback)`|`listen(target, name, callback)`|
|`selectRootElement(selectorOrNode, debugInfo?)`|`selectRootElement(selectorOrNode)`|
|`createElement(parentElement, name, debugInfo?)`|`appendChild(parentElement, createElement(name))`|
|`setElementStyle(el, style, value?)`|`value == null ? removeStyle(el, style) : setStyle(el, style, value)`
|`setElementAttribute(el, name, value?)`|`attributeValue == null ? removeAttribute(el, name) : setAttribute(el, name, value)`
|`createText(parentElement, value, debugInfo?)`|`appendChild(parentElement, createText(value))`|
|`createTemplateAnchor(parentElement)`|`appendChild(parentElement, createComment(''))`|
|`setElementClass(renderElement, className, isAdd)`|`isAdd ? addClass(renderElement, className) : removeClass(renderElement, className)`|
|`projectNodes(parentElement, nodes)`|`for (let i = 0; i < nodes.length; i<ins></ins>) { appendChild(parentElement, nodes<i>); }`|
|`attachViewAfter(node, viewRootNodes)`|`const parentElement = parentNode(node); const nextSibling = nextSibling(node); for (let i = 0; i < viewRootNodes.length; i<ins></ins>) { insertBefore(parentElement, viewRootNodes<i>, nextSibling);}`|
|`detachView(viewRootNodes)`|`for (let i = 0; i < viewRootNodes.length; i<ins></ins>) {const node = viewRootNodes<i>; const parentElement = parentNode(node); removeChild(parentElement, node);}`|
|`destroyView(hostElement, viewAllNodes)`|`for (let i = 0; i < viewAllNodes.length; i<ins></ins>) { destroyNode(viewAllNodes<i>); }`|
|`setBindingDebugInfo()`|This function is a noop in `Renderer2`.|
|`createViewRoot(hostElement)`|Should be replaced with a reference to `hostElement`|
|`invokeElementMethod(renderElement, methodName, args?)`|`(renderElement as any)<methodName>.apply(renderElement, args);`|
|`animate(element, startingStyles, keyframes, duration, delay, easing, previousPlayers?)`|Throws an error (same behavior as `Renderer.animate()`)|

View File

@ -9,8 +9,10 @@ conditionally show a message below the list.
The final UI looks like this:
<figure>
<img src="generated/images/guide/displaying-data/final.png" alt="Final UI">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/displaying-data/final.png" alt="Final UI">
</div>
</figure>
<div class="alert is-helpful">
@ -103,8 +105,10 @@ inside the `<app-root>` tag.
Now run the app. It should display the title and hero name:
<figure>
<img src="generated/images/guide/displaying-data/title-and-hero.png" alt="Title and Hero">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/displaying-data/title-and-hero.png" alt="Title and Hero">
</div>
</figure>
@ -212,8 +216,10 @@ repeat items for any [iterable](https://developer.mozilla.org/en-US/docs/Web/Jav
Now the heroes appear in an unordered list.
<figure>
<img src="generated/images/guide/displaying-data/hero-names-list.png" alt="After ngfor">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/displaying-data/hero-names-list.png" alt="After ngfor">
</div>
</figure>

View File

@ -322,6 +322,7 @@ The <code class="no-auto-link">item</code> property is `true`.
For block code snippets, we generally prefer to display code with
the Angular documentation _code-example_ component represented by the `<code-example>` tag.
The `<code-example>` tag has a `header` attribute that you use to identify the file that the example comes from. The header should be used whenever possible to establish the context of the example.
See [Code snippets and code examples](guide/docs-style-guide#code-snippets-and-code-samples) for more details.
<h3 class="no-toc">Inline code-snippets</h3>
@ -348,6 +349,8 @@ user input in a command shell or the _output_ of some process.
**Do not write inline code snippets** unless you have a good reason and the editor's permission to do so.
In all other cases, code snippets should be generated automatically from tested code samples.
For hypothetical examples such as illustrations of configuration options in a JSON file, you should still use The `<code-example>` tag with the `header` attribute to identify the context.
{@a from-code-samples}
<h3 class="no-toc">Code snippets and code samples</h3>

View File

@ -183,8 +183,10 @@ Here are two sample components and the `AdComponent` interface for reference:
## Final ad banner
The final ad banner looks like this:
<figure>
<img src="generated/images/guide/dynamic-component-loader/ads-example.gif" alt="Ads">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dynamic-component-loader/ads-example.gif" alt="Ads">
</div>
</figure>

View File

@ -197,8 +197,10 @@ Saving and retrieving the data is an exercise for another time.
The final form looks like this:
<figure>
<img src="generated/images/guide/dynamic-form/dynamic-form.png" alt="Dynamic-Form">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dynamic-form/dynamic-form.png" alt="Dynamic-Form">
</div>
</figure>

View File

@ -42,10 +42,10 @@ After you register your configured class with the browser's custom-element regis
When your custom element is placed on a page, the browser creates an instance of the registered class and adds it to the DOM. The content is provided by the component's template, which uses Angular template syntax, and is rendered using the component and DOM data. Input properties in the component correspond to input attributes for the element.
<figure>
<img src="generated/images/guide/elements/customElement1.png" alt="Custom element in browser" class="left">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/elements/customElement1.png" alt="Custom element in browser" class="left">
</div>
</figure>
<hr class="clear">
@ -64,10 +64,10 @@ Use a JavaScript function, `customElements.define()`, to register the configure
and its associated custom-element tag with the browser's `CustomElementRegistry`.
When the browser encounters the tag for the registered element, it uses the constructor to create a custom-element instance.
<figure>
<img src="generated/images/guide/elements/createElement.png" alt="Transform a component to a custom element" class="left">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/elements/createElement.png" alt="Transform a component to a custom element" class="left">
</div>
</figure>
### Mapping
@ -170,7 +170,7 @@ You can download the full code for the example <live-example downloadOnly>here</
Generic DOM APIs, such as `document.createElement()` or `document.querySelector()`, return an element type that is appropriate for the specified arguments. For example, calling `document.createElement('a')` will return an `HTMLAnchorElement`, which TypeScript knows has an `href` property. Similarly, `document.createElement('div')` will return an `HTMLDivElement`, which TypeScript knows has no `href` property.
When called with unknown elements, such as a custom element name (`popup-element` in our example), the methods will return a generic type, such as `HTMLELement`, since TypeScript can't infer the correct type of the returned element.
When called with unknown elements, such as a custom element name (`popup-element` in our example), the methods will return a generic type, such as `HTMLElement`, since TypeScript can't infer the correct type of the returned element.
Custom elements created with Angular extend `NgElement` (which in turn extends `HTMLElement`). Additionally, these custom elements will have a property for each input of the corresponding component. For example, our `popup-element` will have a `message` property of type `string`.
@ -194,7 +194,7 @@ aDialog.body = 'News'; // <-- ERROR: TypeScript knows there is no `body` proper
This is a good way to quickly get TypeScript features, such as type checking and autocomplete support, for you custom element. But it can get cumbersome if you need it in several places, because you have to cast the return type on every occurrence.
An alternative way, that only requires defining each custom element's type once, is augmenting the `HTMLELementTagNameMap`, which TypeScript uses to infer the type of a returned element based on its tag name (for DOM methods such as `document.createElement()`, `document.querySelector()`, etc.):
An alternative way, that only requires defining each custom element's type once, is augmenting the `HTMLElementTagNameMap`, which TypeScript uses to infer the type of a returned element based on its tag name (for DOM methods such as `document.createElement()`, `document.querySelector()`, etc.):
```ts
declare global {

View File

@ -103,8 +103,10 @@ Next, in the `AppComponent`, `app.component.html`, add the tag `<app-customer-da
Now, in addition to the title that renders by default, the `CustomerDashboardComponent` template renders too:
<figure>
<img src="generated/images/guide/feature-modules/feature-module.png" alt="feature module component">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/feature-modules/feature-module.png" alt="feature module component">
</div>
</figure>
<hr />

View File

@ -67,8 +67,10 @@ Here's a component with an input field for a single control implemented using re
The source of truth provides the value and status of the form element at a given point in time. In reactive forms, the form model is the source of truth. In the example above, the form model is the `FormControl` instance.
<figure>
<img src="generated/images/guide/forms-overview/key-diff-reactive-forms.png" alt="Reactive forms key differences">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms-overview/key-diff-reactive-forms.png" alt="Reactive forms key differences">
</div>
</figure>
With reactive forms, the form model is explicitly defined in the component class. The reactive form directive (in this case, `FormControlDirective`) then links the existing `FormControl` instance to a specific form element in the view using a value accessor (`ControlValueAccessor` instance).
@ -82,8 +84,10 @@ Here's the same component with an input field for a single control implemented u
In template-driven forms, the source of truth is the template.
<figure>
<img src="generated/images/guide/forms-overview/key-diff-td-forms.png" alt="Template-driven forms key differences">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms-overview/key-diff-td-forms.png" alt="Template-driven forms key differences">
</div>
</figure>
The abstraction of the form model promotes simplicity over structure. The template-driven form directive `NgModel` is responsible for creating and managing the `FormControl` instance for a given form element. It's less explicit, but you no longer have direct control over the form model.
@ -98,8 +102,10 @@ When building forms in Angular, it's important to understand how the framework h
As described above, in reactive forms each form element in the view is directly linked to a form model (`FormControl` instance). Updates from the view to the model and from the model to the view are synchronous and aren't dependent on the UI rendered. The diagrams below use the same favorite color example to demonstrate how data flows when an input field's value is changed from the view and then from the model.
<figure>
<img src="generated/images/guide/forms-overview/dataflow-reactive-forms-vtm.png" alt="Reactive forms data flow - view to model" width="100%">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms-overview/dataflow-reactive-forms-vtm.png" alt="Reactive forms data flow - view to model" width="100%">
</div>
</figure>
The steps below outline the data flow from view to model.
@ -110,8 +116,10 @@ The steps below outline the data flow from view to model.
1. The `FormControl` instance emits the new value through the `valueChanges` observable.
1. Any subscribers to the `valueChanges` observable receive the new value.
<figure>
<img src="generated/images/guide/forms-overview/dataflow-reactive-forms-mtv.png" alt="Reactive forms data flow - model to view" width="100%">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms-overview/dataflow-reactive-forms-mtv.png" alt="Reactive forms data flow - model to view" width="100%">
</div>
</figure>
The steps below outline the data flow from model to view.
@ -125,8 +133,10 @@ The steps below outline the data flow from model to view.
In template-driven forms, each form element is linked to a directive that manages the form model internally. The diagrams below use the same favorite color example to demonstrate how data flows when an input field's value is changed from the view and then from the model.
<figure>
<img src="generated/images/guide/forms-overview/dataflow-td-forms-vtm.png" alt="Template-driven forms data flow - view to model" width="100%">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms-overview/dataflow-td-forms-vtm.png" alt="Template-driven forms data flow - view to model" width="100%">
</div>
</figure>
The steps below outline the data flow from view to model when the input value changes from *Red* to *Blue*.
@ -140,8 +150,10 @@ The steps below outline the data flow from view to model when the input value ch
1. Because the component template uses two-way data binding for the `favoriteColor` property, the `favoriteColor` property in the component
is updated to the value emitted by the `ngModelChange` event (*Blue*).
<figure>
<img src="generated/images/guide/forms-overview/dataflow-td-forms-mtv.png" alt="Template-driven forms data flow - model to view" width="100%">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms-overview/dataflow-td-forms-mtv.png" alt="Template-driven forms data flow - model to view" width="100%">
</div>
</figure>
The steps below outline the data flow from model to view when the `favoriteColor` changes from *Blue* to *Red*.

View File

@ -45,8 +45,10 @@ otherwise wrestle with yourself.
You'll learn to build a template-driven form that looks like this:
<figure>
<img src="generated/images/guide/forms/hero-form-1.png" alt="Clean Form">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms/hero-form-1.png" alt="Clean Form">
</div>
</figure>
The *Hero Employment Agency* uses this form to maintain personal information about heroes.
@ -56,8 +58,10 @@ Two of the three fields on this form are required. Required fields have a green
If you delete the hero name, the form displays a validation error in an attention-grabbing style:
<figure>
<img src="generated/images/guide/forms/hero-form-2.png" alt="Invalid, Name Required">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms/hero-form-2.png" alt="Invalid, Name Required">
</div>
</figure>
Note that the *Submit* button is disabled, and the "required" bar to the left of the input control changes from green to red.
@ -272,8 +276,10 @@ you display its name using the interpolation syntax.
Running the app right now would be disappointing.
<figure>
<img src="generated/images/guide/forms/hero-form-3.png" alt="Early form with no binding">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms/hero-form-3.png" alt="Early form with no binding">
</div>
</figure>
@ -335,8 +341,10 @@ adding and deleting characters, you'd see them appear and disappear
from the interpolated text.
At some point it might look like this:
<figure>
<img src="generated/images/guide/forms/ng-model-in-action.png" alt="ngModel in action">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms/ng-model-in-action.png" alt="ngModel in action">
</div>
</figure>
The diagnostic is evidence that values really are flowing from the input box to the model and
@ -383,8 +391,10 @@ After revision, the core of the form should look like this:
If you run the app now and change every hero model property, the form might display like this:
<figure>
<img src="generated/images/guide/forms/ng-model-in-action-2.png" alt="ngModel in action">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms/ng-model-in-action-2.png" alt="ngModel in action">
</div>
</figure>
The diagnostic near the top of the form
@ -483,14 +493,18 @@ Follow these steps *precisely*:
The actions and effects are as follows:
<figure>
<img src="generated/images/guide/forms/control-state-transitions-anim.gif" alt="Control State Transition">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms/control-state-transitions-anim.gif" alt="Control State Transition">
</div>
</figure>
You should see the following transitions and class names:
<figure>
<img src="generated/images/guide/forms/ng-control-class-changes.png" alt="Control state transitions">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms/ng-control-class-changes.png" alt="Control state transitions">
</div>
</figure>
The `ng-valid`/`ng-invalid` pair is the most interesting, because you want to send a
@ -504,8 +518,10 @@ To create such visual feedback, add definitions for the `ng-*` CSS classes.
You can mark required fields and invalid data at the same time with a colored bar
on the left of the input box:
<figure>
<img src="generated/images/guide/forms/validity-required-indicator.png" alt="Invalid Form">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms/validity-required-indicator.png" alt="Invalid Form">
</div>
</figure>
You achieve this effect by adding these class definitions to a new `forms.css` file
@ -525,8 +541,10 @@ Leverage the control's state to reveal a helpful message.
When the user deletes the name, the form should look like this:
<figure>
<img src="generated/images/guide/forms/name-required-error.png" alt="Name required">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/forms/name-required-error.png" alt="Name required">
</div>
</figure>
To achieve this effect, extend the `<input>` tag with the following:

View File

@ -111,8 +111,10 @@ directives in `CommonModule`; they dont need to re-install app-wide providers
If you do import `BrowserModule` into a lazy loaded feature module,
Angular returns an error telling you to use `CommonModule` instead.
<figure>
<img src="generated/images/guide/frequent-ngmodules/browser-module-error.gif" width=750 alt="BrowserModule error">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/frequent-ngmodules/browser-module-error.gif" width=750 alt="BrowserModule error">
</div>
</figure>
<hr />

View File

@ -3,7 +3,7 @@
Injectors in Angular have rules that you can leverage to
achieve the desired visibility of injectables in your apps.
By understanding these rules, you can determine in which
NgModule or component you should declare a provider.
NgModule, Component or Directive you should declare a provider.
## Two injector hierarchies
@ -108,8 +108,10 @@ The following diagram represents the relationship between the
`root` `ModuleInjector` and its parent injectors as the
previous paragraphs describe.
<figure>
<img src="generated/images/guide/dependency-injection/injectors.svg" alt="NullInjector, ModuleInjector, root injector">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection/injectors.svg" alt="NullInjector, ModuleInjector, root injector">
</div>
</figure>
While the name `root` is a special alias, other `ModuleInjector`s
@ -592,7 +594,7 @@ its search at the `<#VIEW>` belonging to `<app-child>` (`<#VIEW>` is
included because it is injected from `@Component()`) and ends with
`<app-child>`. In this case, the `FlowerService` is resolved in the
`<app-child>`'s `providers` array with sunflower 🌻. The injector doesn't
have to look any further in the injector tree. It stops as soon as it as it
have to look any further in the injector tree. It stops as soon as it
finds the `FlowerService` and never sees the 🌺 (red hibiscus).
@ -618,7 +620,7 @@ set it up on your own, skip ahead to [Modifying service availability](guide/hier
The example app features a second service, the `AnimalService` to
demonstrate `viewProviders`.
First, create an `AnimalService` with an `emoji` property of whale 🐳:
First, create an `AnimalService` with an `emoji` property of 🐳 (whale):
<code-example path="providers-viewproviders/src/app/animal.service.ts" header="providers-viewproviders/src/app/animal.service.ts" region="animal-service">
@ -795,7 +797,7 @@ The `AnimalService` in the logical tree would look like this:
</app-root>
```
The projected content of `<app-inspector>` sees the whale 🐳, not
The projected content of `<app-inspector>` sees the 🐳 (whale), not
the 🐶 (puppy), because the
🐶 (puppy) is inside the `<app-child>` `<#VIEW>`. The `<app-inspector>` can
only see the 🐶 (puppy)
@ -1097,8 +1099,10 @@ Each tax return component has the following characteristics:
* Has the ability to save the changes to its tax return or cancel them.
<figure>
<img src="generated/images/guide/dependency-injection/hid-heroes-anim.gif" alt="Heroes in action">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection/hid-heroes-anim.gif" alt="Heroes in action">
</div>
</figure>
Suppose that the `HeroTaxReturnComponent` had logic to manage and restore changes.
@ -1168,8 +1172,10 @@ that have special capabilities suitable for whatever is going on in component (B
Component (B) is the parent of another component (C) that defines its own, even _more specialized_ provider for `CarService`.
<figure>
<img src="generated/images/guide/dependency-injection/car-components.png" alt="car components">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection/car-components.png" alt="car components">
</div>
</figure>
Behind the scenes, each component sets up its own injector with zero, one, or more providers defined for that component itself.
@ -1179,8 +1185,10 @@ its injector produces an instance of `Car` resolved by injector (C) with an `Eng
`Tires` resolved by the root injector (A).
<figure>
<img src="generated/images/guide/dependency-injection/injector-tree.png" alt="car injector tree">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/dependency-injection/injector-tree.png" alt="car injector tree">
</div>
</figure>

View File

@ -193,7 +193,7 @@ text messages with different descriptions (not different meanings), then they ar
The angular i18n extractor tool generates a file with a translation unit entry for each `i18n`
attribute in a template. By default, it assigns each translation unit a unique id such as this one:
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="generated-id"></code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" header="messages.fr.xlf.html" region="generated-id"></code-example>
When you change the translatable text, the extractor tool generates a new id for that translation unit.
You must then update the translation file with the new id.
@ -206,7 +206,7 @@ The example below defines the custom id `introductionHeader`:
When you specify a custom id, the extractor tool and compiler generate a translation unit with that
custom id.
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="custom-id"></code-example>
<code-example path="i18n/doc-files/messages.fr.xlf.html" header="messages.fr.xlf.html" region="custom-id"></code-example>
The custom id is persistent. The extractor tool does not change it when the translatable text changes.
Therefore, you do not need to update the translation. This approach makes maintenance easier.
@ -645,9 +645,9 @@ ready-to-run application package, typically for production.
When you internationalize with the AOT compiler, you must pre-build a separate application
package for each language and serve the appropriate package based on either server-side language
detection or url parameters.
detection or URL parameters.
To instruct the AOT compiler to use your translation configuration, set the three "i18n" build configuration options in your `angular.json` file.
To instruct the AOT compiler to use your translation configuration, set the three "i18n" build configuration options in your CLI configuration file, `angular.json`.
* `i18nFile`: the path to the translation file.
* `i18nFormat`: the format of the translation file.
@ -763,6 +763,7 @@ Then provide the `LOCALE_ID` in the main module:
{@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:
@ -772,7 +773,7 @@ compilation, the app will fail to load.
* Warning (default): show a 'Missing translation' warning in the console or shell.
* Ignore: do nothing.
You specify the warning level in the `configurations` section your Angular CLI build configuration. The example below shows how to set the warning level to error:
You specify the warning level in the `configurations` section of your Angular CLI configuration file, `angular.json`. The example below shows how to set the warning level to error.
```
"configurations": {
@ -786,7 +787,7 @@ You specify the warning level in the `configurations` section your Angular CLI b
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:
error.
<code-example path="i18n/doc-files/main.3.ts" header="src/main.ts">
</code-example>
@ -796,7 +797,7 @@ error:
When you use the CLI `build` or `serve` command to build your application for different locales, change the output path using the `--outputPath` command option (along with the i18n-specific command options), so that the translation files are saved to different locations.
When you are serving a locale-specific version from a subdirectory, you can also change the base URL used by your app by specifying the `--baseHref` option.
For example, if the French version of your application is served from https://myapp.com/fr/, configure the build for the French version as follows.
For example, if the French version of your application is served from https://example.com/fr/, configure the build for the French version as follows.
```
"configurations": {

View File

@ -1,86 +1,86 @@
# Angular Language Service
The Angular Language Service is a way to get completions, errors,
hints, and navigation inside your Angular templates whether they
are external in an HTML file or embedded in annotations/decorators
in a string. The Angular Language Service autodetects that you are
opening an Angular file, reads your `tsconfig.json` file, finds all the
templates you have in your application, and then provides language
services for any templates that you open.
The Angular Language Service provides code editors with a way to get completions, errors,
hints, and navigation inside Angular templates.
It works with external templates in separate HTML files, and also with in-line templates.
## Features
Your editor autodetects that you are opening an Angular file.
It then uses the Angular Language Service to read your `tsconfig.json` file, find all the
templates you have in your application, and then provide language services for any templates that you open.
Language services include:
* Completions lists
* AOT Diagnostic messages
* Quick info
* Go to definition
## Autocompletion
### Autocompletion
Autocompletion can speed up your development time by providing you with
contextual possibilities and hints as you type. This example shows
autocomplete in an interpolation. As you type it out,
contextual possibilities and hints as you type.
This example shows autocomplete in an interpolation. As you type it out,
you can hit tab to complete.
<figure>
<img src="generated/images/guide/language-service/language-completion.gif" alt="autocompletion">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/language-service/language-completion.gif" alt="autocompletion">
</div>
</figure>
There are also completions within
elements. Any elements you have as a component selector will
There are also completions within elements. Any elements you have as a component selector will
show up in the completion list.
## Error checking
### Error checking
The Angular Language Service can also forewarn you of mistakes in your code.
The Angular Language Service can forewarn you of mistakes in your code.
In this example, Angular doesn't know what `orders` is or where it comes from.
<figure>
<img src="generated/images/guide/language-service/language-error.gif" alt="error checking">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/language-service/language-error.gif" alt="error checking">
</div>
</figure>
## Navigation
### Quick info and navigation
Navigation allows you to hover to
see where a component, directive, module, etc. is from and then
click and press F12 to go directly to its definition.
The quick-info feature allows you to hover to see where components, directives, modules, and so on come from.
You can then click "Go to definition" or press F12 to go directly to the definition.
<figure>
<img src="generated/images/guide/language-service/language-navigation.gif" alt="navigation">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/language-service/language-navigation.gif" alt="navigation">
</div>
</figure>
## Angular Language Service in your editor
Angular Language Service is currently available for [Visual Studio Code](https://code.visualstudio.com/) and
[WebStorm](https://www.jetbrains.com/webstorm).
Angular Language Service is currently available as an extension for [Visual Studio Code](https://code.visualstudio.com/),
[WebStorm](https://www.jetbrains.com/webstorm), and [Sublime Text](https://www.sublimetext.com/).
### Visual Studio Code
In Visual Studio Code, install Angular Language Service from the store,
which is accessible from the bottom icon on the left menu pane.
You can also use the VS Quick Open (⌘+P on Mac, CTRL+P on Windows) to search for the extension. When you've opened it,
enter the following command:
```sh
ext install Angular.ng-template
```
Then click the install button to install the Angular Language Service.
In [Visual Studio Code](https://code.visualstudio.com/), install the extension from the [Extensions: Marketplace](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template). You can open the marketplace from the editor using the Extensions icon on the left menu pane, or use VS Quick Open (⌘+P on Mac, CTRL+P on Windows) and type "? ext".
In the marketplace, search for Angular Language Service extension, and click the **Install** button.
### WebStorm
In webstorm, you have to install the language service as a dev dependency.
When Angular sees this dev dependency, it provides the
language service inside of WebStorm. Webstorm then gives you
colorization inside the template and autocomplete in addition to the Angular Language Service.
In [WebStorm](https://www.jetbrains.com/webstorm/), you must install the language service package as a project dependency.
Here's the dev dependency
you need to have in `package.json`:
1. Add the following to your `devDependencies` in your project's `package.json`
```json
<code-example language="json" header="package.json">
devDependencies {
"@angular/language-service": "^6.0.0"
}
```
</code-example>
Then in the terminal window at the root of your project,
install the `devDependencies` with `npm` or `yarn`:
2. In the terminal window at the root of your project, install the `devDependencies` with `npm` or `yarn`:
```sh
npm install
@ -97,79 +97,72 @@ yarn
yarn install
```
When Angular sees this dev dependency, it provides the language service in the WebStorm environment.
WebStorm then gives you colorization inside the template and autocomplete in addition to the Angular Language Service.
### Sublime Text
In [Sublime Text](https://www.sublimetext.com/), you first need an extension to allow Typescript.
Install the latest version of typescript in a local `node_modules` directory:
In [Sublime Text](https://www.sublimetext.com/), the Language Service supports only in-line templates when installed as a plug-in.
You need a custom Sublime plug-in (or modifications to the current plug-in) for completions in HTML files.
To use the Language Service for in-line templates, you must first add an extension to allow TypeScript, then install the Angular Language Service plug-in. Starting with TypeScript 2.3, TypeScript has a plug-in model that the language service can use.
1. Install the latest version of TypeScript in a local `node_modules` directory:
```sh
npm install --save-dev typescript
```
Then install the Angular Language Service in the same location:
```sh
npm install --save-dev @angular/language-service
```
Starting with TypeScript 2.3, TypeScript has a language service plugin model that the language service can use.
Next, in your user preferences (`Cmd+,` or `Ctrl+,`), add:
```json
"typescript-tsdk": "<path to your folder>/node_modules/typescript/lib"
```
## Installing in your project
You can also install Angular Language Service in your project with the
following `npm` command:
2. Install the Angular Language Service package in the same location:
```sh
npm install --save-dev @angular/language-service
```
Additionally, add the following to the `"compilerOptions"` section of
your project's `tsconfig.json`.
```json
3. Once the package is installed, add the following to the `"compilerOptions"` section of your project's `tsconfig.json`.
<code-example language="json" header="tsconfig.json">
"plugins": [
{"name": "@angular/language-service"}
]
```
Note that this only provides diagnostics and completions in `.ts`
files. You need a custom sublime plugin (or modifications to the current plugin)
for completions in HTML files.
</code-example>
4. In your editor's user preferences (`Cmd+,` or `Ctrl+,`), add the following:
<code-example language="json" header="Sublime Text user preferences">
"typescript-tsdk": "<path to your folder>/node_modules/typescript/lib"
</code-example>
This allows the Angular Language Service to provide diagnostics and completions in `.ts` files.
## How the Language Service works
When you use an editor with a language service, there's an
editor process which starts a separate language process/service
to which it speaks through an [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call).
Any time you type inside of the editor, it sends information to the other process to
track the state of your project. When you trigger a completion list within a template, the editor process first parses the template into an HTML AST, or [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree). Then the Angular compiler interprets
what module the template is part of, the scope you're in, and the component selector. Then it figures out where in the template AST your cursor is. When it determines the
context, it can then determine what the children can be.
It's a little more involved if you are in an interpolation. If you have an interpolation of `{{data.---}}` inside a `div` and need the completion list after `data.---`, the compiler can't use the HTML AST to find the answer. The HTML AST can only tell the compiler that there is some text with the characters "`{{data.---}}`". That's when the template parser produces an expression AST, which resides within the template AST. The Angular Language Services then looks at `data.---` within its context and asks the TypeScript Language Service what the members of data are. TypeScript then returns the list of possibilities.
For more in-depth information, see the
[Angular Language Service API](https://github.com/angular/angular/blob/master/packages/language-service/src/types.ts)
When you use an editor with a language service, the editor starts a separate language-service process
and communicates with it through an [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call), using the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/).
When you type into the editor, the editor sends information to the language-service process to
track the state of your project.
When you trigger a completion list within a template, the editor first parses the template into an
HTML [abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
The Angular compiler interprets that tree to determine the context: which module the template is part of, the current scope, the component selector, and where your cursor is in the template AST. It can then determine the symbols that could potentially be at that position..
It's a little more involved if you are in an interpolation.
If you have an interpolation of `{{data.---}}` inside a `div` and need the completion list after `data.---`, the compiler can't use the HTML AST to find the answer.
The HTML AST can only tell the compiler that there is some text with the characters "`{{data.---}}`".
That's when the template parser produces an expression AST, which resides within the template AST.
The Angular Language Services then looks at `data.---` within its context, asks the TypeScript Language Service what the members of `data` are, and returns the list of possibilities.
<hr>
## More on Information
## More information
For more information, see [Chuck Jazdzewski's presentation](https://www.youtube.com/watch?v=ez3R0Gi4z5A&t=368s) on the Angular Language
Service from [ng-conf](https://www.ng-conf.org/) 2017.
* For more in-depth information on the implementation, see the
[Angular Language Service API](https://github.com/angular/angular/blob/master/packages/language-service/src/types.ts).
* For more on the design considerations and intentions, see [design documentation here](https://github.com/angular/vscode-ng-language-service/wiki/Design).
* See also [Chuck Jazdzewski's presentation](https://www.youtube.com/watch?v=ez3R0Gi4z5A&t=368s) on the Angular Language Service from [ng-conf](https://www.ng-conf.org/) 2017.

View File

@ -5,13 +5,13 @@
By default, NgModules are eagerly loaded, which means that as soon as the app loads, so do all the NgModules, whether or not they are immediately necessary. For large apps with lots of routes, consider lazy loading&mdash;a design pattern that loads NgModules as needed. Lazy loading helps keep initial
bundle sizes smaller, which in turn helps decrease load times.
For the final sample app with two lazy loaded modules that this page describes, see the
For the final sample app with two lazy-loaded modules that this page describes, see the
<live-example></live-example>.
There are three main steps to setting up a lazy loaded feature module:
There are three main steps to setting up a lazy-loaded feature module:
1. Create the feature module.
1. Create the feature modules routing module.
1. Create the feature module with the CLI, using the `--route` flag.
1. Create the feature modules component.
1. Configure the routes.
## Set up an app
@ -21,9 +21,9 @@ create one with the CLI. If you do already have an app, skip to
[Configure the routes](#config-routes). Enter the following command
where `customer-app` is the name of your app:
```sh
<code-example language="bash">
ng new customer-app --routing
```
</code-example>
This creates an app called `customer-app` and the `--routing` flag
generates a file called `app-routing.module.ts`, which is one of
@ -32,153 +32,136 @@ Navigate into the project by issuing the command `cd customer-app`.
## Create a feature module with routing
Next, youll need a feature module to route to. To make one, enter
the following command at the terminal window prompt where `customers` is the name of the module:
Next, youll need a feature module with a component to route to.
To make one, enter the following command in the terminal, where `customers` is the name of the feature module, and `customer-list` is the route path for loading the `customers` component:
```sh
ng generate module customers --routing
```
<code-example language="bash">
ng generate module customers --route customer-list --module app.module
</code-example>
This creates a customers folder with two files inside; `CustomersModule`
and `CustomersRoutingModule`. `CustomersModule` will act as the gatekeeper
for anything that concerns customers. `CustomersRoutingModule` will handle
any customer-related routing. This keeps the apps structure organized as
the app grows and allows you to reuse this module while easily keeping its routing intact.
This creates a `customers` folder with the new lazy-loadable module `CustomersModule` defined in the file `customers.module.ts`. The command automatically adds the `CustomerComponent` to the new feature module.
The CLI imports the `CustomersRoutingModule` into the `CustomersModule` by
adding a JavaScript import statement at the top of the file and adding
`CustomersRoutingModule` to the `@NgModule` `imports` array.
Because the new module is meant to be lazy-loaded, the command does NOT add a reference for the new feature module to the root application's module file, `app.module.ts`.
Instead, it adds the declared route, `customer-list` to the `Routes` array declared in the module provided as the `--module` option.
## Add a component to the feature module
<code-example language="typescript" header="src/app/app-routing.module.ts">
const routes: Routes = [
{ path: 'customer-list',
loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule) }
];
</code-example>
In order to see the module being lazy loaded in the browser, create a component to render some HTML when the app loads `CustomersModule`. At the command line, enter the following:
Notice that the lazy-loading syntax uses `loadChildren` followed by a function that uses the browser's built-in `import('...')` syntax for dynamic imports.
The import path is the relative path to the module.
```sh
ng generate component customers/customer-list
```
### Add another feature module
This creates a folder inside of `customers` called `customer-list`
with the four files that make up the component.
Use the same command to create a second lazy-loaded feature module with routing, along with its stub component.
Just like with the routing module, the CLI imports the
`CustomerListComponent` into the `CustomersModule`.
<code-example language="bash">
ng generate module orders --route order-list --module app.module
</code-example>
This creates a new folder called `orders` containing an `OrdersModule` and `OrdersRoutingModule`, along with the new `OrderComponent` source files.
The `order-list` route is added to the `Routes` array in `app-routing.module.ts`, using the lazy-loading syntax.
## Add another feature module
For another place to route to, create a second feature module with routing:
```sh
ng generate module orders --routing
```
This makes a new folder called `orders` containing an `OrdersModule` and an `OrdersRoutingModule`.
Now, just like with the `CustomersModule`, give it some content:
```sh
ng generate component orders/order-list
```
<code-example language="typescript" header="src/app/app-routing.module.ts">
const routes: Routes = [
{ path: 'customer-list',
loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule) },
{ path: 'order-list',
loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule) }
];
</code-example>
## Set up the UI
Though you can type the URL into the address bar, a nav
is easier for the user and more common. Replace the default
placeholder markup in `app.component.html` with a custom nav
Though you can type the URL into the address bar, a navigation UI is easier for the user and more common.
Replace the default placeholder markup in `app.component.html` with a custom nav
so you can easily navigate to your modules in the browser:
<code-example path="lazy-loading-ngmodules/src/app/app.component.html" region="app-component-template" header="src/app/app.component.html"></code-example>
<code-example path="lazy-loading-ngmodules/src/app/app.component.html" header="app.component.html" region="app-component-template" header="src/app/app.component.html"></code-example>
To see your app in the browser so far, enter the following command in the terminal window:
```sh
<code-example language="bash">
ng serve
```
</code-example>
Then go to `localhost:4200` where you should see “app works!” and three buttons.
<figure>
<img src="generated/images/guide/lazy-loading-ngmodules/three-buttons.png" width="300" alt="three buttons in the browser">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/lazy-loading-ngmodules/three-buttons.png" width="300" alt="three buttons in the browser">
</div>
</figure>
To make the buttons work, you need to configure the routing modules.
These buttons work, because the CLI automatically added the routes to the feature modules to the `routes` array in `app.module.ts`.
{@a config-routes}
## Configure the routes
The two feature modules, `OrdersModule` and `CustomersModule`, have to be
wired up to the `AppRoutingModule` so the router knows about them. The structure is as follows:
<figure>
<img src="generated/images/guide/lazy-loading-ngmodules/lazy-load-relationship.jpg" width="400" alt="lazy loaded modules diagram">
</figure>
Each feature module acts as a doorway via the router. In the `AppRoutingModule`, you configure the routes to the feature modules, in this case `OrdersModule` and `CustomersModule`. This way, the router knows to go to the feature module. The feature module then connects the `AppRoutingModule` to the `CustomersRoutingModule` or the `OrdersRoutingModule`. Those routing modules tell the router where to go to load relevant components.
### Routes at the app level
## Imports and route configuration
The CLI automatically added each feature module to the routes map at the application level.
Finish this off by adding the default route.
In `AppRoutingModule`, update the `routes` array with the following:
<code-example path="lazy-loading-ngmodules/src/app/app-routing.module.ts" id="app-routing.module.ts" region="const-routes" header="src/app/app-routing.module.ts"></code-example>
<code-example path="lazy-loading-ngmodules/src/app/app-routing.module.ts" region="const-routes" header="src/app/app-routing.module.ts"></code-example>
The first two paths are the routes to the `CustomersModule` and the `OrdersModule`.
The final entry defines a default route. The empty path matches everything that doesn't match an earlier path.
The import statements stay the same. The first two paths are the routes to the `CustomersModule` and the `OrdersModule` respectively. Notice that the lazy loading syntax uses `loadChildren` followed by a function that uses the browser's built-in `import('...')` syntax for dynamic imports. The import path is the relative path to the module.
### Inside the feature module
Next, take a look at `customers.module.ts`. If youre using the CLI and following the steps outlined in this page, you dont have to do anything here. The feature module is like a connector between the `AppRoutingModule` and the feature routing module. The `AppRoutingModule` imports the feature module, `CustomersModule`, and `CustomersModule` in turn imports the `CustomersRoutingModule`.
<code-example path="lazy-loading-ngmodules/src/app/customers/customers.module.ts" region="customers-module" header="src/app/customers/customers.module.ts"></code-example>
Next, take a look at `customers.module.ts`. If youre using the CLI and following the steps outlined in this page, you dont have to do anything here.
<code-example path="lazy-loading-ngmodules/src/app/customers/customers.module.ts" id="customers.module.ts" region="customers-module" header="src/app/customers/customers.module.ts"></code-example>
The `customers.module.ts` file imports the `CustomersRoutingModule` and `CustomerListComponent` so the `CustomersModule` class can have access to them. `CustomersRoutingModule` is then listed in the `@NgModule` `imports` array giving `CustomersModule` access to its own routing module, and `CustomerListComponent` is in the `declarations` array, which means `CustomerListComponent` belongs to the `CustomersModule`.
### Configure the feature modules routes
The feature module has its own routing module, `customers-routing.module.ts`. The `AppRoutingModule` imports the feature module, `CustomersModule`, and `CustomersModule` in turn imports the `CustomersRoutingModule`.
The next step is in `customers-routing.module.ts`. First, import the component at the top of the file with the other JavaScript import statements. Then, add the route to `CustomerListComponent`.
<code-example path="lazy-loading-ngmodules/src/app/customers/customers-routing.module.ts" region="customers-routing-module" header="src/app/customers/customers-routing.module.ts"></code-example>
The feature-specific routing module imports its own feature component, `CustomerListComponent`, along with the other JavaScript import statements. It also adds the route to its own component.
<code-example path="lazy-loading-ngmodules/src/app/customers/customers-routing.module.ts" id="customers-routing.module.ts" region="customers-routing-module" header="src/app/customers/customers-routing.module.ts"></code-example>
Notice that the `path` is set to an empty string. This is because the path in `AppRoutingModule` is already set to `customers`, so this route in the `CustomersRoutingModule`, is already within the `customers` context. Every route in this routing module is a child route.
Repeat this last step of importing the `OrdersListComponent` and configuring the Routes array for the `orders-routing.module.ts`:
The other feature module's routing module is configured similarly.
<code-example path="lazy-loading-ngmodules/src/app/orders/orders-routing.module.ts" region="orders-routing-module-detail" header="src/app/orders/orders-routing.module.ts (excerpt)"></code-example>
Now, if you view the app in the browser, the three buttons take you to each module.
<code-example path="lazy-loading-ngmodules/src/app/orders/orders-routing.module.ts" id="orders-routing.module.ts" region="orders-routing-module-detail" header="src/app/orders/orders-routing.module.ts (excerpt)"></code-example>
## Confirm its working
You can check to see that a module is indeed being lazy loaded with the Chrome developer tools. In Chrome, open the dev tools by pressing `Cmd+Option+i` on a Mac or `Ctrl+Shift+j` on a PC and go to the Network Tab.
<figure>
<img src="generated/images/guide/lazy-loading-ngmodules/network-tab.png" width="600" alt="lazy loaded modules diagram">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/lazy-loading-ngmodules/network-tab.png" width="600" alt="lazy loaded modules diagram">
</div>
</figure>
Click on the Orders or Customers button. If you see a chunk appear, youve wired everything up properly and the feature module is being lazy loaded. A chunk should appear for Orders and for Customers but will only appear once for each.
Click on the Orders or Customers button. If you see a chunk appear, everything is wired up properly and the feature module is being lazy loaded. A chunk should appear for Orders and for Customers but will only appear once for each.
<figure>
<img src="generated/images/guide/lazy-loading-ngmodules/chunk-arrow.png" width="600" alt="lazy loaded modules diagram">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/lazy-loading-ngmodules/chunk-arrow.png" width="600" alt="lazy loaded modules diagram">
</div>
</figure>
To see it again, or to test after working in the project, clear everything out by clicking the circle with a line through it in the upper left of the Network Tab:
<figure>
<img src="generated/images/guide/lazy-loading-ngmodules/clear.gif" width="200" alt="lazy loaded modules diagram">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/lazy-loading-ngmodules/clear.gif" width="200" alt="lazy loaded modules diagram">
</div>
</figure>
@ -186,17 +169,17 @@ Then reload with `Cmd+r` or `Ctrl+r`, depending on your platform.
## `forRoot()` and `forChild()`
You might have noticed that the CLI adds `RouterModule.forRoot(routes)` to the `app-routing.module.ts` `imports` array. This lets Angular know that this module,
`AppRoutingModule`, is a routing module and `forRoot()` specifies that this is the root
routing module. It configures all the
routes you pass to it, gives you access to the router directives, and registers the `RouterService`.
You might have noticed that the CLI adds `RouterModule.forRoot(routes)` to the `app-routing.module.ts` `imports` array.
This lets Angular know that this module, `AppRoutingModule`, is a routing module and `forRoot()` specifies that this is the root routing module.
It configures all the routes you pass to it, gives you access to the router directives, and registers the `RouterService`.
Use `forRoot()` in the `AppRoutingModule`&mdash;that is, one time in the app at the root level.
The CLI also adds `RouterModule.forChild(routes)` to feature routing modules. This way, Angular
knows that the route list is only responsible for providing additional routes and is intended for feature modules. You can use `forChild()` in multiple modules.
`forRoot()` contains injector configuration which is global; such as configuring the Router. `forChild()` has no injector configuration, only directives such as `RouterOutlet` and `RouterLink`.
The CLI also adds `RouterModule.forChild(routes)` to feature routing modules.
This way, Angular knows that the route list is only responsible for providing additional routes and is intended for feature modules.
You can use `forChild()` in multiple modules.
The `forRoot()` method takes care of the *global* injector configuration for the Router.
The `forChild()` method has no injector configuration. It uses directives such as `RouterOutlet` and `RouterLink`.
For more information, see the [`forRoot()` pattern](guide/singleton-services#forRoot) section of the [Singleton Services](guide/singleton-services) guide.
<hr>
@ -209,4 +192,3 @@ You may also be interested in the following:
* [Types of Feature Modules](guide/module-types).
* [Route-level code-splitting in Angular](https://web.dev/route-level-code-splitting-in-angular/)
* [Route preloading strategies in Angular](https://web.dev/route-preloading-in-angular/)

View File

@ -2,8 +2,7 @@
A component has a lifecycle managed by Angular.
Angular creates it, renders it, creates and renders its children,
checks it when its data-bound properties change, and destroys it before removing it from the DOM.
Angular creates and renders components along with their children, checks when their data-bound properties change, and destroys them before removing them from the DOM.
Angular offers **lifecycle hooks**
that provide visibility into these key life moments and the ability to act when they occur.
@ -292,8 +291,10 @@ The peek-a-boo exists to show how Angular calls the hooks in the expected order.
This snapshot reflects the state of the log after the user clicked the *Create...* button and then the *Destroy...* button.
<figure>
<img src="generated/images/guide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo">
</div>
</figure>
The sequence of log messages follows the prescribed hook calling order:
@ -350,8 +351,10 @@ Here it is attached to the repeated hero `<div>`:
Each spy's birth and death marks the birth and death of the attached hero `<div>`
with an entry in the *Hook Log* as seen here:
<figure>
<img src='generated/images/guide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive">
</div>
</figure>
Adding a hero results in a new hero `<div>`. The spy's `ngOnInit()` logs that event.
@ -441,8 +444,10 @@ The host `OnChangesParentComponent` binds to them like this:
Here's the sample in action as the user makes changes.
<figure>
<img src='generated/images/guide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges">
</div>
</figure>
The log entries appear as the string value of the *power* property changes.
@ -474,8 +479,10 @@ This code inspects certain _values of interest_, capturing and comparing their c
It writes a special message to the log when there are no substantive changes to the `hero` or the `power`
so you can see how often `DoCheck` is called. The results are illuminating:
<figure>
<img src='generated/images/guide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck">
</div>
</figure>
While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it has a frightful cost.
@ -528,8 +535,10 @@ for one turn of the browser's JavaScript cycle and that's just long enough.
Here's *AfterView* in action:
<figure>
<img src='generated/images/guide/lifecycle-hooks/after-view-anim.gif' alt="AfterView">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/lifecycle-hooks/after-view-anim.gif' alt="AfterView">
</div>
</figure>
Notice that Angular frequently calls `AfterViewChecked()`, often when there are no changes of interest.
@ -573,8 +582,10 @@ The `<ng-content>` tag is a *placeholder* for the external content.
It tells Angular where to insert that content.
In this case, the projected content is the `<app-child>` from the parent.
<figure>
<img src='generated/images/guide/lifecycle-hooks/projected-child-view.png' alt="Projected Content">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/lifecycle-hooks/projected-child-view.png' alt="Projected Content">
</div>
</figure>
<div class="alert is-helpful">

View File

@ -107,8 +107,10 @@ As you click the button, the displayed date alternates between
"**<samp>Friday, April 15, 1988</samp>**".
<figure>
<img src='generated/images/guide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle">
</div>
</figure>
@ -186,8 +188,10 @@ Now you need a component to demonstrate the pipe.
<code-example path="pipes/src/app/power-booster.component.ts" header="src/app/power-booster.component.ts"></code-example>
<figure>
<img src='generated/images/guide/pipes/power-booster.png' alt="Power Booster">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/pipes/power-booster.png' alt="Power Booster">
</div>
</figure>
@ -230,8 +234,10 @@ your pipe and two-way data binding with `ngModel`.
<figure>
<img src='generated/images/guide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator">
</div>
</figure>
@ -307,8 +313,10 @@ The Flying Heroes application extends the
code with checkbox switches and additional displays to help you experience these effects.
<figure>
<img src='generated/images/guide/pipes/flying-heroes-anim.gif' alt="Flying Heroes">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/pipes/flying-heroes-anim.gif' alt="Flying Heroes">
</div>
</figure>
@ -486,8 +494,10 @@ both requesting the heroes from the `heroes.json` file.
The component renders as the following:
<figure>
<img src='generated/images/guide/pipes/hero-list.png' alt="Hero List">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/pipes/hero-list.png' alt="Hero List">
</div>
</figure>

View File

@ -64,8 +64,10 @@ The form control assigned to `name` is displayed when the component is added to
<code-example path="reactive-forms/src/app/app.component.1.html" region="app-name-editor" header="src/app/app.component.html (name editor)"></code-example>
<figure>
<img src="generated/images/guide/reactive-forms/name-editor-1.png" alt="Name Editor">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/reactive-forms/name-editor-1.png" alt="Name Editor">
</div>
</figure>
## Managing control values
@ -108,8 +110,10 @@ Update the template with a button to simulate a name update. When you click the
The form model is the source of truth for the control, so when you click the button, the value of the input is changed within the component class, overriding its current value.
<figure>
<img src="generated/images/guide/reactive-forms/name-editor-2.png" alt="Name Editor Update">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/reactive-forms/name-editor-2.png" alt="Name Editor Update">
</div>
</figure>
<div class="alert is-helpful">
@ -188,8 +192,10 @@ To display the `ProfileEditor` component that contains the form, add it to a com
`ProfileEditor` allows you to manage the form control instances for the `firstName` and `lastName` controls within the form group instance.
<figure>
<img src="generated/images/guide/reactive-forms/profile-editor-1.png" alt="Profile Editor">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/reactive-forms/profile-editor-1.png" alt="Profile Editor">
</div>
</figure>
## Creating nested form groups
@ -214,8 +220,10 @@ Add the `address` form group containing the `street`, `city`, `state`, and `zip`
The `ProfileEditor` form is displayed as one group, but the model is broken down further to represent the logical grouping areas.
<figure>
<img src="generated/images/guide/reactive-forms/profile-editor-2.png" alt="Profile Editor Update">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/reactive-forms/profile-editor-2.png" alt="Profile Editor Update">
</div>
</figure>
<div class="alert is-helpful">
@ -346,8 +354,10 @@ Display the current status of `profileForm` using interpolation.
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.html" region="display-status" header="src/app/profile-editor/profile-editor.component.html (display status)"></code-example>
<figure>
<img src="generated/images/guide/reactive-forms/profile-editor-3.png" alt="Profile Editor Validation">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/reactive-forms/profile-editor-3.png" alt="Profile Editor Validation">
</div>
</figure>
The **Submit** button is disabled because `profileForm` is invalid due to the required `firstName` form control. After you fill out the `firstName` input, the form becomes valid and the **Submit** button is enabled.
@ -412,8 +422,10 @@ Add the template HTML below after the `<div>` closing the `formGroupName` elemen
The `*ngFor` directive iterates over each form control instance provided by the aliases form array instance. Because form array elements are unnamed, you assign the index to the `i` variable and pass it to each control to bind it to the `formControlName` input.
<figure>
<img src="generated/images/guide/reactive-forms/profile-editor-4.png" alt="Profile Editor Aliases">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/reactive-forms/profile-editor-4.png" alt="Profile Editor Aliases">
</div>
</figure>
Each time a new alias instance is added, the new form array instance is provided its control based on the index. This allows you to track each individual control when calculating the status and value of the root control.

View File

@ -25,8 +25,10 @@ Let's illustrate a router transition animation by navigating between two routes,
</br>
<figure>
<img src="generated/images/guide/animations/route-animation.gif" alt="Animations in action" width="440">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/animations/route-animation.gif" alt="Animations in action" width="440">
</div>
</figure>
## Route configuration

View File

@ -765,16 +765,20 @@ Once the app warms up, you'll see a row of navigation buttons
and the *Heroes* view with its list of heroes.
<figure>
<img src='generated/images/guide/router/hero-list.png' alt="Hero List">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/router/hero-list.png' alt="Hero List">
</div>
</figure>
Select one hero and the app takes you to a hero editing screen.
<figure>
<img src='generated/images/guide/router/hero-detail.png' alt="Crisis Center Detail">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/router/hero-detail.png' alt="Crisis Center Detail">
</div>
</figure>
@ -790,8 +794,10 @@ Angular app navigation updates the browser history as normal web navigation does
Now click the *Crisis Center* link for a list of ongoing crises.
<figure>
<img src='generated/images/guide/router/crisis-center-list.png' alt="Crisis Center List">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/router/crisis-center-list.png' alt="Crisis Center List">
</div>
</figure>
@ -803,8 +809,10 @@ Alter the name of a crisis.
Notice that the corresponding name in the crisis list does _not_ change.
<figure>
<img src='generated/images/guide/router/crisis-center-detail.png' alt="Crisis Center Detail">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/router/crisis-center-detail.png' alt="Crisis Center Detail">
</div>
</figure>
@ -819,8 +827,10 @@ Click the browser back button or the "Heroes" link instead.
Up pops a dialog box.
<figure>
<img src='generated/images/guide/router/confirm-dialog.png' alt="Confirm Dialog">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/router/confirm-dialog.png' alt="Confirm Dialog">
</div>
</figure>
@ -842,8 +852,10 @@ Proceed to the first application milestone.
Begin with a simple version of the app that navigates between two empty views.
<figure>
<img src='generated/images/guide/router/router-1-anim.gif' alt="App in action">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/router/router-1-anim.gif' alt="App in action">
</div>
</figure>
{@a import}
@ -928,8 +940,10 @@ Registering the `RouterModule.forRoot()` in the `AppModule` imports makes the `R
The root `AppComponent` is the application shell. It has a title, a navigation bar with two links, and a router outlet where the router swaps components on and off the page. Here's what you get:
<figure>
<img src='generated/images/guide/router/shell-and-outlet.png' alt="Shell">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/router/shell-and-outlet.png' alt="Shell">
</div>
</figure>
The router outlet serves as a placeholder when the routed components will be rendered below it.
@ -1363,8 +1377,10 @@ from the <live-example name="toh-pt4" title="Tour of Heroes: Services example co
Here's how the user will experience this version of the app:
<figure>
<img src='generated/images/guide/router/router-2-anim.gif' alt="App in action">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/router/router-2-anim.gif' alt="App in action">
</div>
</figure>
@ -1944,8 +1960,10 @@ For example, when returning to the hero-detail.component.ts list from the hero d
it would be nice if the viewed hero was preselected in the list.
<figure>
<img src='generated/images/guide/router/selected-hero.png' alt="Selected hero">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/router/selected-hero.png' alt="Selected hero">
</div>
</figure>
@ -2129,8 +2147,10 @@ Add some styles to apply when the list item is selected.
When the user navigates from the heroes list to the "Magneta" hero and back, "Magneta" appears selected:
<figure>
<img src='generated/images/guide/router/selected-hero.png' alt="Selected List">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/router/selected-hero.png' alt="Selected List">
</div>
</figure>
@ -2523,8 +2543,10 @@ to conform to the following recommended pattern for Angular applications:
If your app had many feature areas, the app component trees might look like this:
<figure>
<img src='generated/images/guide/router/component-tree.png' alt="Component Tree">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/router/component-tree.png' alt="Component Tree">
</div>
</figure>
@ -2789,8 +2811,10 @@ It displays a simple form with a header, an input box for the message,
and two buttons, "Send" and "Cancel".
<figure>
<img src='generated/images/guide/router/contact-popup.png' alt="Contact popup">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/router/contact-popup.png' alt="Contact popup">
</div>
</figure>

View File

@ -32,7 +32,7 @@ The context also defines a *merge strategy* that determines how changes are merg
When you create a new blank schematic with the [Schematics CLI](#cli), the generated entry function is a *rule factory*.
A `RuleFactory`object defines a higher-order function that creates a `Rule`.
<code-example language="TypeScript">
<code-example language="TypeScript" header="index.ts">
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
// You don't have to export the function as default.
@ -49,7 +49,7 @@ You need a rule, for example, to define how a template in the schematic is to be
Rules can make use of utilities provided with the `@schematics/angular` package. Look for helper functions for working with modules, dependencies, TypeScript, AST, JSON, Angular CLI workspaces and projects, and more.
<code-example language="none">
<code-example language="TypeScript" header="index.ts">
import {
JsonAstObject,
@ -69,8 +69,191 @@ Rules can collect option values from the caller and inject them into templates.
The options available to your rules, with their allowed values and defaults, are defined in the schematic's JSON schema file, `<schematic>/schema.json`.
You can define variable or enumerated data types for the schema using TypeScript interfaces.
The schema defines the types and default values of variables used in the schematic.
For example, the hypothetical "Hello World" schematic might have the following schema.
<code-example language="json" header="src/hello-world/schema.json">
{
"properties": {
"name": {
"type": "string",
"minLength": 1,
"default": "world"
},
"useColor": {
"type": "boolean"
}
}
}
</code-example>
You can see examples of schema files for the Angular CLI command schematics in [`@schematics/angular`](https://github.com/angular/angular-cli/blob/7.0.x/packages/schematics/angular/application/schema.json).
### Schematic prompts
Schematic *prompts* introduce user interaction into schematic execution.
You can configure schematic options to display a customizable question to the user.
The prompts are displayed before the execution of the schematic, which then uses the response as the value for the option.
This allows users to direct the operation of the schematic without requiring in-depth knowledge of the full spectrum of available options.
The "Hello World" schematic might, for example, ask the user for their name, and display that name in place of the default name "world". To define such a prompt, add an `x-prompt` property to the schema for the `name` variable.
Similarly, you can add a prompt to allow the user to decide whether the schematic will use color when executing its hello action. The schema with both prompts would be as follows.
<code-example language="json" header="src/hello-world/schema.json">
{
"properties": {
"name": {
"type": "string",
"minLength": 1,
"default": "world",
"x-prompt": "What is your name?"
},
"useColor": {
"type": "boolean",
"x-prompt": "Would you like the response in color?"
}
}
}
</code-example>
#### Prompt short-form syntax
These examples use a shorthand form of the prompt syntax, supplying only the text of the question.
In most cases, this is all that is required.
Notice however, that the two prompts expect different types of input.
When using the shorthand form, the most appropriate type is automatically selected based on the property's schema.
In the example, the `name` prompt uses the `input` type because it it is a string property.
The `useColor` prompt uses a `confirmation` type because it is a Boolean property.
In this case, "yes" corresponds to `true` and "no" corresponds to `false`.
There are three supported input types.
| Input type | Description |
| :----------- | :-------------------|
| confirmation | A yes or no question; ideal for Boolean options. |
| input | Textual input; ideal for string or number options. |
| list | A predefined set of allowed values. |
In the short form, the type is inferred from the property's type and constraints.
| Property Schema | Prompt Type |
| :--------------- | :------------- |
| "type": "boolean" | confirmation ("yes"=`true`, "no"=`false`) |
| "type": "string" | input |
| "type": "number" | input (only valid numbers accepted) |
| "type": "integer" | input (only valid numbers accepted) |
| "enum": [...] | list (enum members become list selections) |
In the following example, the property takes an enumerated value, so the schematic automatically chooses the list type, and creates a menu from the possible values.
<code-example language="json" header="schema.json">
"style": {
"description": "The file extension or preprocessor to use for style files.",
"type": "string",
"default": "css",
"enum": [
"css",
"scss",
"sass",
"less",
"styl"
],
"x-prompt": "Which stylesheet format would you like to use?"
}
</code-example>
The prompt runtime automatically validates the provided response against the constraints provided in the JSON schema.
If the value is not acceptable, the user is prompted for a new value.
This ensures that any values passed to the schematic meet the expectations of the schematic's implementation, so that you do not need to add additional checks within the schematic's code.
#### Prompt long-form syntax
The `x-prompt` field syntax supports a long form for cases where you require additional customization and control over the prompt.
In this form, the `x-prompt` field value is a JSON object with subfields that customize the behavior of the prompt.
| Field | Data Value |
| :----------- | :------ |
| type | `confirmation`, `input`, or `list` (selected automatically in short form) |
| message | string (required) |
| items | string and/or label/value object pair (only valid with type `list`) |
The following example of the long form is from the JSON schema for the schematic that the CLI uses to [generate applications](https://github.com/angular/angular-cli/blob/ba8a6ea59983bb52a6f1e66d105c5a77517f062e/packages/schematics/angular/application/schema.json#L56).
It defines the prompt that allows users to choose which style preprocessor they want to use for the application being created.
By using the long form, the schematic can provide more explicit formatting of the menu choices.
<code-example language="json" header="package/schematics/angular/application/schema.json">
"style": {
"description": "The file extension or preprocessor to use for style files.",
"type": "string",
"default": "css",
"enum": [
"css",
"scss",
"sass",
"less",
"styl"
],
"x-prompt": {
"message": "Which stylesheet format would you like to use?",
"type": "list",
"items": [
{ "value": "css", "label": "CSS" },
{ "value": "scss", "label": "SCSS [ https://sass-lang.com/documentation/syntax#scss ]" },
{ "value": "sass", "label": "Sass [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]" },
{ "value": "less", "label": "Less [ http://lesscss.org ]" },
{ "value": "styl", "label": "Stylus [ http://stylus-lang.com ]" }
]
},
},
</code-example>
#### x-prompt schema
The JSON schema that defines a schematic's options supports extensions to allow the declarative definition of prompts and their respective behavior.
No additional logic or changes are required to the code of a schematic to support the prompts.
The following JSON schema is a complete description of the long-form syntax for the `x-prompt` field.
<code-example language="json" header="x-prompt schema">
{
"oneOf": [
{ "type": "string" },
{
"type": "object",
"properties": {
"type": { "type": "string" },
"message": { "type": "string" },
"items": {
"type": "array",
"items": {
"oneOf": [
{ "type": "string" },
{
"type": "object",
"properties": {
"label": { "type": "string" },
"value": { }
},
"required": [ "value" ]
}
]
}
}
},
"required": [ "message" ]
}
]
}
</code-example>
{@a cli}
## Schematics CLI
@ -176,8 +359,10 @@ When you add a new named schematic to this collection, it is automatically added
In addition to the name and description, each schematic has a `factory` property that identifies the schematics entry point.
In the example, you invoke the schematic's defined functionality by calling the `helloWorld()` function in the main file, `hello-world/index.ts`.
<figure>
<img src="generated/images/guide/schematics/collection-files.gif" alt="overview">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/schematics/collection-files.gif" alt="overview">
</div>
</figure>
Each named schematic in the collection has the following main parts.

View File

@ -118,8 +118,10 @@ Angular recognizes the value as unsafe and automatically sanitizes it, which rem
tag but keeps safe content such as the `<b>` element.
<figure>
<img src='generated/images/guide/security/binding-inner-html.png' alt='A screenshot showing interpolated and bound HTML values'>
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/security/binding-inner-html.png' alt='A screenshot showing interpolated and bound HTML values'>
</div>
</figure>
@ -209,8 +211,10 @@ this, mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` cal
<figure>
<img src='generated/images/guide/security/bypass-security-component.png' alt='A screenshot showing an alert box created from a trusted URL'>
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/security/bypass-security-component.png' alt='A screenshot showing an alert box created from a trusted URL'>
</div>
</figure>

View File

@ -207,6 +207,9 @@ There are two possible degraded states:
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.
The service worker will try to recover from this state when a new
version of the application is detected and installed (that is,
when a new `ngsw.json` is available).
* `SAFE_MODE`: the service worker cannot guarantee the safety of
using cached data. Either an unexpected error occurred or all
@ -216,6 +219,12 @@ 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.
Both states are temporary; they are saved only for the lifetime of the [ServiceWorker
instance](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope).
The browser sometimes terminates an idle service worker to conserve memory and
processor power, and creates a new service worker instance in response to
network events. The new instance starts in the `NORMAL` mode, regardless of the
state of the previous instance.
#### Latest manifest hash
@ -335,6 +344,24 @@ This script can be used both to deactivate `@angular/service-worker`
as well as any other Service Workers which might have been served in
the past on your site.
### Changing your app's location
It is important to note that service workers don't work behind redirect. You
may have already encountered the error `The script resource is behind a redirect, which is disallowed`.
This can be a problem if you have to change your app's location. If you setup
a redirect from the old location (for example `example.com`) to the new
location (for example `www.example.com`) the worker will stop working.
Also, the redirect won't even trigger for users who are loading the site
entirely from Service Worker. The old worker (registered at `example.com`)
tries to update and sends requests to the old location `example.com` which
get redirected to the new location `www.example.com` and create the error
`The script resource is behind a redirect, which is disallowed`.
To remedy this, you may need to kill the old worker using one of the above
techniques ([Fail-safe](#fail-safe) or [Safety Worker](#safety-worker)).
## More on Angular service workers
You may also be interested in the following:

View File

@ -74,8 +74,10 @@ To simulate a network issue, disable network interaction for your application. I
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 class="lightbox">
<div class="card">
<img src="generated/images/guide/service-worker/offline-checkbox.png" alt="The offline checkbox in the Network tab pis checked">
</div>
</figure>
Now the app has no access to network interaction.
@ -86,8 +88,10 @@ With the addition of an Angular service worker, the application behavior changes
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 class="lightbox">
<div class="card">
<img src="generated/images/guide/service-worker/sw-active.png" alt="Requests are marked as from ServiceWorker">
</div>
</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.
@ -142,8 +146,10 @@ 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 class="lightbox">
<div class="card">
<img src="generated/images/guide/service-worker/welcome-msg-en.png" alt="It still says Welcome to Service Workers!">
</div>
</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.
@ -152,8 +158,10 @@ If you look at the `http-server` logs, you can see the service worker requesting
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 class="lightbox">
<div class="card">
<img src="generated/images/guide/service-worker/welcome-msg-fr.png" alt="The text has changed to say Bienvenue à app!">
</div>
</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.

View File

@ -47,8 +47,10 @@ You can inject the `Title` service into the root `AppComponent` and expose a bin
Bind that method to three anchor tags and voilà!
<figure>
<img src="generated/images/guide/set-document-title/set-title-anim.gif" alt="Set title">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/set-document-title/set-title-anim.gif" alt="Set title">
</div>
</figure>
Here's the complete solution:

View File

@ -104,8 +104,10 @@ to `http://localhost:4200/`.
Your app greets you with a message:
<figure>
<img src='generated/images/guide/setup-local/app-works.png' alt="Welcome to my-app!">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/setup-local/app-works.png' alt="Welcome to my-app!">
</div>
</figure>

View File

@ -125,8 +125,10 @@ The `ngIf` directive doesn't hide elements with CSS. It adds and removes them ph
Confirm that fact using browser developer tools to inspect the DOM.
<figure>
<img src='generated/images/guide/structural-directives/element-not-in-dom.png' alt="ngIf=false element not in DOM">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/structural-directives/element-not-in-dom.png' alt="ngIf=false element not in DOM">
</div>
</figure>
@ -151,8 +153,10 @@ A directive could hide the unwanted paragraph instead by setting its `display` s
While invisible, the element remains in the DOM.
<figure>
<img src='generated/images/guide/structural-directives/element-display-in-dom.png' alt="hidden element still in DOM">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/structural-directives/element-display-in-dom.png' alt="hidden element still in DOM">
</div>
</figure>
@ -211,8 +215,10 @@ Internally, Angular translates the `*ngIf` _attribute_ into a `<ng-template>` _e
The first form is not actually rendered, only the finished product ends up in the DOM.
<figure>
<img src='generated/images/guide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM">
</div>
</figure>
@ -297,7 +303,7 @@ describes additional `NgFor` directive properties and context properties.
These microsyntax mechanisms are also available to you when you write your own structural directives.
For example, microsyntax in Angular allows you to write `<div *ngFor="let item of items">{{item}}</div>`
instead of `<ng-template ngFor [ngForOf]="items"><div>{{item}}</div></ng-template`.
instead of `<ng-template ngFor [ngForOf]="items"><div>{{item}}</div></ng-template>`.
The following sections provide detailed information on constraints, grammar,
and translation of microsyntax.
@ -559,8 +565,10 @@ That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!".
Angular erases the middle "Hip!", leaving the cheer a bit less enthusiastic.
<figure>
<img src='generated/images/guide/structural-directives/template-rendering.png' alt="template tag rendering">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/structural-directives/template-rendering.png' alt="template tag rendering">
</div>
</figure>
@ -617,8 +625,10 @@ You also have a CSS style rule that happens to apply to a `<span>` within a `<p>
The constructed paragraph renders strangely.
<figure>
<img src='generated/images/guide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style">
</div>
</figure>
@ -639,8 +649,10 @@ When you try this,
the drop down is empty.
<figure>
<img src='generated/images/guide/structural-directives/bad-select.png' alt="spanned options don't work">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/structural-directives/bad-select.png' alt="spanned options don't work">
</div>
</figure>
@ -662,8 +674,10 @@ Here's the conditional paragraph again, this time using `<ng-container>`.
It renders properly.
<figure>
<img src='generated/images/guide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style">
</div>
</figure>
@ -678,8 +692,10 @@ Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
The drop down works properly.
<figure>
<img src='generated/images/guide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly">
</div>
</figure>
<div class="alert is-helpful">
@ -828,8 +844,10 @@ When the `condition` is falsy, the top (A) paragraph appears and the bottom (B)
When the `condition` is truthy, the top (A) paragraph is removed and the bottom (B) paragraph appears.
<figure>
<img src='generated/images/guide/structural-directives/unless-anim.gif' alt="UnlessDirective in action">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/structural-directives/unless-anim.gif' alt="UnlessDirective in action">
</div>
</figure>

View File

@ -635,7 +635,7 @@ which is the attribute, spelled with a lowercase `s`.
<code-example path="property-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html"></code-example>
For more details, see the [MDN HTMLTableCellElment](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) documentation.
For more details, see the [MDN HTMLTableCellElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) documentation.
<!-- Add link when Attribute Binding updates are merged:
For more about `colSpan` and `colspan`, see (Attribute Binding)[guide/template-syntax]. -->
@ -804,7 +804,7 @@ Imagine the following malicious content.
In the component template, the content might be used with interpolation:
<code-example path="property-binding/src/app/app.component.html" region="malicious-interpolated" header="src/app/app.component.ts"></code-example>
<code-example path="property-binding/src/app/app.component.html" region="malicious-interpolated" header="src/app/app.component.html"></code-example>
Fortunately, Angular data binding is on alert for dangerous HTML. In the above case,
the HTML displays as is, and the Javascript does not execute. Angular **does not**
@ -969,8 +969,10 @@ template statement on the right.
The following event binding listens for the button's click events, calling
the component's `onSave()` method whenever a click occurs:
<figure>
<img src='generated/images/guide/template-syntax/syntax-diagram.svg' alt="Syntax diagram">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/template-syntax/syntax-diagram.svg' alt="Syntax diagram">
</div>
</figure>
### Target event
@ -1306,8 +1308,10 @@ for example, the following changes the `<input>` value to uppercase:
Here are all variations in action, including the uppercase version:
<figure>
<img src='generated/images/guide/built-in-directives/ng-model-anim.gif' alt="NgModel variations">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/built-in-directives/ng-model-anim.gif' alt="NgModel variations">
</div>
</figure>
<hr/>
@ -1512,8 +1516,10 @@ Here is an illustration of the `trackBy` effect.
* With no `trackBy`, both buttons trigger complete DOM element replacement.
* With `trackBy`, only changing the `id` triggers element replacement.
<figure>
<img src="generated/images/guide/built-in-directives/ngfor-trackby.gif" alt="Animation of trackBy">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/built-in-directives/ngfor-trackby.gif" alt="Animation of trackBy">
</div>
</figure>
@ -1538,8 +1544,10 @@ Angular puts only the selected element into the DOM.
<code-example path="built-in-directives/src/app/app.component.html" region="NgSwitch" header="src/app/app.component.html"></code-example>
<figure>
<img src="generated/images/guide/built-in-directives/ngswitch.gif" alt="Animation of NgSwitch">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/built-in-directives/ngswitch.gif" alt="Animation of NgSwitch">
</div>
</figure>
`NgSwitch` is the controller directive. Bind it to an expression that returns
@ -1610,8 +1618,8 @@ by HTML.
The reference value of itemForm, without the ngForm attribute value, would be
the [HTMLFormElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement).
There is, however, a difference between a Component and a Directive in that a `Component
`will be referenced without specifying the attribute value, and a `Directive` will not
There is, however, a difference between a Component and a Directive in that a `Component`
will be referenced without specifying the attribute value, and a `Directive` will not
change the implicit reference (that is, the element).
@ -1695,8 +1703,10 @@ child component. So an `@Input()` allows data to be input _into_ the
child component from the parent component.
<figure>
<img src="generated/images/guide/inputs-outputs/input.svg" alt="Input data flow diagram">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/inputs-outputs/input.svg" alt="Input data flow diagram">
</div>
</figure>
To illustrate the use of `@Input()`, edit these parts of your app:
@ -1742,8 +1752,10 @@ With `@Input()`, Angular passes the value for `currentItem` to the child so that
The following diagram shows this structure:
<figure>
<img src="generated/images/guide/inputs-outputs/input-diagram-target-source.svg" alt="Property binding diagram">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/inputs-outputs/input-diagram-target-source.svg" alt="Property binding diagram">
</div>
</figure>
The target in the square brackets, `[]`, is the property you decorate
@ -1776,8 +1788,10 @@ the child _out_ to the parent.
An `@Output()` property should normally be initialized to an Angular [`EventEmitter`](api/core/EventEmitter) with values flowing out of the component as [events](#event-binding).
<figure>
<img src="generated/images/guide/inputs-outputs/output.svg" alt="Output diagram">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/inputs-outputs/output.svg" alt="Output diagram">
</div>
</figure>
Just like with `@Input()`, you can use `@Output()`
@ -1918,8 +1932,10 @@ The target, `item`, which is an `@Input()` property in the child component class
The following diagram is of an `@Input()` and an `@Output()` on the same
child component and shows the different parts of each:
<figure>
<img src="generated/images/guide/inputs-outputs/input-output-diagram.svg" alt="Input/Output diagram">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/inputs-outputs/input-output-diagram.svg" alt="Input/Output diagram">
</div>
</figure>
As the diagram shows, use inputs and outputs together in the same manner as using them separately. Here, the child selector is `<app-input-output>` with `item` and `deleteRequest` being `@Input()` and `@Output()`

View File

@ -41,8 +41,10 @@ It shows that Karma ran three tests that all passed.
A chrome browser also opens and displays the test output in the "Jasmine HTML Reporter" like this.
<figure>
<img src='generated/images/guide/testing/initial-jasmine-html-reporter.png' alt="Jasmine HTML Reporter in the browser">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/testing/initial-jasmine-html-reporter.png' alt="Jasmine HTML Reporter in the browser">
</div>
</figure>
Most people find this browser output easier to read than the console log.
@ -2251,8 +2253,10 @@ tests with the `RouterTestingModule`.
The `HeroDetailComponent` is a simple view with a title, two hero fields, and two buttons.
<figure>
<img src='generated/images/guide/testing/hero-detail.component.png' alt="HeroDetailComponent in action">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/testing/hero-detail.component.png' alt="HeroDetailComponent in action">
</div>
</figure>
But there's plenty of template complexity even in this simple form.
@ -2680,8 +2684,10 @@ A better solution is to create an artificial test component that demonstrates al
<code-example path="testing/src/app/shared/highlight.directive.spec.ts" region="test-component" header="app/shared/highlight.directive.spec.ts (TestComponent)"></code-example>
<figure>
<img src='generated/images/guide/testing/highlight-directive-spec.png' alt="HighlightDirective spec in action">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/testing/highlight-directive-spec.png' alt="HighlightDirective spec in action">
</div>
</figure>
<div class="alert is-helpful">
@ -2761,8 +2767,10 @@ Debug specs in the browser in the same way that you debug an application.
1. Set a breakpoint in the test.
1. Refresh the browser, and it stops at the breakpoint.
<figure>
<img src='generated/images/guide/testing/karma-1st-spec-debug.png' alt="Karma debugging">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/testing/karma-1st-spec-debug.png' alt="Karma debugging">
</div>
</figure>
<hr>

View File

@ -14,8 +14,10 @@ An asterisk `*` or *wildcard* matches any animation state. This is useful for de
For example, a transition of `open => *` applies when the element's state changes from open to anything else.
<figure>
<img src="generated/images/guide/animations/wildcard-state-500.png" alt="wildcard state expressions">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/animations/wildcard-state-500.png" alt="wildcard state expressions">
</div>
</figure>
Here's another code sample using the wildcard state together with our previous example using the `open` and `closed` states. Instead of defining each state-to-state transition pair, we're now saying that any transition to `closed` takes 1 second, and any transition to `open` takes 0.5 seconds.
@ -32,8 +34,10 @@ Use a double arrow syntax to specify state-to-state transitions in both directio
In our two-state button example, the wildcard isn't that useful because there are only two possible states, `open` and `closed`. Wildcard states are better when an element in one particular state has multiple potential states that it can change to. If our button can change from `open` to either `closed` or something like `inProgress`, using a wildcard state could reduce the amount of coding needed.
<figure>
<img src="generated/images/guide/animations/wildcard-3-states.png" alt="wildcard state with 3 states">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/animations/wildcard-3-states.png" alt="wildcard state with 3 states">
</div>
</figure>
@ -217,8 +221,10 @@ In the previous section, we saw a simple two-state transition. Now we'll create
Angular's `keyframe()` function is similar to keyframes in CSS. Keyframes allow several style changes within a single timing segment. For example, our button, instead of fading, could change color several times over a single 2-second timespan.
<figure>
<img src="generated/images/guide/animations/keyframes-500.png" alt="keyframes">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/animations/keyframes-500.png" alt="keyframes">
</div>
</figure>
The code for this color change might look like this.
@ -231,8 +237,10 @@ Keyframes include an *offset* that defines the point in the animation where each
Defining offsets for keyframes is optional. If you omit them, evenly spaced offsets are automatically assigned. For example, three keyframes without predefined offsets receive offsets of 0, 0.5, and 1. Specifying an offset of 0.8 for the middle transition in the above example might look like this.
<figure>
<img src="generated/images/guide/animations/keyframes-offset-500.png" alt="keyframes with offset">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/animations/keyframes-offset-500.png" alt="keyframes with offset">
</div>
</figure>
The code with offsets specified would be as follows.
@ -252,8 +260,10 @@ Here's an example of using keyframes to create a pulse effect:
* A keyframes sequence inserted in the middle that causes the button to appear to pulsate irregularly over the course of that same 1-second timeframe
<figure>
<img src="generated/images/guide/animations/keyframes-pulsation.png" alt="keyframes with irregular pulsation">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/animations/keyframes-pulsation.png" alt="keyframes with irregular pulsation">
</div>
</figure>
The code snippet for this animation might look like this.

View File

@ -34,7 +34,6 @@ The initial `tsconfig.json` for an Angular app typically looks like this example
<code-example lang="json" header="tsconfig.json" linenums="false">
{
"compileOnSave": false,
"compilerOptions": {
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
@ -54,6 +53,7 @@ The initial `tsconfig.json` for an Angular app typically looks like this example
"dom"
]
}
}
</code-example>

View File

@ -203,9 +203,9 @@ One solution is to provide the full URL to your application on the server, and w
value and prepend it to the request URL. If you're using the `ngExpressEngine`, as shown in the example in this guide, half
the work is already done. We'll assume this is the case, but it's trivial to provide the same functionality.
Start by creating an [HttpInterceptor](api/common/http/HttpInterceptor):
Start by creating an [HttpInterceptor](api/common/http/HttpInterceptor).
<code-example language="typescript">
<code-example language="typescript" header="universal-interceptor.ts">
import {Injectable, Inject, Optional} from '@angular/core';
import {HttpInterceptor, HttpHandler, HttpRequest, HttpHeaders} from '@angular/common/http';
@ -233,9 +233,9 @@ export class UniversalInterceptor implements HttpInterceptor {
</code-example>
Next, provide the interceptor in the providers for the server `AppModule` (app.server.module.ts):
Next, provide the interceptor in the providers for the server `AppModule`.
<code-example language="typescript">
<code-example language="typescript" header="app.server.module.ts">
import {HTTP_INTERCEPTORS} from '@angular/common/http';
import {UniversalInterceptor} from './universal-interceptor';

View File

@ -266,8 +266,10 @@ everything work seamlessly:
When you register a downgraded service, you must explicitly specify a *string token* that you want to
use in AngularJS.
<figure>
<img src="generated/images/guide/upgrade/injectors.png" alt="The two injectors in a hybrid application">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/upgrade/injectors.png" alt="The two injectors in a hybrid application">
</div>
</figure>
#### Components and the DOM
@ -302,8 +304,10 @@ ways:
bridges the related concepts of AngularJS transclusion and Angular content
projection together.
<figure>
<img src="generated/images/guide/upgrade/dom.png" alt="DOM element ownership in a hybrid application">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/upgrade/dom.png" alt="DOM element ownership in a hybrid application">
</div>
</figure>
Whenever you use a component that belongs to the other framework, a
@ -347,12 +351,14 @@ AngularJS and Angular approaches. Here's what happens:
every turn of the Angular zone. This also triggers AngularJS change
detection after every event.
<figure>
<img src="generated/images/guide/upgrade/change_detection.png" alt="Change detection in a hybrid application">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/upgrade/change_detection.png" alt="Change detection in a hybrid application">
</div>
</figure>
In practice, you do not need to call `$apply()`,
regardless of whether it is in AngularJS on Angular. The
regardless of whether it is in AngularJS or Angular. The
`UpgradeModule` does it for us. You *can* still call `$apply()` so there
is no need to remove such calls from existing code. Those calls just trigger
additional AngularJS change detection checks in a hybrid application.
@ -1622,7 +1628,7 @@ There are several notable changes here:
* You're using the property binding syntax around `ng-class`. Though Angular
does have [a very similar `ngClass`](guide/template-syntax#directives)
as AngularJS does, its value is not magically evaluated as an expression.
In Angular, you always specify in the template when an attribute's value is
In Angular, you always specify in the template when an attribute's value is
a property expression, as opposed to a literal string.
* You've replaced `ng-repeat`s with `*ngFor`s.
@ -1709,7 +1715,7 @@ Create a new `app.component.ts` file with the following `AppComponent` class:
<code-example path="upgrade-phonecat-3-final/app/app.component.ts" header="app/app.component.ts">
</code-example>
It has a simple template that only includes the `<router-outlet>.
It has a simple template that only includes the `<router-outlet>`.
This component just renders the contents of the active route and nothing else.
The selector tells Angular to plug this root component into the `<phonecat-app>`

View File

@ -82,8 +82,10 @@ Here's what the UI displays:
<figure>
<img src='generated/images/guide/user-input/keyup1-anim.gif' alt="key up 1">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/user-input/keyup1-anim.gif' alt="key up 1">
</div>
</figure>
@ -161,8 +163,10 @@ and the component does nothing.
Type something in the input box, and watch the display update with each keystroke.
<figure>
<img src='generated/images/guide/user-input/keyup-loop-back-anim.gif' alt="loop back">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/user-input/keyup-loop-back-anim.gif' alt="loop back">
</div>
</figure>
@ -211,8 +215,10 @@ Then Angular calls the event handler only when the user presses _Enter_.
Here's how it works.
<figure>
<img src='generated/images/guide/user-input/keyup3-anim.gif' alt="key up 3">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/user-input/keyup3-anim.gif' alt="key up 3">
</div>
</figure>
@ -243,8 +249,10 @@ The user can add a hero by typing the hero's name in the input box and
clicking **Add**.
<figure>
<img src='generated/images/guide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes">
</div>
</figure>

View File

@ -13,16 +13,16 @@ Running this command will:
- configure your project to use Web Workers, if it isn't already.
- add `src/app/app.worker.ts` with scaffolded code to receive messages:
```typescript
<code-example language="typescript" header="src/app/app.worker.ts">
addEventListener('message', ({ data }) => {
const response = `worker response to ${data}`;
postMessage(response);
});
```
</code-example>
- add scaffolded code to `src/app/app.component.ts` to use the worker:
```typescript
<code-example language="typescript" header="src/app/app.component.ts">
if (typeof Worker !== 'undefined') {
// Create a new
const worker = new Worker('./app.worker', { type: 'module' });
@ -34,7 +34,7 @@ Running this command will:
// Web Workers are not supported in this environment.
// You should add a fallback so that your program still executes correctly.
}
```
</code-example>
After the initial scaffolding, you will need to refactor your code to use the Web Worker by sending messages to and from.

View File

@ -66,6 +66,13 @@
"rev": true,
"title": "Happy Angular Podcast",
"url": "https://happy-angular.de/"
},
"ngruair": {
"desc": "Russian language video podcast about Angular.",
"logo": "",
"rev": true,
"title": "NgRuAir",
"url": "https://github.com/ngRuAir/ngruair"
}
}
}
@ -667,6 +674,13 @@
"rev": true,
"title": "MDB Angular Boilerplate",
"url": "https://github.com/mdbootstrap/Angular-Bootstrap-Boilerplate"
},
"dotnettricks": {
"desc": "Online videos and training for Angular.",
"logo": "",
"rev": true,
"title": "DotNetTricks",
"url": "https://www.dotnettricks.com/courses/angular"
}
}
},

View File

@ -20,7 +20,7 @@ Services are the place where you share data between parts of your application. F
Up to this point, users can view product information, and simulate sharing and being notified about product changes. They cannot, however, buy products.
In this section, you'll add a "Buy" button the product details page.
In this section, you'll add a "Buy" button to the product details page.
You'll also set up a cart service to store information about products in the cart.
<div class="alert is-helpful">
@ -105,14 +105,18 @@ When the "Buy" button is clicked, you'll use the cart service to add the current
1. To see the new "Buy" button, refresh the application and click on a product's name to display its details.
<figure>
<img src='generated/images/guide/start/product-details-buy.png' alt="Display details for selected product with a Buy button">
</figure>
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/start/product-details-buy.png' alt="Display details for selected product with a Buy button">
</div>
</figure>
1. Click the "Buy" button. The product is added to the stored list of items in the cart, and a message is displayed.
1. Click the "Buy" button. The product is added to the stored list of items in the cart, and a message is displayed.
<figure>
<img src='generated/images/guide/start/buy-alert.png' alt="Display details for selected product with a Buy button">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/start/buy-alert.png' alt="Display details for selected product with a Buy button">
</div>
</figure>
@ -150,8 +154,10 @@ We'll create the cart page in two steps:
(Note: The "Checkout" button that we provided in the top-bar component was already configured with a `routerLink` for `/cart`.)
<figure>
<img src='generated/images/guide/start/cart-works.png' alt="Display cart page before customizing">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/start/cart-works.png' alt="Display cart page before customizing">
</div>
</figure>
@ -204,8 +210,10 @@ Services can be used to share data across components:
1. Click "Checkout" to see the cart.
1. To add another product, click "My Store" to return to the product list. Repeat the steps above.
<figure>
<img src='generated/images/guide/start/cart-page-full.png' alt="Cart page with products added">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/start/cart-page-full.png' alt="Cart page with products added">
</div>
</figure>
@ -363,14 +371,18 @@ Now that your app can retrieve shipping data, you'll create a shipping component
Click on the "Checkout" button to see the updated cart. (Remember that changing the app causes the preview to refresh, which empties the cart.)
<figure>
<img src='generated/images/guide/start/cart-empty-with-shipping-prices.png' alt="Cart with link to shipping prices">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/start/cart-empty-with-shipping-prices.png' alt="Cart with link to shipping prices">
</div>
</figure>
Click on the link to navigate to the shipping prices.
<figure>
<img src='generated/images/guide/start/shipping-prices.png' alt="Display shipping prices">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/start/shipping-prices.png' alt="Display shipping prices">
</div>
</figure>

View File

@ -50,6 +50,12 @@ ng build --prod
This will produce the files that you need to deploy.
<div class="alert is-helpful">
If the above `ng build` command throws an error about missing packages, append the missing dependencies in your local project's `package.json` file to match the one in the downloaded StackBlitz project.
</div>
#### Hosting the built project
The files in the `dist/my-project-name` folder are static and can be hosted on any web server capable of serving files (`Node.js`, Java, .NET) or any backend (Firebase, Google Cloud, App Engine, others).
@ -62,8 +68,11 @@ One of the easiest ways to get your site live is to host it using Firebase.
1. Create a new project, giving it any name you like.
1. Install the `firebase-tools` CLI that will handle your deployment using `npm install -g firebase-tools`.
1. Connect your CLI to your Firebase account and initialize the connection to your project using `firebase login` and `firebase init`.
1. Follow the prompts to select the `Firebase` project you creating for hosting.
1. Deploy your application with `firebase deploy` because StackBlitz has created a `firebase.json` that tells Firebase how to serve your app.
1. Follow the prompts to select the `Firebase` project you are creating for hosting.
- Select the `Hosting` option on the first prompt.
- Select the project you previously created on Firebase.
- Select `dist/my-project-name` as the public directory.
1. Deploy your application with `firebase deploy`, because the command `firebase init` has created a `firebase.json` file that tells Firebase how to serve your app.
1. Once deployed, visit https://your-firebase-project-name.firebaseapp.com to see it live!
### Hosting an Angular app anywhere else

View File

@ -73,8 +73,10 @@ Next, you'll add a checkout form at the bottom of the "Cart" page.
After putting a few items in the cart, users can now review their items, enter name and address, and submit their purchase:
<figure>
<img src='generated/images/guide/start/cart-with-items-and-form.png' alt="Cart page with checkout form">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/start/cart-with-items-and-form.png' alt="Cart page with checkout form">
</div>
</figure>

View File

@ -2,13 +2,12 @@
Welcome to Angular!
This tutorial introduces you to the essentials of Angular.
It leverages what you already know about HTML and JavaScript&mdash;plus some useful Angular features&mdash;to build a simple online store application, with a catalog, shopping cart, and check-out form.
You don't need to install anything: you'll build the app using the [StackBlitz](https://stackblitz.com/ "StackBlitz web site") online development environment.
This tutorial introduces you to the essentials of Angular by walking you through building a simple e-commerce site with a catalog, shopping cart, and check-out form. It uses the [StackBlitz](https://stackblitz.com/ "StackBlitz website") online development environment so you can get started right away.
<div class="alert is-helpful">
We are using the StackBlitz Generator to show you a ready-made, simple application that you can examine and play with interactively. In actual development you will typically use the [Angular CLI](guide/glossary#command-line-interface-cli), a powerful command-line tool that lets you generate and modify applications. For more information, see the [CLI Overview](cli).
This guide uses the StackBlitz Generator to show you a ready-made, simple application that you can examine and play with interactively. In actual development you will typically use the [Angular CLI](guide/glossary#command-line-interface-cli), a powerful command-line tool that lets you generate and modify applications. For more information, see the [CLI Overview](cli).
</div>
@ -16,10 +15,10 @@ We are using the StackBlitz Generator to show you a ready-made, simple applicati
<header>New to web development?</header>
You'll find many resources to complement the Angular docs. Mozilla's MDN docs include both [HTML](https://developer.mozilla.org/en-US/docs/Learn/HTML "Learning HTML: Guides and tutorials") and [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript "JavaScript") introductions. [TypeScript's docs](https://www.typescriptlang.org/docs/home.html "TypeScript documentation") include a 5-minute tutorial. Various online course platforms, such as [Udemy](http://www.udemy.com "Udemy online courses") and [Codecademy](https://www.codecademy.com/ "Codeacademy online courses"), also cover web development basics.
There are many resources to complement the Angular docs. Mozilla's MDN docs include both [HTML](https://developer.mozilla.org/en-US/docs/Learn/HTML "Learning HTML: Guides and tutorials") and [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript "JavaScript") introductions. [TypeScript's docs](https://www.typescriptlang.org/docs/home.html "TypeScript documentation") include a 5-minute tutorial. Various online course platforms, such as [Udemy](http://www.udemy.com "Udemy online courses") and [Codecademy](https://www.codecademy.com/ "Codeacademy online courses"), also cover web development basics.
</div>
</div>
@ -27,115 +26,139 @@ You'll find many resources to complement the Angular docs. Mozilla's MDN docs in
## Create a new project
<h4>
<live-example name="getting-started-v0" noDownload>Click here to create a new project in StackBlitz.</live-example>
<live-example name="getting-started-v0" noDownload>Click here to create a new project in StackBlitz.</live-example>
</h4>
StackBlitz creates a starter Angular app.
We've seeded this particular app with a top bar&mdash;containing the store name and checkout icon&mdash;and the title for a product list.
StackBlitz creates a starter Angular app with a top
bar&mdash;containing the store name and
checkout icon&mdash;and the title for a product list.
<figure>
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/start/new-app.png" alt="Starter online store app">
</div>
</figure>
<div class="callout is-helpful">
<header>StackBlitz tips</header>
* Log into StackBlitz, so you can save and resume your work. If you have a GitHub account, you can log into StackBlitz with that account. In order to save your progress, first fork the project using the Fork button at the top left, then you'll be able to save your work to your own StackBlitz account by clicking the Save button.
* To copy a code example from this tutorial, click the icon at the top right of the code example box, and then paste the code snippet from the clipboard into StackBlitz.
* If the StackBlitz preview pane isn't showing what you expect, save and then click the refresh button.
* StackBlitz is continually improving, so there may be slight differences in generated code, but the app's behavior will be the same.
* Log into StackBlitz so you can save and resume your work.
If you have a GitHub account, you can log into StackBlitz
with that account. In order to save your progress, first
fork the project using the Fork button at the top left,
then you'll be able to save your work to your own StackBlitz
account by clicking the Save button.
* To copy a code example from this tutorial, click the icon
at the top right of the code example box, and then paste the
code snippet from the clipboard into StackBlitz.
* If the StackBlitz preview pane isn't showing what you
expect, save and then click the refresh button.
* StackBlitz is continually improving, so there may be
slight differences in generated code, but the app's
behavior will be the same.
</div>
{@a template-syntax}
## Template syntax
<!--
Angular extends HTML with a template syntax that gives components control over the display of content.
This section introduces five things you can do in an Angular template to affect what your user sees, based on the component's state and behavior:
-->
Angular's template syntax extends HTML and JavaScript.
This section introduces template syntax by enhancing the "Products" area.
Angular's template syntax extends HTML and JavaScript.
In this section, you'll learn about template syntax by enhancing the "Products" area.
<div class="alert is-helpful">
(So that you can focus on the template syntax, the following steps use predefined product data and methods from the `product-list.component.ts` file.)
To help you get going, the following steps use predefined product data and methods from the `product-list.component.ts` file.
1. In the `product-list` folder, open the template file `product-list.component.html`.
</div>
1. Modify the product list template to display a list of product names.
1. In the `product-list` folder, open the template
file `product-list.component.html`.
1. We want each product in the list to be displayed the same way, one after the other on the page. To iterate over the predefined list of products, use the `*ngFor` directive. Put the `*ngFor` directive on a `<div>`, as shown below:
1. Modify the product list template to display a list of product names.
1. Each product in the list displays the same way, one after another on the page. To iterate over the predefined list of products, put the `*ngFor` directive on a `<div>`, as follows:
<code-example header="src/app/product-list/product-list.component.html" path="getting-started/src/app/product-list/product-list.component.2.html" region="ngfor">
</code-example>
`*ngFor` causes the `<div>` to be repeated for each product in the list.
With `*ngFor`, the `<div>` repeats for each product in the list.
<div class="alert is-helpful">
`*ngFor` is a "structural directive". Structural directives shape or reshape the DOM's structure, typically by adding, removing, and manipulating the elements to which they are attached. Any directive with an `*` is a structural directive.
`*ngFor` is a "structural directive". Structural directives shape or reshape the DOM's structure, typically by adding, removing, and manipulating the elements to which they are attached. Any directive with an asterisk, `*`, is a structural directive.
</div>
1. To display the names of the products, use the interpolation syntax {{ }}. Interpolation renders a property's value as text. Inside the `<div>`, add an `<h3>` heading to display the interpolation of the product's name property:
1. To display the names of the products, use the interpolation syntax `{{ }}`. Interpolation renders a property's value as text. Inside the `<div>`, add an `<h3>` to display the interpolation of the product's name property:
<code-example path="getting-started/src/app/product-list/product-list.component.2.html" region="interpolation">
<code-example path="getting-started/src/app/product-list/product-list.component.2.html" header="src/app/product-list/product-list.component.html" region="interpolation">
</code-example>
The preview pane immediately updates to display the name of each product in the list.
The preview pane immediately updates to display the name of each product in the list.
<figure>
<img src="generated/images/guide/start/template-syntax-product-names.png" alt="Product names added to list">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/start/template-syntax-product-names.png" alt="Product names added to list">
</div>
</figure>
1. In the final app, each product name will be a link to product details. Add the anchor now, and set the anchor's title to be the product's name by using the property binding [ ] syntax, as shown below:
1. To make each product name a link to product details, add the `<a>` element and set its title to be the product's name by using the property binding `[ ]` syntax, as follows:
<code-example path="getting-started/src/app/product-list/product-list.component.2.html">
<code-example path="getting-started/src/app/product-list/product-list.component.2.html" header="src/app/product-list/product-list.component.html">
</code-example>
<!--
To do: Description and code don't match exactly. Do we want to just use product name as the anchor hover text to show a simple property or append "details" to show an expression? Also affects screen shot.
-->
In the preview pane, hold the pointer over a product
name to see the bound name property value, which is
the product name plus the word "details".
Interpolation `{{ }}` lets you render the
property value as text; property binding `[ ]` lets you
use the property value in a template expression.
In the preview pane, hover over the displayed product name to see the bound name property value. They are the same. Interpolation {{ }} lets you render the property value as text; property binding [ ] lets you use the property value in a template expression.
<figure>
<img src="generated/images/guide/start/template-syntax-product-anchor.png" alt="Product name anchor text is product name property">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/start/template-syntax-product-anchor.png" alt="Product name anchor text is product name property">
</div>
</figure>
1. Add the product descriptions. On the paragraph tag, use an `*ngIf` directive so that the paragraph element is only created if the current product has a description.
<code-example path="getting-started/src/app/product-list/product-list.component.3.html">
1. Add the product descriptions. On the `<p>` element, use an `*ngIf` directive so that Angular only creates the `<p>` element if the current product has a description.
<code-example path="getting-started/src/app/product-list/product-list.component.3.html" header="src/app/product-list/product-list.component.html">
</code-example>
The app now displays the name and description of each product in the list, as shown below. Notice that the final product does not have a description paragraph at all. Because the product's description property is empty, the paragraph element&mdash;including the word "Description"&mdash;is not created.
The app now displays the name and description of each product in the list. Notice that the final product does not have a description paragraph. Because the product's description property is empty, Angular doesn't create the `<p>` element&mdash;including the word "Description".
<figure>
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/start/template-syntax-product-description.png" alt="Product descriptions added to list">
</div>
</figure>
1. Add a button so users can share a product with friends. Bind the button's `click` event to the `share()` event that we defined for you (in `product-list.component.ts`). Event binding is done by using ( ) around the event, as shown below:
1. Add a button so users can share a product with friends. Bind the button's `click` event to the `share()` method (in `product-list.component.ts`). Event binding uses a set of parentheses, `( )`, around the event, as in the following `<button>` element:
<code-example path="getting-started/src/app/product-list/product-list.component.4.html">
<code-example path="getting-started/src/app/product-list/product-list.component.4.html" header="src/app/product-list/product-list.component.html">
</code-example>
Each product now has a "Share" button:
Each product now has a "Share" button:
<figure>
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/start/template-syntax-product-share-button.png" alt="Share button added for each product">
</div>
</figure>
Test the "Share" button:
Test the "Share" button:
<figure>
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/start/template-syntax-product-share-alert.png" alt="Alert box indicating product has been shared">
</div>
</figure>
The app now has a product list and sharing feature.
In the process, you've learned to use five common features of Angular's template syntax:
The app now has a product list and sharing feature.
In the process, you've learned to use five common features of Angular's template syntax:
* `*ngFor`
* `*ngIf`
* Interpolation `{{ }}`
@ -145,7 +168,8 @@ In the process, you've learned to use five common features of Angular's template
<div class="alert is-helpful">
Learn more: See the [Template Syntax guide](guide/template-syntax "Template Syntax") for information about the full capabilities of Angular's template syntax.
For more information about the full capabilities of Angular's
template syntax, see [Template Syntax](guide/template-syntax "Template Syntax").
</div>
@ -153,50 +177,55 @@ Learn more: See the [Template Syntax guide](guide/template-syntax "Template Synt
{@a components}
## Components
*Components* define areas of responsibility in your UI that let you reuse these sets of UI functionality.
You've already built one with the product list component.
*Components* define areas of responsibility in the user interface, or UI,
that let you reuse sets of UI functionality.
You've already built one with the product list component.
A component is comprised of three things:
* **A component class,** which handles data and functionality. In the previous section, the product data and the `share()` method were defined for you in the component class.
* **An HTML template,** which determines what is presented to the user. In the previous section, you modified the product list's HTML template to display the name, description, and a "Share" button for each product.
* **Component-specific styles** that define the look and feel. The product list does not define any styles.
A component consists of three things:
* **A component class** that handles data and functionality. In the previous section, the product data and the `share()` method in the component class handle data and functionality, respectively.
* **An HTML template** that determines the UI. In the previous section, the product list's HTML template displays the name, description, and a "Share" button for each product.
* **Component-specific styles** that define the look and feel.
Though product list does not define any styles, this is where component CSS
resides.
<!--
<!--
### Class definition
Let's take a quick look a the product list component's class definition:
Let's take a quick look a the product list component's class definition:
1. In the `product-list` directory, open `product-list.component.ts`.
1. In the `product-list` directory, open `product-list.component.ts`.
1. Notice the `@Component` decorator. This provides metadata about the component, including its templates, styles, and a selector.
1. Notice the `@Component` decorator. This provides metadata about the component, including its templates, styles, and a selector.
* The `selector` is used to identify the component. The selector is the name you give the Angular component when it is rendered as an HTML element on the page. By convention, Angular component selectors begin with the prefix such as `app-`, followed by the component name.
* The `selector` is used to identify the component. The selector is the name you give the Angular component when it is rendered as an HTML element on the page. By convention, Angular component selectors begin with the prefix such as `app-`, followed by the component name.
* The template and style filename also are provided here. By convention each of the component's parts is in a separate file, all in the same directory and with the same prefix.
* The template and style filename also are provided here. By convention each of the component's parts is in a separate file, all in the same directory and with the same prefix.
1. The component definition also includes an exported class, which handles functionality for the component. This is where the product list data and `Share()` method are defined.
1. The component definition also includes an exported class, which handles functionality for the component. This is where the product list data and `Share()` method are defined.
### Composition
-->
An Angular application is composed of a tree of components, in which each Angular component has a specific purpose and responsibility.
An Angular application comprises a tree of components, in which each Angular component has a specific purpose and responsibility.
Currently, our app has three components:
Currently, the example app has three components:
<figure>
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/start/app-components.png" alt="Online store with three components">
</div>
</figure>
* `app-root` (orange box) is the application shell. This is the first component to load, and the parent of all other components. You can think of it as the base page.
* `app-root` (orange box) is the application shell. This is the first component to load and the parent of all other components. You can think of it as the base page.
* `app-top-bar` (blue background) is the store name and checkout button.
* `app-product-list` (purple box) is the product list that you modified in the previous section.
* `app-product-list` (purple box) is the product list that you modified in the previous section.
In the next section, you'll expand the app's capabilities by adding a new component for a product alert. You'll add it as a child of the product list component.
The next section expands the app's capabilities by adding a new component&mdash;a product alert&mdash;as a child of the product list component.
<div class="alert is-helpful">
Learn more: See [Introduction to Components](guide/architecture-components "Architecture > Introduction to Components") for more information about components and how they interact with templates.
For more information about components and how they interact with templates, see [Introduction to Components](guide/architecture-components "Architecture > Introduction to Components").
</div>
@ -204,72 +233,75 @@ Learn more: See [Introduction to Components](guide/architecture-components "Arch
{@a input}
## Input
Currently, the product list displays the name and description of each product.
You might have noticed that the product list component also defines a `products` property that contains imported data for each product. (See the `products` array in `products.ts`.)
Currently, the product list displays the name and description of each product.
The product list component also defines a `products` property that contains imported data for each product from the `products` array in `products.ts`.
We're going to create a new alert feature. The alert feature will take a product as an input. It will then check the product's price, and, if the price is greater than $700, it will display a "Notify Me" button that lets users sign up for notifications when the product goes on sale.
The next step is to create a new alert feature that takes a product as an input. The alert checks the product's price, and, if the price is greater than $700, displays a "Notify Me" button that lets users sign up for notifications when the product goes on sale.
1. Create a new product alerts component.
1. Create a new product alerts component.
1. Right click on the `app` folder and use the `Angular Generator` to generate a new component named `product-alerts`.
<figure>
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/start/generate-component.png" alt="StackBlitz command to generate component">
</div>
</figure>
The generator creates starter files for all three parts of the component:
The generator creates starter files for all three parts of the component:
* `product-alerts.component.ts`
* `product-alerts.component.html`
* `product-alerts.component.css`
1. Open `product-alerts.component.ts`.
<code-example header="src/app/product-alerts/product-alerts.component.ts" path="getting-started/src/app/product-alerts/product-alerts.component.1.ts" region="as-generated"></code-example>
<code-example header="src/app/product-alerts/product-alerts.component.ts" path="getting-started/src/app/product-alerts/product-alerts.component.1.ts" region="as-generated"></code-example>
1. Notice the `@Component` decorator. This indicates that the following class is a component. It provides metadata about the component, including its templates, styles, and a selector.
1. Notice the `@Component()` decorator. This indicates that the following class is a component. It provides metadata about the component, including its selector, templates, and styles.
* The `selector` is used to identify the component. The selector is the name you give the Angular component when it is rendered as an HTML element on the page. By convention, Angular component selectors begin with the prefix `app-`, followed by the component name.
* The `selector` identifies the component. The selector is the name you give the Angular component when it is rendered as an HTML element on the page. By convention, Angular component selectors begin with the prefix `app-`, followed by the component name.
* The template and style filenames. These reference the other two files generated for you.
* The template and style filenames reference the HTML and CSS files that StackBlitz generates.
1. The component definition also includes an exported class (`ProductAlertsComponent`), which handles functionality for the component.
1. The component definition also exports the class, `ProductAlertsComponent`, which handles functionality for the component.
1. Set up the new product alerts component to receive a product as input:
1. Import `Input` from `@angular/core`.
<code-example path="getting-started/src/app/product-alerts/product-alerts.component.1.ts" region="imports"></code-example>
<code-example path="getting-started/src/app/product-alerts/product-alerts.component.1.ts" region="imports" header="src/app/product-list/product-alerts.component.ts"></code-example>
1. In the `ProductAlertsComponent` class definition, define a property named `product` with an `@Input` decorator. The `@Input` decorator indicates that the property value will be passed in from the component's parent (in this case, the product list component).
1. In the `ProductAlertsComponent` class definition, define a property named `product` with an `@Input()` decorator. The `@Input()` decorator indicates that the property value passes in from the component's parent, the product list component.
<code-example path="getting-started/src/app/product-alerts/product-alerts.component.1.ts" region="input-decorator"></code-example>
<code-example path="getting-started/src/app/product-alerts/product-alerts.component.1.ts" region="input-decorator" header="src/app/product-list/product-alerts.component.ts"></code-example>
1. Define the view for the new product alert component.
1. Define the view for the new product alert component.
Open the `product-alerts.component.html` template and replace the placeholder paragraph with a "Notify Me" button that appears if the product price is over $700.
1. Open the `product-alerts.component.html` template and replace the placeholder paragraph with a "Notify Me" button that appears if the product price is over $700.
<code-example header="src/app/product-alerts/product-alerts.component.html" path="getting-started/src/app/product-alerts/product-alerts.component.1.html"></code-example>
1. Display the new product alert component as part of (a child of) the product list.
1. Display the new product alert component as a child of the product list.
1. Open `product-list.component.html`.
1. To include the new component, use its selector (`app-product-alert`) as you would an HTML element.
1. Pass the current product as input to the component using property binding.
1. To include the new component, use its selector, `app-product-alert`, as you would an HTML element.
1. Pass the current product as input to the component using property binding.
<code-example header="src/app/product-list/product-list.component.html" path="getting-started/src/app/product-list/product-list.component.5.html" region="app-product-alerts"></code-example>
The new product alert component takes a product as input from the product list. With that input, it shows or hides the "Notify Me" button, based on the price of the product. The Phone XL price is over $700, so the "Notify Me" button appears on that product.
The new product alert component takes a product as input from the product list. With that input, it shows or hides the "Notify Me" button, based on the price of the product. The Phone XL price is over $700, so the "Notify Me" button appears on that product.
<figure>
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/start/product-alert-button.png" alt="Product alert button added to products over $700">
</div>
</figure>
<div class="alert is-helpful">
Learn more: See [Component Interaction](guide/component-interaction "Components & Templates > Component Interaction") for more information about passing data from a parent to child component, intercepting and acting upon a value from the parent, and detecting and acting on changes to input property values.
See [Component Interaction](guide/component-interaction "Components & Templates > Component Interaction") for more information about passing data from a parent to child component, intercepting and acting upon a value from the parent, and detecting and acting on changes to input property values.
</div>
@ -277,42 +309,47 @@ Learn more: See [Component Interaction](guide/component-interaction "Components
{@a output}
## Output
The "Notify Me" button doesn't do anything yet. In this section, you'll set up the product alert component so that it emits an event up to the product list component when the user clicks "Notify Me". You'll define the notification behavior in the product list component.
To make the "Notify Me" button work, you need to configure two things:
- the product alert component to emit an event when the user clicks "Notify Me"
- the product list component to act on that event
1. Open `product-alerts.component.ts`.
1. Import `Output` and `EventEmitter` from `@angular/core`:
1. Import `Output` and `EventEmitter` from `@angular/core`:
<code-example header="src/app/product-alerts/product-alerts.component.ts" path="getting-started/src/app/product-alerts/product-alerts.component.ts" region="imports"></code-example>
1. In the component class, define a property named `notify` with an `@Output` decorator and an instance of event emitter. This makes it possible for the product alert component to emit an event when the value of the notify property changes.
1. In the component class, define a property named `notify` with an `@Output()` decorator and an instance of `EventEmitter()`. This allows the product alert component to emit an event when the value of the notify property changes.
<code-example path="getting-started/src/app/product-alerts/product-alerts.component.ts" region="input-output"></code-example>
<code-example path="getting-started/src/app/product-alerts/product-alerts.component.ts" header="src/app/product-alerts/product-alerts.component.ts" region="input-output"></code-example>
1. In the product alert template (`product-alerts.component.html`), update the "Notify Me" button with an event binding to call the `notify.emit()` method.
1. In the product alert template, `product-alerts.component.html`, update the "Notify Me" button with an event binding to call the `notify.emit()` method.
<code-example header="src/app/product-alerts/product-alerts.component.html" path="getting-started/src/app/product-alerts/product-alerts.component.html"></code-example>
1. Next, define the behavior that should happen when the button is clicked. Recall that it's the parent (product list component)&mdash;not the product alerts component&mdash;that's going to take the action. In the `product-list.component.ts` file, define an `onNotify()` method, similar to the `share()` method:
1. Next, define the behavior that should happen when the user clicks the button. Recall that it's the parent, product list component&mdash;not the product alerts component&mdash;that acts when the child raises the event. In `product-list.component.ts`, define an `onNotify()` method, similar to the `share()` method:
<code-example header="src/app/product-list/product-list.component.ts" path="getting-started/src/app/product-list/product-list.component.ts" region="on-notify"></code-example>
1. Finally, update the product list component to receive output from the product alerts component.
1. Finally, update the product list component to receive output from the product alerts component.
In `product-list.component.html`, bind the `app-product-alerts` component (which is what displays the "Notify Me" button) to the `onNotify()` method of the product list component.
In `product-list.component.html`, bind the `app-product-alerts` component (which is what displays the "Notify Me" button) to the `onNotify()` method of the product list component.
<code-example header="src/app/product-list/product-list.component.html" path="getting-started/src/app/product-list/product-list.component.6.html" region="on-notify"></code-example>
1. Try out the "Notify Me" button:
1. Try the "Notify Me" button:
<figure>
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/start/product-alert-notification.png" alt="Product alert notification confirmation dialog">
</div>
</figure>
<div class="alert is-helpful">
Learn more: See [Component Interaction](guide/component-interaction "Components & Templates > Component Interaction") for more information about listening for events from child components, reading child properties or invoking child methods, and using a service for bi-directional communication within the family.
See [Component Interaction](guide/component-interaction "Components & Templates > Component Interaction") for more information about listening for events from child components, reading child properties or invoking child methods, and using a service for bi-directional communication between components.
</div>
@ -322,11 +359,11 @@ Learn more: See [Component Interaction](guide/component-interaction "Components
Congratulations! You've completed your first Angular app!
You have a basic online store catalog, with a product list, "Share" button, and "Notify Me" button.
You've learned about the foundation of Angular: components and template syntax.
You've also learned how the component class and template interact, and how components communicate with each other.
You have a basic online store catalog with a product list, "Share" button, and "Notify Me" button.
You've learned about the foundation of Angular: components and template syntax.
You've also learned how the component class and template interact, and how components communicate with each other.
To continue exploring Angular, choose either of the following options:
* [Continue to the "Routing" section](start/routing "Getting Started: Routing") to create a product details page that can be accessed by clicking a product name and that has its own URL pattern.
* [Continue to the "Routing" section](start/routing "Getting Started: Routing") to create a product details page that can be accessed by clicking a product name and that has its own URL pattern.
* [Skip ahead to the "Deployment" section](start/deployment "Getting Started: Deployment") to move to local development, or deploy your app to Firebase or your own server.

View File

@ -54,8 +54,10 @@ The app is already set up to use the Angular router and to use routing to naviga
Notice that the URL in the preview window changes. The final segment is `products/1`.
<figure>
<img src="generated/images/guide/start/product-details-works.png" alt="Product details page with updated URL">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/start/product-details-works.png" alt="Product details page with updated URL">
</div>
</figure>
@ -90,8 +92,16 @@ The product details component handles the display of each product. The Angular R
<code-example path="getting-started/src/app/product-details/product-details.component.1.ts" region="get-product">
</code-example>
Angular calls `ngOnInit()` shortly after creating a component.
The route parameters correspond to the path variables defined in the route. The `productId` is provided from
the URL that was matched to the route. You use the `productId` to display the details for each unique product.
the URL that was matched to the route. You use the `productId` to display the details for each unique product.
<div class="alert is-helpful">
For more information on `ngOnInit()`, see [Lifecycle hooks](guide/lifecycle-hooks).
</div>
1. Update the template to display product details information inside an `*ngIf`.
@ -100,8 +110,10 @@ The product details component handles the display of each product. The Angular R
Now, when the user clicks on a name in the product list, the router navigates you to the distinct URL for the product, swaps out the product list component for the product details component, and displays the product details.
<figure>
<img src="generated/images/guide/start/product-details-routed.png" alt="Product details page with updated URL and full details displayed">
<figure class="lightbox">
<div class="card">
<img src="generated/images/guide/start/product-details-routed.png" alt="Product details page with updated URL and full details displayed">
</div>
</figure>

View File

@ -49,8 +49,10 @@ After completing all tutorial steps, the final app will look like this: <live-ex
Here's a visual idea of where this tutorial leads, beginning with the "Dashboard"
view and the most heroic heroes:
<figure>
<img src='generated/images/guide/toh/heroes-dashboard-1.png' alt="Output of heroes dashboard">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/toh/heroes-dashboard-1.png' alt="Output of heroes dashboard">
</div>
</figure>
You can click the two links above the dashboard ("Dashboard" and "Heroes")
@ -59,8 +61,10 @@ to navigate between this Dashboard view and a Heroes view.
If you click the dashboard hero "Magneta," the router opens a "Hero Details" view
where you can change the hero's name.
<figure>
<img src='generated/images/guide/toh/hero-details-1.png' alt="Details of hero in app">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/toh/hero-details-1.png' alt="Details of hero in app">
</div>
</figure>
Clicking the "Back" button returns you to the Dashboard.
@ -68,8 +72,10 @@ Links at the top take you to either of the main views.
If you click "Heroes," the app displays the "Heroes" master list view.
<figure>
<img src='generated/images/guide/toh/heroes-list-2.png' alt="Output of heroes list app">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/toh/heroes-list-2.png' alt="Output of heroes list app">
</div>
</figure>
When you click a different hero name, the read-only mini detail beneath the list reflects the new choice.
@ -79,12 +85,16 @@ editable details of the selected hero.
The following diagram captures all of the navigation options.
<figure>
<img src='generated/images/guide/toh/nav-diagram.png' alt="View navigations">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/toh/nav-diagram.png' alt="View navigations">
</div>
</figure>
Here's the app in action:
<figure>
<img src='generated/images/guide/toh/toh-anim.gif' alt="Tour of Heroes in Action">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/toh/toh-anim.gif' alt="Tour of Heroes in Action">
</div>
</figure>

View File

@ -196,10 +196,10 @@ It's difficult to identify the _selected hero_ in the list when all `<li>` eleme
If the user clicks "Magneta", that hero should render with a distinctive but subtle background color like this:
<figure>
<img src='generated/images/guide/toh/heroes-list-selected.png' alt="Selected hero">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/toh/heroes-list-selected.png' alt="Selected hero">
</div>
</figure>
That _selected hero_ coloring is the work of the `.selected` CSS class in the [styles you added earlier](#styles).

View File

@ -9,10 +9,10 @@ There are new requirements for the Tour of Heroes app:
When youre done, users will be able to navigate the app like this:
<figure>
<img src='generated/images/guide/toh/nav-diagram.png' alt="View navigations">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/toh/nav-diagram.png' alt="View navigations">
</div>
</figure>
## Add the `AppRoutingModule`

View File

@ -513,8 +513,10 @@ That's the job of the [`AsyncPipe`](#asyncpipe) in the template.
Run the app again. In the *Dashboard*, enter some text in the search box.
If you enter characters that match any existing hero names, you'll see something like this.
<figure>
<img src='generated/images/guide/toh/toh-hero-search.png' alt="Hero Search Component">
<figure class="lightbox">
<div class="card">
<img src='generated/images/guide/toh/toh-hero-search.png' alt="Hero Search Component">
</div>
</figure>
## Final code review

View File

@ -19,11 +19,11 @@
"build-local": "yarn ~~build",
"prebuild-local-ci": "yarn setup-local --no-build-packages",
"build-local-ci": "yarn ~~build --progress=false",
"prebuild-with-ivy": "yarn setup-local && node scripts/switch-to-ivy",
"build-with-ivy": "yarn ~~build",
"prebuild-with-ivy-ci": "yarn setup-local --no-build-packages && node scripts/switch-to-ivy",
"build-with-ivy-ci": "yarn ~~build --progress=false",
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 6734efe52",
"prebuild-local-with-ivy": "yarn setup-local && node scripts/switch-to-ivy",
"build-local-with-ivy": "yarn ~~build",
"prebuild-local-with-ivy-ci": "yarn setup-local --no-build-packages && node scripts/switch-to-ivy",
"build-local-with-ivy-ci": "yarn ~~build --progress=false",
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 5bf754195",
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
"test": "yarn check-env && ng test",
"pree2e": "yarn check-env && yarn update-webdriver",
@ -115,7 +115,7 @@
"chalk": "^2.1.0",
"chrome-launcher": "^0.10.7",
"cjson": "^0.5.0",
"codelyzer": "^5.0.0",
"codelyzer": "^5.1.1",
"cross-spawn": "^5.1.0",
"css-selector-parser": "^1.3.0",
"dgeni": "^0.4.11",

View File

@ -2,11 +2,8 @@
"aio": {
"master": {
"uncompressed": {
"runtime-es5": 3042,
"runtime-es2015": 3048,
"main-es5": 511052,
"main-es2015": 450562,
"polyfills-es5": 129161,
"polyfills-es2015": 53295
}
}
@ -14,11 +11,8 @@
"aio-local": {
"master": {
"uncompressed": {
"runtime-es5": 3042,
"runtime-es2015": 3048,
"main-es5": 499085,
"main-es2015": 438296,
"polyfills-es5": 129161,
"polyfills-es2015": 53295
}
}
@ -26,11 +20,8 @@
"aio-local-ivy": {
"master": {
"uncompressed": {
"runtime-es5": 2932,
"runtime-es2015": 2938,
"main-es5": 555102,
"main-es2015": 572938,
"polyfills-es5": 129161,
"polyfills-es2015": 53295
}
}

View File

@ -464,14 +464,14 @@ describe('AppComponent', () => {
let scrollSpy: jasmine.Spy;
let scrollToTopSpy: jasmine.Spy;
let scrollAfterRenderSpy: jasmine.Spy;
let removeStoredScrollPositionSpy: jasmine.Spy;
let removeStoredScrollInfoSpy: jasmine.Spy;
beforeEach(() => {
scrollService = fixture.debugElement.injector.get<ScrollService>(ScrollService);
scrollSpy = spyOn(scrollService, 'scroll');
scrollToTopSpy = spyOn(scrollService, 'scrollToTop');
scrollAfterRenderSpy = spyOn(scrollService, 'scrollAfterRender');
removeStoredScrollPositionSpy = spyOn(scrollService, 'removeStoredScrollPosition');
removeStoredScrollInfoSpy = spyOn(scrollService, 'removeStoredScrollInfo');
});
it('should not scroll immediately when the docId (path) changes', () => {
@ -516,9 +516,9 @@ describe('AppComponent', () => {
expect(scrollSpy).toHaveBeenCalledTimes(1);
});
it('should call `removeStoredScrollPosition` when call `onDocRemoved` directly', () => {
it('should call `removeStoredScrollInfo` when call `onDocRemoved` directly', () => {
component.onDocRemoved();
expect(removeStoredScrollPositionSpy).toHaveBeenCalled();
expect(removeStoredScrollInfoSpy).toHaveBeenCalled();
});
it('should call `scrollAfterRender` when call `onDocInserted` directly', (() => {

View File

@ -202,7 +202,7 @@ export class AppComponent implements OnInit {
}
onDocRemoved() {
this.scrollService.removeStoredScrollPosition();
this.scrollService.removeStoredScrollInfo();
}
onDocInserted() {

View File

@ -7,10 +7,11 @@ import { LocationService } from 'app/shared/location.service';
selector: `aio-contributor-list`,
template: `
<div class="flex-center group-buttons">
<a *ngFor="let name of groupNames"
[class.selected]="name == selectedGroup.name"
class="button mat-button filter-button"
(click)="selectGroup(name)">{{name}}</a>
<a *ngFor="let name of groupNames"
[class.selected]="name == selectedGroup.name"
class="button mat-button filter-button"
(click)="selectGroup(name)"
(keyup.enter)="selectGroup(name)">{{name}}</a>
</div>
<section *ngIf="selectedGroup" class="grid-fluid">
<div class="contributor-group">

View File

@ -8,7 +8,7 @@ import { CONTENT_URL_PREFIX } from 'app/documents/document.service';
template: `
<div [ngClass]="{ 'flipped': person.isFlipped }" class="contributor-card">
<div class="card-front" (click)="flipCard(person)">
<div class="card-front" (click)="flipCard(person)" (keyup.enter)="flipCard(person)">
<h3>{{person.name}}</h3>
<div class="contributor-image" [style.background-image]="'url('+pictureBase+(person.picture || noPicture)+')'">
@ -28,7 +28,7 @@ import { CONTENT_URL_PREFIX } from 'app/documents/document.service';
</div>
</div>
<div class="card-back" *ngIf="person.isFlipped" (click)="flipCard(person)">
<div class="card-back" *ngIf="person.isFlipped" (click)="flipCard(person)" (keyup.enter)="flipCard(person)">
<h3>{{person.name}}</h3>
<p class="contributor-bio">{{person.bio}}</p>
</div>

View File

@ -4,6 +4,7 @@ import { PlatformLocation } from '@angular/common';
import { Category } from './resource.model';
import { ResourceService } from './resource.service';
/* tslint:disable:template-accessibility-elements-content */
@Component({
selector: 'aio-resource-list',
templateUrl: 'resource-list.component.html'

View File

@ -1,4 +1,4 @@
<span class="content" (click)="contentClick()">
<span class="content" (click)="contentClick()" (keyup.enter)="contentClick()">
<ng-content></ng-content>
</span>

View File

@ -296,11 +296,11 @@ describe('LocationService', () => {
it('should do a "full page navigation" and remove the stored scroll position when navigating to ' +
'internal URLs only if a ServiceWorker update has been activated', () => {
const goExternalSpy = spyOn(service, 'goExternal');
const removeStoredScrollPositionSpy = spyOn(scrollService, 'removeStoredScrollPosition');
const removeStoredScrollInfoSpy = spyOn(scrollService, 'removeStoredScrollInfo');
// Internal URL - No ServiceWorker update
service.go('some-internal-url');
expect(removeStoredScrollPositionSpy).not.toHaveBeenCalled();
expect(removeStoredScrollInfoSpy).not.toHaveBeenCalled();
expect(goExternalSpy).not.toHaveBeenCalled();
expect(location.path(true)).toEqual('some-internal-url');
@ -308,24 +308,24 @@ describe('LocationService', () => {
swUpdates.updateActivated.next('foo');
service.go('other-internal-url');
expect(goExternalSpy).toHaveBeenCalledWith('other-internal-url');
expect(removeStoredScrollPositionSpy).toHaveBeenCalled();
expect(removeStoredScrollInfoSpy).toHaveBeenCalled();
});
it('should not remove the stored scroll position when navigating to external URLs', () => {
const removeStoredScrollPositionSpy = spyOn(scrollService, 'removeStoredScrollPosition');
const removeStoredScrollInfoSpy = spyOn(scrollService, 'removeStoredScrollInfo');
const goExternalSpy = spyOn(service, 'goExternal');
const externalUrl = 'http://some/far/away/land';
const otherExternalUrl = 'http://some/far/far/away/land';
// External URL - No ServiceWorker update
service.go(externalUrl);
expect(removeStoredScrollPositionSpy).not.toHaveBeenCalled();
expect(removeStoredScrollInfoSpy).not.toHaveBeenCalled();
expect(goExternalSpy).toHaveBeenCalledWith(externalUrl);
// External URL - ServiceWorker update
swUpdates.updateActivated.next('foo');
service.go(otherExternalUrl);
expect(removeStoredScrollPositionSpy).not.toHaveBeenCalled();
expect(removeStoredScrollInfoSpy).not.toHaveBeenCalled();
expect(goExternalSpy).toHaveBeenCalledWith(otherExternalUrl);
});
@ -633,7 +633,7 @@ class MockSwUpdatesService {
}
class MockScrollService {
removeStoredScrollPosition() { }
removeStoredScrollInfo() { }
}
class TestGaService {

View File

@ -49,7 +49,7 @@ export class LocationService {
} else if (this.swUpdateActivated) {
// (Do a "full page navigation" if a ServiceWorker update has been activated)
// We need to remove stored Position in order to be sure to scroll to the Top position
this.scrollService.removeStoredScrollPosition();
this.scrollService.removeStoredScrollInfo();
this.goExternal(url);
} else {
this.location.go(url);

View File

@ -58,6 +58,10 @@ export class ScrollService implements OnDestroy {
.pipe(debounceTime(250), takeUntil(this.onDestroy))
.subscribe(() => this.updateScrollPositionInHistory());
fromEvent(window, 'beforeunload')
.pipe(takeUntil(this.onDestroy))
.subscribe(() => this.updateScrollLocationHref());
// Change scroll restoration strategy to `manual` if it's supported
if (this.supportManualScrollRestoration) {
history.scrollRestoration = 'manual';
@ -70,13 +74,18 @@ export class ScrollService implements OnDestroy {
} else {
// Navigating with the forward/back button, we have to remove the position from the
// session storage in order to avoid a race-condition.
this.removeStoredScrollPosition();
this.removeStoredScrollInfo();
// The `popstate` event is always triggered by a browser action such as clicking the
// forward/back button. It can be followed by a `hashchange` event.
this.poppedStateScrollPosition = event.state ? event.state.scrollPosition : null;
}
});
}
// If this was not a reload, discard the stored scroll info.
if (window.location.href !== this.getStoredScrollLocationHref()) {
this.removeStoredScrollInfo();
}
}
ngOnDestroy() {
@ -170,6 +179,10 @@ export class ScrollService implements OnDestroy {
}
}
updateScrollLocationHref(): void {
window.sessionStorage.setItem('scrollLocationHref', window.location.href);
}
/**
* Update the state with scroll position into history.
*/
@ -181,6 +194,11 @@ export class ScrollService implements OnDestroy {
}
}
getStoredScrollLocationHref(): string | null {
const href = window.sessionStorage.getItem('scrollLocationHref');
return href || null;
}
getStoredScrollPosition(): ScrollPosition | null {
const position = window.sessionStorage.getItem('scrollPosition');
if (!position) { return null; }
@ -189,7 +207,8 @@ export class ScrollService implements OnDestroy {
return [+x, +y];
}
removeStoredScrollPosition() {
removeStoredScrollInfo() {
window.sessionStorage.removeItem('scrollLocationHref');
window.sessionStorage.removeItem('scrollPosition');
}

View File

@ -65,10 +65,10 @@ export class TocService {
div.innerHTML = heading.innerHTML;
// Remove any `.github-links` or `.header-link` elements (along with their content).
div.querySelectorAll('.github-links, .header-link').forEach(removeNode);
querySelectorAll(div, '.github-links, .header-link').forEach(removeNode);
// Remove any remaining `a` elements (but keep their content).
div.querySelectorAll('a').forEach(anchorLink => {
querySelectorAll(div, 'a').forEach(anchorLink => {
// We want to keep the content of this anchor, so move it into its parent.
const parent = anchorLink.parentNode!;
while (anchorLink.childNodes.length) {
@ -88,10 +88,11 @@ export class TocService {
}
private findTocHeadings(docElement: Element): HTMLHeadingElement[] {
const headings = docElement.querySelectorAll('h1,h2,h3');
// const headings = querySelectorAll(docElement, 'h1,h2,h3');
const headings = querySelectorAll<HTMLHeadingElement>(docElement, 'h1,h2,h3');
const skipNoTocHeadings = (heading: HTMLHeadingElement) => !/(?:no-toc|notoc)/i.test(heading.className);
return Array.prototype.filter.call(headings, skipNoTocHeadings);
return headings.filter(skipNoTocHeadings);
}
private resetScrollSpyInfo() {
@ -127,6 +128,16 @@ export class TocService {
}
// Helpers
function querySelectorAll<K extends keyof HTMLElementTagNameMap>(parent: Element, selector: K): HTMLElementTagNameMap[K][];
function querySelectorAll<K extends keyof SVGElementTagNameMap>(parent: Element, selector: K): SVGElementTagNameMap[K][];
function querySelectorAll<E extends Element = Element>(parent: Element, selector: string): E[];
function querySelectorAll(parent: Element, selector: string) {
// Wrap the `NodeList` as a regular `Array` to have access to array methods.
// NOTE: IE11 does not even support some methods of `NodeList`, such as
// [NodeList#forEach()](https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach).
return Array.from(parent.querySelectorAll(selector));
}
function removeNode(node: Node): void {
if (node.parentNode !== null) {
// We cannot use `Node.remove()` because of IE11.

View File

@ -49,8 +49,8 @@ aio-shell.page-resources mat-toolbar.mat-toolbar {
aio-shell.folder-api mat-toolbar.mat-toolbar,
aio-shell.folder-cli mat-toolbar.mat-toolbar,
aio-shell.folder-docs mat-toolbar.mat-toolbar,
aio-shell.folder-getting-started mat-toolbar.mat-toolbar,
aio-shell.folder-guide mat-toolbar.mat-toolbar,
aio-shell.folder-start mat-toolbar.mat-toolbar,
aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
@media (min-width: 992px) {
.hamburger.mat-button {

View File

@ -18,6 +18,7 @@
@media (max-width: 1300px) {
max-width: 100%;
height: auto;
margin: auto;
}
@media (max-width: 600px) {
@ -40,7 +41,8 @@
border: 1px solid $lightgray;
padding: 32px;
box-sizing: border-box;
display: inline-block;
display: flex;
justify-content: center;
box-shadow: 2px 2px 5px 0 rgba(0, 0, 0, .2);
margin: 16px 0;
@ -56,6 +58,11 @@
border-radius: 4px;
padding: 8px;
background-color: $white;
img {
max-width: 100%;
height: auto;
}
}
}
}

View File

@ -100,7 +100,7 @@ describe('site App', function() {
it('should scroll to the top when navigating to another page', () => {
page.navigateTo('guide/security');
page.scrollToBottom();
page.scrollTo('bottom');
expect(page.getScrollTop()).toBeGreaterThan(0);
page.click(page.getNavItem(/api/i));
@ -111,7 +111,7 @@ describe('site App', function() {
it('should scroll to the top when navigating to the same page', () => {
page.navigateTo('guide/security');
page.scrollToBottom();
page.scrollTo('bottom');
expect(page.getScrollTop()).toBeGreaterThan(0);
page.click(page.getNavItem(/security/i));

View File

@ -59,8 +59,13 @@ export class SitePage {
return browser.executeScript('return window.pageYOffset');
}
scrollToBottom() {
return browser.executeScript('window.scrollTo(0, document.body.scrollHeight)');
scrollTo(y: 'top' | 'bottom' | number) {
const yExpr = (y === 'top') ? '0' : (y === 'bottom') ? 'document.body.scrollHeight' : y;
return browser.executeScript(`
window.scrollTo(0, ${yExpr});
window.dispatchEvent(new Event('scroll'));
`);
}
click(elementFinder: ElementFinder) {

View File

@ -0,0 +1,90 @@
import { browser } from 'protractor';
import { SitePage } from './app.po';
describe('site auto-scrolling', () => {
let page: SitePage;
// Helpers
const scrollAndWait = async (y: Parameters<SitePage['scrollTo']>[0] = 'bottom') => {
await page.scrollTo(y);
await browser.sleep(500); // Scroll position is stored every 250ms for performance reasons.
};
beforeEach(async () => {
page = new SitePage();
await page.navigateTo('');
});
it('should be initially scrolled to top', async () => {
expect(await page.getScrollTop()).toBe(0);
});
it('should scroll to top when navigating to a different page', async () => {
await scrollAndWait();
expect(await page.getScrollTop()).not.toBe(0);
await page.navigateTo('docs');
expect(await page.getScrollTop()).toBe(0);
});
it('should retain the scroll position on reload', async () => {
await scrollAndWait();
expect(await page.getScrollTop()).not.toBe(0);
await browser.refresh();
expect(await page.getScrollTop()).not.toBe(0);
});
it('should scroll to top when navigating to a different page via a link', async () => {
await scrollAndWait();
expect(await page.getScrollTop()).not.toBe(0);
await page.docsMenuLink.click();
expect(await page.getScrollTop()).toBe(0);
});
it('should scroll to top when navigating to the same page via a link', async () => {
await scrollAndWait();
expect(await page.getScrollTop()).not.toBe(0);
await page.homeLink.click();
expect(await page.getScrollTop()).toBe(0);
});
// TODO: Find a way to accurately emulate clicking the browser back/forward button. Apparently,
// both `browser.navigate().back()` and `browser.executeScript('history.back()')` cause a full
// page load, which behaves differently than clicking the browser back button (and triggering a
// `popstate` event instead of a navigation). Same for `forward()`.
xit('should retain the scroll position when navigating back/forward', async () => {
await scrollAndWait(100);
expect(await page.getScrollTop()).toBeCloseTo(100, -1);
await page.navigateTo('docs');
await scrollAndWait(50);
expect(await page.getScrollTop()).toBeCloseTo(50, -1);
await page.navigateTo('features');
await scrollAndWait(75);
expect(await page.getScrollTop()).toBeCloseTo(75, -1);
// Go back.
await browser.navigate().back();
expect(await page.locationPath()).toBe('/docs');
expect(await page.getScrollTop()).toBeCloseTo(50, -1);
// Go back.
await browser.navigate().back();
expect(await page.locationPath()).toBe('/');
expect(await page.getScrollTop()).toBeCloseTo(100, -1);
// Go forward.
await browser.navigate().forward();
expect(await page.locationPath()).toBe('/docs');
expect(await page.getScrollTop()).toBeCloseTo(50, -1);
// Go forward.
await browser.navigate().forward();
expect(await page.locationPath()).toBe('/features');
expect(await page.getScrollTop()).toBeCloseTo(75, -1);
});
});

View File

@ -21,16 +21,6 @@
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/** IE10 and IE11 requires the following for the Reflect API. */
/**
* DO NOT REMOVE
* By default, Reflect polyfills are auto-included by the CLI and
* are required for JIT compilation. StackBlitz examples are
* compiled using JIT.
*/
import 'core-js/es6/reflect';
import 'core-js/es7/reflect';
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
@ -65,8 +55,7 @@ import 'core-js/es7/reflect';
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
import 'zone.js/dist/zone-patch-canvas';
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS

View File

@ -33,7 +33,7 @@
"@nguniversal/express-engine": "^8.0.0-rc.1",
"@nguniversal/module-map-ngfactory-loader": "^8.0.0-rc.1",
"angular": "1.7.8",
"angular-in-memory-web-api": "github:brandonroberts/in-memory-web-api-bazel#50a34d8",
"angular-in-memory-web-api": "^0.9.0",
"angular-route": "1.7.8",
"core-js": "^2.5.4",
"express": "^4.14.1",
@ -69,7 +69,7 @@
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^0.2.2",
"karma-jasmine-html-reporter": "^1.4.2",
"lite-server": "^2.2.2",
"lodash": "^4.16.2",
"protractor": "~5.4.0",

View File

@ -795,11 +795,10 @@ amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
"angular-in-memory-web-api@github:brandonroberts/in-memory-web-api-bazel#50a34d8":
version "0.8.0"
resolved "https://codeload.github.com/brandonroberts/in-memory-web-api-bazel/tar.gz/50a34d84b627ec88816242dec77603d6dcb9c880"
dependencies:
tslib "^1.9.0"
angular-in-memory-web-api@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/angular-in-memory-web-api/-/angular-in-memory-web-api-0.9.0.tgz#6c98d9494fadc6b98f54e68376a1998ccfff04bc"
integrity sha512-//PiJ5qb1+Yf/N7270ioQqR2laf4/Irjavg+M+WEn8y4At9LUoYgbQ5HVwvM5xUTlVlL0XkbJRLxREcGGNdIEw==
angular-route@1.7.8:
version "1.7.8"
@ -4430,15 +4429,10 @@ karma-coverage-istanbul-reporter@~2.0.1:
istanbul-api "^2.0.5"
minimatch "^3.0.4"
karma-jasmine-html-reporter@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz#48a8e5ef18807617ee2b5e33c1194c35b439524c"
dependencies:
karma-jasmine "^1.0.2"
karma-jasmine@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e3733811b810acf"
karma-jasmine-html-reporter@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.4.2.tgz#16d100fd701271192d27fd28ddc90b710ad36fff"
integrity sha512-7g0gPj8+9JepCNJR9WjDyQ2RkZ375jpdurYQyAYv8PorUCadepl8vrD6LmMqOGcM17cnrynBawQYZHaumgDjBw==
karma-jasmine@~2.0.1:
version "2.0.1"

View File

@ -29,6 +29,7 @@ module.exports =
.processor(require('./processors/processClassLikeMembers'))
.processor(require('./processors/markBarredODocsAsPrivate'))
.processor(require('./processors/filterPrivateDocs'))
.processor(require('./processors/filterMembers'))
.processor(require('./processors/computeSearchTitle'))
.processor(require('./processors/simplifyMemberAnchors'))
.processor(require('./processors/computeStability'))
@ -176,6 +177,12 @@ module.exports =
filterContainedDocs.docTypes = API_CONTAINED_DOC_TYPES;
})
.config(function(filterMembers) {
filterMembers.notAllowedPatterns.push(
/^ng[A-Z].*Def$/
);
})
.config(function(computePathsProcessor, EXPORT_DOC_TYPES, generateApiListDoc) {

View File

@ -0,0 +1,21 @@
/**
* Filter out members (i.e. static and instance properties and methods) that match specific
* patterns. Patterns can be added (as `RegExp`s) to the `notAllowedPatterns` array.
*
* (By default, no members are excluded.)
*/
module.exports = function filterMembers() {
return {
$runAfter: ['processing-docs'],
$runBefore: ['docs-processed'],
notAllowedPatterns: [],
$process(docs) {
const isAllowed = ({name}) => !this.notAllowedPatterns.some(re => re.test(name));
docs.forEach(doc => {
if (doc.statics) doc.statics = doc.statics.filter(isAllowed);
if (doc.members) doc.members = doc.members.filter(isAllowed);
});
},
};
};

View File

@ -0,0 +1,102 @@
const processorFactory = require('./filterMembers');
const testPackage = require('../../helpers/test-package');
const Dgeni = require('dgeni');
describe('filterMembers processor', () => {
it('should be available on the injector', () => {
const dgeni = new Dgeni([testPackage('angular-api-package')]);
const injector = dgeni.configureInjector();
const processor = injector.get('filterMembers');
expect(processor.$process).toBeDefined();
expect(processor.$runAfter).toEqual(['processing-docs']);
expect(processor.$runBefore).toEqual(['docs-processed']);
});
it('should remove members that match one of the not allowed patterns', () => {
const processor = processorFactory();
processor.notAllowedPatterns = [/^foo/, /bar$/];
const docs = [
// Doc without members.
{ },
// Doc with static members only.
{
statics: [
{ name: 'fooStatic' }, // Will be removed.
{ name: 'FOOStatic' },
{ name: 'barStatic' },
{ name: 'statiCbar' }, // Will be removed.
],
},
// Doc with instance members only.
{
members: [
{ name: 'fooInstance' }, // Will be removed.
{ name: 'FOOInstance' },
{ name: 'barInstance' },
{ name: 'instancEbar' }, // Will be removed.
],
},
// Doc with both static and instance members.
{
statics: [
{ name: 'fooStatic' }, // Will be removed.
{ name: 'FOOStatic' },
{ name: 'barStatic' },
{ name: 'statiCbar' }, // Will be removed.
],
members: [
{ name: 'fooInstance' }, // Will be removed.
{ name: 'FOOInstance' },
{ name: 'barInstance' },
{ name: 'instancEbar' }, // Will be removed.
],
},
];
processor.$process(docs);
expect(docs).toEqual([
{ },
{
statics: [ { name: 'FOOStatic' }, { name: 'barStatic' } ],
},
{
members: [ { name: 'FOOInstance' }, { name: 'barInstance' } ],
},
{
statics: [ { name: 'FOOStatic' }, { name: 'barStatic' } ],
members: [ { name: 'FOOInstance' }, { name: 'barInstance' } ],
},
]);
});
it('should remove no members by default', () => {
const processor = processorFactory();
const expectedDocs = [
{
statics: [
{ name: '' },
{ name: 'foo' },
{ name: '__bar' },
{ name: 'ngBazDef' },
],
members: [
{ name: '' },
{ name: 'foo' },
{ name: '__bar' },
{ name: 'ngBazDef' },
],
},
];
const actualDocs = JSON.parse(JSON.stringify(expectedDocs));
processor.$process(actualDocs);
expect(processor.notAllowedPatterns).toEqual([]);
expect(actualDocs).toEqual(expectedDocs);
});
});

View File

@ -96,6 +96,16 @@
"ban-keywords",
"check-format",
"require-const-for-all-caps"
]
],
"template-accessibility-alt-text": true,
"template-accessibility-elements-content": true,
"template-accessibility-label-for": true,
"template-accessibility-tabindex-no-positive": true,
"template-accessibility-table-scope": true,
"template-accessibility-valid-aria": true,
"template-click-events-have-key-events": true,
"template-mouse-events-have-key-events": true,
"template-no-autofocus": true,
"template-no-distracting-elements": true
}
}

View File

@ -938,10 +938,10 @@ anymatch@^3.0.1:
normalize-path "^3.0.0"
picomatch "^2.0.4"
app-root-path@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.1.0.tgz#98bf6599327ecea199309866e8140368fd2e646a"
integrity sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=
app-root-path@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a"
integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==
append-transform@^1.0.0:
version "1.0.0"
@ -2179,12 +2179,12 @@ code-point-at@^1.0.0:
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
codelyzer@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-5.0.0.tgz#e4032efb23a7c5d4bcfe7321fc1789490c679837"
integrity sha512-Bif70XYt8NFf/Q9GPTxmC86OsBRfQZq1dBjdruJ5kZhJ8/jKhJL6MvCLKnYtSOG6Rhiv/44DU0cHk6GYthjy8Q==
codelyzer@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-5.1.1.tgz#a599fa8c2a5847f553a792b934e493d1506a4a62"
integrity sha512-t8ZLSZBUjVFOJVk4jASLgmTdKWK/0ZsQCnPXy6PXw1LWOOormQOVnyy4OYoiZ6rAWTrz60Obx+zA2t8xY53QzQ==
dependencies:
app-root-path "^2.1.0"
app-root-path "^2.2.1"
aria-query "^3.0.0"
axobject-query "^2.0.2"
css-selector-tokenizer "^0.7.1"

View File

@ -26,7 +26,7 @@ following products on your development machine:
* [Yarn](https://yarnpkg.com) (version specified in the engines field of [`package.json`](../package.json)) which is used to install dependencies.
* [Java Development Kit](http://www.oracle.com/technetwork/es/java/javase/downloads/index.html) which is used
* [Java Development Kit](https://www.oracle.com/technetwork/java/javase/downloads/index.html) which is used
to execute the selenium standalone server for e2e testing.
## Getting the Sources
@ -97,8 +97,8 @@ A better way is to set up your IDE to format the changed file on each file save.
### VS Code
1. Install [Clang-Format](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format) extension for VS Code.
It will automatically pick up the settings from Angular's [settings.json](../.vscode/settings.json).
2. It will automatically pick up the settings from `.vscode/settings.json`.
If you haven't already, create a `settings.json` file by following the instructions [here](../.vscode/README.md).
### WebStorm / IntelliJ
1. Install the [ClangFormatIJ](https://plugins.jetbrains.com/plugin/8396-clangformatij) plugin
@ -154,12 +154,29 @@ The script will publish the build snapshot to a branch with the same name as you
and create it if it doesn't exist.
## Bazel support
### VS Code
### IDEs
#### VS Code
1. Install [Bazel](https://marketplace.visualstudio.com/items?itemName=DevonDCarew.bazel-code) extension for VS Code.
### WebStorm / IntelliJ
#### WebStorm / IntelliJ
1. Install the [Bazel](https://plugins.jetbrains.com/plugin/8609-bazel) plugin
1. You can find the settings under `Preferences->Other Settings->Bazel Settings`
It will automatically recognize `*.bazel` and `*.bzl` files.
### Remote Build Execution and Remote Caching
Bazel builds in the Angular repository use a shared http cache. When a build occurs a hash of the inputs is computed
and checked against available outputs in the shared http cache. If an output is found, it is used as the output for the
build action rather than performing the build locally.
> Remote Build Execution and uploading to the Remote Cache requires authentication as a google.com or angular.io account.
### --config=remote-http-caching flag
The `--config=remote-http-caching` flag can be added to enable uploading of build results to the shared http cache. This flag
can be added to the `.bazelrc.user` file using the script at `scripts/local-dev/setup-rbe.sh`.
### --config=remote flag
The `--config=remote` flag can be added to enable remote execution of builds. This flag can be added to
the `.bazelrc.user` file using the script at `scripts/local-dev/setup-rbe.sh`.

View File

@ -6,6 +6,10 @@ package(default_visibility = ["//modules/playground:__subpackages__"])
ng_module(
name = "relative_assets",
srcs = glob(["**/*.ts"]),
assets = [
"app/style.css",
"app/tpl.html",
],
# This example demonstrates how external resources can be loaded relatively, so we
# need to disable resource inlining.
inline_resources = False,

View File

@ -6,7 +6,10 @@ package(default_visibility = ["//modules/playground:__subpackages__"])
ng_module(
name = "todo",
srcs = glob(["**/*.ts"]),
assets = ["todo.html"],
assets = [
"todo.html",
"css/base.css",
],
tsconfig = "//modules/playground:tsconfig-build.json",
# TODO: FW-1004 Type checking is currently not complete.
type_check = False,

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "8.2.6",
"version": "8.2.12",
"private": true,
"description": "Angular - a web framework for modern web apps",
"homepage": "https://github.com/angular/angular",
@ -162,8 +162,8 @@
"vrsource-tslint-rules": "5.1.1",
"webpack": "1.12.9"
},
"// 4": "natives is needed for gulp to work with node >= 10.13, see #28213",
"// 4": "Overwrite graceful-fs to a version that does not rely on the 'natives' package. This fixes gulp for >= 10.13, more information: #28213",
"resolutions": {
"natives": "1.1.6"
"**/graceful-fs": "4.2.2"
}
}

View File

@ -707,7 +707,7 @@ export function group(
*
* ```typescript
* sequence([
* style({ opacity: 0 })),
* style({ opacity: 0 }),
* animate("1s", style({ opacity: 1 }))
* ])
* ```

View File

@ -372,7 +372,8 @@ def ngc_compile_action(
node_opts,
locale = None,
i18n_args = [],
dts_bundles_out = None):
dts_bundles_out = None,
compile_mode = "prodmode"):
"""Helper function to create the ngc action.
This is exposed for google3 to wire up i18n replay rules, and is not intended
@ -397,13 +398,13 @@ def ngc_compile_action(
is_legacy_ngc = _is_legacy_ngc(ctx)
mnemonic = "AngularTemplateCompile"
progress_message = "Compiling Angular templates (%s) %s" % (_compiler_name(ctx), label)
progress_message = "Compiling Angular templates (%s - %s) %s" % (_compiler_name(ctx), compile_mode, label)
if locale:
mnemonic = "AngularI18NMerging"
supports_workers = "0"
progress_message = ("Recompiling Angular templates (ngc) %s for locale %s" %
(label, locale))
progress_message = ("Recompiling Angular templates (ngc - %s) %s for locale %s" %
(compile_mode, label, locale))
else:
supports_workers = str(int(ctx.attr._supports_workers))
@ -463,7 +464,7 @@ def ngc_compile_action(
dts_entry_points.append(_R3_SYMBOLS_DTS_FILE)
ctx.actions.run(
progress_message = "Bundling DTS %s" % str(ctx.label),
progress_message = "Bundling DTS (%s) %s" % (compile_mode, str(ctx.label)),
mnemonic = "APIExtractor",
executable = ctx.executable.api_extractor,
inputs = filter_inputs,
@ -495,7 +496,15 @@ def _filter_ts_inputs(all_inputs):
if f.path.endswith(".js") or f.path.endswith(".ts") or f.path.endswith(".json")
]
def _compile_action(ctx, inputs, outputs, dts_bundles_out, messages_out, tsconfig_file, node_opts):
def _compile_action(
ctx,
inputs,
outputs,
dts_bundles_out,
messages_out,
tsconfig_file,
node_opts,
compile_mode):
# Give the Angular compiler all the user-listed assets
file_inputs = list(ctx.files.assets)
@ -533,16 +542,16 @@ def _compile_action(ctx, inputs, outputs, dts_bundles_out, messages_out, tsconfi
],
)
return ngc_compile_action(ctx, ctx.label, action_inputs, outputs, messages_out, tsconfig_file, node_opts, None, [], dts_bundles_out)
return ngc_compile_action(ctx, ctx.label, action_inputs, outputs, messages_out, tsconfig_file, node_opts, None, [], dts_bundles_out, compile_mode)
def _prodmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
outs = _expected_outs(ctx)
return _compile_action(ctx, inputs, outputs + outs.closure_js, None, outs.i18n_messages, tsconfig_file, node_opts)
return _compile_action(ctx, inputs, outputs + outs.closure_js, None, outs.i18n_messages, tsconfig_file, node_opts, "prodmode")
def _devmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
outs = _expected_outs(ctx)
compile_action_outputs = outputs + outs.devmode_js + outs.declarations + outs.summaries + outs.metadata
_compile_action(ctx, inputs, compile_action_outputs, outs.dts_bundles, None, tsconfig_file, node_opts)
_compile_action(ctx, inputs, compile_action_outputs, outs.dts_bundles, None, tsconfig_file, node_opts, "devmode")
def _ts_expected_outs(ctx, label, srcs_files = []):
# rules_typescript expects a function with two or more arguments, but our

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