Compare commits

..

146 Commits

Author SHA1 Message Date
769c132d22 release: cut the v7.2.14 release 2019-04-23 13:10:31 -07:00
ee65d0d4ab fix(common): prevent repeated application of HttpParams mutations (#29045)
Previously, an instance of HttpParams would retain its list of mutations
after they have been materialized as a result of a read operation. Not
only does this unnecessarily hold onto memory, more importantly does it
introduce a bug where branching of off a materialized instance would
reconsider the set of mutations that had already been applied, resulting
in repeated application of mutations.

This commit fixes the bug by clearing the list of pending mutations
after they have been materialized, such that they will not be considered
once again for branched off instances.

Fixes #20430

PR Close #29045
2019-04-23 08:43:54 -07:00
cd48a5383d build(docs-infra): pin versions of packages installed in preview server docker image (#29976)
This minimises the risk of unexpected failures due to breaking changes,
when building a new image (e.g. as a result of an unrelated config
change in Dockerfile).

PR Close #29976
2019-04-23 08:33:28 -07:00
0323affe18 build(docs-infra): upgrade preview server docker image to Debian 9 (#29976)
Previously, the preview server docker image was based on Debian 8
(jessie). Recently, `jessie-updates` and `jessie-backborts` were removed
from the Debian mirrors ([more info][1]), thus breaking new builds of
the image.

Instead of updating `/etc/apt/sources.list` to remove the obsolete
sources, this commit upgrades to Debian 9 (stretch).

(The GCE VM running the preview server docker container was also
upgraded from Debian 8 to 9 this morning.)

---
Other changes:
- Removed dependency on `chkconfig`, which is not supported on Debian 9.
- Installing `nginx` from the regular repositories (instead of
  `*-backports).
- Upgraded to `pm2` v3, which can handle hooking itself up to system
  startup better (without `chkconfig` - see above).
- Updated tests to reflect the fact that `nginx` has dropped the reason
  phrase in response status lines for HTTP/2 (in compliance with
  [the spec][2]). (HTTP/1.1: `HTTP/1.1 200 OK` | HTTP/2: `HTTP/2 200`)

[1]: https://www.lucas-nussbaum.net/blog/?p=947
[2]: https://http2.github.io/http2-spec/#rfc.section.8.1.2.4

PR Close #29976
2019-04-23 08:33:28 -07:00
b793aa3f85 build(docs-infra): increase build artifact size limit for preview server (#29976)
In #29926, the size of the build artifacts has increased due to turning
on differential loading (which generates an es2015/es5 pair for each JS
resource).

To avoid the preview server's rejecting the build artifacts (as in
[288181][1]), this commit increases the max allowed artifact size from
20MB to 25MB (current artifact size after #29926 is ~22MB).

[1]: https://circleci.com/gh/angular/angular/288181

PR Close #29976
2019-04-23 08:33:28 -07:00
4382ceae02 docs: fix grammatical errors in the guides and API documentation (#29928)
Fix grammatical errors in the DI and HttpClient guides as well as the Resolve API documentaiton.

There is no associated issue.

PR Close #29928
2019-04-22 17:32:31 -07:00
9a81e52eac docs: fixed typo on HttpParamsOptions (#29930)
PR Close #29930
2019-04-22 16:36:53 -07:00
903aef07f4 docs: fix typo in routing section of Tour of Heroes (#29961)
PR Close #29961
2019-04-22 11:18:45 -07:00
7f15c014d3 docs: change doc for address form group in reactive forms guide (#30007)
Closes #29925

PR Close #30007
2019-04-22 11:17:38 -07:00
546299b6f2 docs: add ng-japan 2019 event (#30021)
PR Close #30021
2019-04-22 08:45:45 -07:00
a593b3f877 ci(docs-infra): do not build with Ivy on 7.2.x (#29993)
The 7.2.x does not include the code necessary to build with Ivy. The
`test_aio_local_ivy` job needs to be skipped on 7.2.x.

The job was accidentally enabled while rebasing 1cdffbdc2.

PR Close #29993
2019-04-19 14:15:03 -07:00
2bb4263a26 ci(docs-infra): increase wait for SW on all origins to avoid CI flakes (#29988)
In #29953, the wait period for SW on localhost was increased to avoid CI
flakes for the PWA score tests.

This commit expands the fix to non-localhost origins to avoid flakes in
the `aio_monitoring` job, when CircleCI VMs/network are slow.
(For reference, example failures: [289127], [289238])

[289127]: https://circleci.com/gh/angular/angular/289127
[289238]: https://circleci.com/gh/angular/angular/289238

PR Close #29988
2019-04-19 09:59:37 -07:00
0c4430b384 build(docs-infra): make tsconfig path detection in switch-to-ivy more robust (#29989)
In light of #29926, that will change the path of `tsconfig.app.json`,
this commit switches from a hard-coded `tsconfig.app.json` path to
looking it up in `angular.json` (to be more future-proof).

PR Close #29989
2019-04-19 09:51:09 -07:00
c68826059c build(docs-infra): change build-with-ivy script to switch-to-ivy (#29989)
Previously, the `build-with-ivy` script could be used to build the `aio`
project with Ivy (once it had been prepared with `ivy-ngcc`, etc.) and
then restored the configuration (e.g. `tsconfig.json`) to non-ivy mode.

As a result, it was not useful for running other commands (e.g. unit/e2e
tests) in Ivy mode.

This commit renames the script to `switch-to-ivy` and employs a
different model (similar to `ng-packages-installer`), where the project
is setup to run in Ivy mode and then all subsequent commands are
executed in that mode (until restored).

Since this is currently only used on CI, there is no automatic way to
switch back to non-ivy mode (but it could be implemented in the future
if needed).

Finally, the script now modifies `src/tsconfig.app/json` instead of
`tsconfig.json` to ensure that the `angularCompilerOptions` are not
ignored/overwritten. This is also closer to what the cli generates
with the `--enable-ivy` option.

PR Close #29989
2019-04-19 09:50:43 -07:00
1cdffbdc2e ci(docs-infra): use pre-ivy packages in test_aio_local_ivy to test ngcc (#29989)
To better test ngcc (in addition to Ivy) on angular.io, change the
`test_aio_local_ivy` CircleCI job to use the pre-ivy Angular packages
(and have ngcc transform them to Ivy ones).

PR Close #29989
2019-04-19 09:49:10 -07:00
00f44ee6d4 refactor(docs-infra): remove unnecessary cast to any (#29989)
PR Close #29989
2019-04-19 09:46:48 -07:00
dcfde77de5 docs(animations): fixed some closing brackets on query animation page (#29854) (#29855)
PR Close #29855
2019-04-18 18:19:49 -07:00
4994527e71 docs: add Muhammad Ahsan Ayaz to GDE contributors group (#29838)
PR Close #29838
2019-04-18 18:18:17 -07:00
69eb46ad97 docs: update extra options available for RouterModule.forRoot() method (#29846)
PR Close #29846
2019-04-17 17:25:32 -07:00
6e190b1a3f ci(docs-infra): increase wait for SW on localhost to avoid CI flakes (#29953)
The server used for testing on localhost has less optimizations (e.g.
serves uncompressed files), so we need to wait longer the ServiceWorker
to be loaded and registered to allow Lighthouse to reliably detect it,
especially on slower environments (e.g. CI).

Related: https://github.com/GoogleChrome/lighthouse/issues/5527#issuecomment-483710849

Fixes #29910

PR Close #29953
2019-04-17 12:14:40 -07:00
2b1820dfea refactor(docs-infra): switch test-pwa-score.js to async/await (#29953)
PR Close #29953
2019-04-17 12:14:40 -07:00
9af4dcc442 docs: use correct values from code-example in i18n (#29920)
PR Close #29920
2019-04-16 10:31:17 -07:00
16ba4ae2a7 docs(service-worker): fix small typo in devops guide (#29866)
PR Close #29866
2019-04-16 10:30:53 -07:00
cd95db482d ci(docs-infra): temporarily lower the min required PWA score for localhost tests (#29911)
The PWA score tests have been occasionally failing on CI recently
(possibly due to CI VM/network issues).

This commit temporarily disables them, until we investigate the
root-cause and/or put a work-around in place.

The PWA score tests are still run against the deployed versions (which
don't suffer as much) or PRs with public previews (as part of the
`test_aio_preview` job) and on upstream builds (as part of the
`deploy_aio` job).

Related to #29910.

[1]: https://github.com/angular/angular/blob/3a836c362/.circleci/config.yml#L390

PR Close #29911
2019-04-15 12:34:43 -07:00
e5cbc9b22b build(docs-infra): convert search-worker.js to TypeScript (#29908)
PR Close #29908
2019-04-15 11:18:00 -07:00
cd2a2f7989 build(docs-infra): update to CLI 8 beta.13 (#29908)
PR Close #29908
2019-04-15 11:18:00 -07:00
8031e58410 fix(docs-infra): add missing ng-conf image (#29902)
As of b9fead7f8, the image is referenced in `announcements.json`, but it
was already removed (as it wasn't used at the time) in 7f905da33.

PR Close #29902
2019-04-15 09:12:59 -07:00
508db0f467 build(docs-infra): add missing dependencies (chrome-launcher, lighthouse-logger) (#29904)
We are using `chrome-launcher` and `lighthouse-logger` in
[test-pwa-score][1], but we do not explicitly list them as
`devDependencies`, so we are relying on the fact that they happen to be
hoisted in `node_modules/` by yarn. This may unexpectedly break in the
future.

This commit fixes this, by explicitly listing them as `devDependencies`.

[1]: https://github.com/angular/angular/blob/ea70d41ac/aio/scripts/test-pwa-score.js#L14-L18

PR Close #29904
2019-04-15 09:12:38 -07:00
57047acf17 build(docs-infra): upgrade lighthouse to 4.3.0 (#29904)
Also, log the Lighthouse version in `test-pwa-score` to aid in
debugging.

PR Close #29904
2019-04-15 09:12:38 -07:00
3e992e18eb release: cut the v7.2.13 release 2019-04-12 11:51:08 -07:00
d801941266 ci: correctly detect status 400 as failure in get-commit-range (#29839)
PR Close #29839
2019-04-11 08:20:27 -07:00
4dc066b979 docs(aio): add missing mentors for collaborators (#29142)
Also improve the presentation of the org chart

PR Close #29142
2019-04-11 08:06:19 -07:00
71c1446def docs(aio): add globegitter to Angular collaborators (#29142)
PR Close #29142
2019-04-11 08:06:19 -07:00
ca14509bfe fix(platform-browser): insert APP_ID in styles, contentAttr and hostAttr (#17745)
PR Close #17745
2019-04-11 07:59:23 -07:00
19fe45dfdc docs: update path mappings and exports (#29810)
1) Path mappings are to be added in the workspace tsconfig files, hence the path needs to be `./` and not `../`
2) Fix export symbol as it cannot contain `-`

Fixes #29807

PR Close #29810
2019-04-10 12:17:32 -07:00
42b7f8525b docs: redirect to schematics-for-libraries (#29780)
Fixes #29778

PR Close #29780
2019-04-10 12:16:48 -07:00
7f1c5f6248 docs: fix grammatical errors in the style guide (#29769)
Fix grammatical errors in the Style Guide.

There is no associated issue.

PR Close #29769
2019-04-10 12:16:31 -07:00
7089c20ce5 docs: fix a grammatical error in the building and serving documentation (#29768)
Fix a grammatical error in the building and serving documentation.

There is no associated issue.

PR Close #29768
2019-04-10 12:16:15 -07:00
b9609020f6 docs: fix incorrect header on custom event docs (#29784)
PR Close #29784
2019-04-09 12:24:00 -07:00
ea0f031995 docs(core): DI module-level info fix (#29756)
PR Close #29756
2019-04-09 12:23:44 -07:00
2e1fc36c4e docs: update release schedule and examples of release numbers (#29714)
PR Close #29714
2019-04-09 12:23:28 -07:00
47c486860e docs: update authors style guide to highlight related guidelines and position these guidelines (#28481)
PR Close #28481
2019-04-09 12:23:10 -07:00
77ee22eb6e docs: typo fix, change "insure" to "ensure" (#29737)
PR Close #29737
2019-04-08 17:18:49 -07:00
8c0c689338 docs(router): use pipe in activatedRoute example (#29752)
Copying the example, mentioned at Activated Route (https://angular.io/api/router/ActivatedRoute), to Stackblitz doesn't compile: https://stackblitz.com/edit/angular-x7pbeb?

That's because the example does not use .pipe.

I've updated the example to include `pipe`.
PR Close #29752
2019-04-08 17:18:08 -07:00
f971f83f06 ci: simplify RBE execution (#29731)
Run all targets with RBE config. Previously we filtered out one target, //tools/ts-api-guardian:tests, and ran that with a different bazelrc

PR Close #29731
2019-04-08 12:03:55 -07:00
895b0454f8 refactor(docs-infra): rename properties (blacklisted --> ignored) (#29754)
PR Close #29754
2019-04-08 09:46:35 -07:00
8febb5ac55 build(docs-infra): do not include announcements.json in sitemap (#29754)
The `announcements.json` file should not be included in the sitemap and
including it causes an error in Google Search Console (because the
generated URL does not exist).

(This is a follow-up to fbef94a8e.)

PR Close #29754
2019-04-08 09:46:35 -07:00
7d6d4c436c refactor: uniformly access all index sigs with index access. (#29390)
Follow-up to https://github.com/angular/angular/pull/28937

A build-time check to enforce this is part of tsetse -
https://tsetse.info/property-renaming-safe but I don't know how to turn
it on for this repositories bazel builds.

PR Close #29390
2019-04-05 09:34:27 -07:00
c15b223001 docs: add schematics guide (patch) (#29666)
PR Close #29666
2019-04-04 15:56:03 -07:00
1423cc0ad2 docs: correct route path description (#29669)
PR Close #29669
2019-04-04 15:41:00 -07:00
e483078d6f docs: add universal terms to glossary (#28492)
PR Close #28492
2019-04-04 14:26:38 -07:00
29481a17b1 test(docs-infra): fix eslint warnings (#29673)
```
warning  An "it" that uses an async method should handle failure (use "done.fail")  jasmine/no-promise-without-done-fail
```

PR Close #29673
2019-04-04 10:52:39 -07:00
09b374c92c build(docs-infra): support doc aliases via @alias dgeni tag (#29673)
Now, one can add an `@alias` tag to API docs, which tells dgeni that this
API element (usually a `const`) is really just an alias for some API element
defined elsewhere.

Dgeni will then look up this API element and copy over the properties from
the alias to the current doc.

For example, we would like to privately export an Enum from `@angular/core`
but then publicly export this from `@angular/common`:

**packages/core/private_exports.ts**

```ts
/**
 * Description of this document.
 */
export enum ɵSomeEnum { ... }
```

**packages/common/public_api.ts**

```ts
import {ɵSomeEnum} from '@angular/core';

 /**
 * @alias core/ɵSomeEnum
 */
export const SomeEnum = ɵSomeEnum;
```

In the generated docs there will be a page for `common/SomeEnum`, which
will be rendered as an enum, rather than a const, showing the description
extracted from the `core/ɵSomeEnum`.

---

The implementation of this feature required some refactoring of the other
processing:

1. Previously `ɵ` prefixed exports were not even considered.
2. Due to 1. some processors needed to have guards added to ignore such
   private exports (`addMetadataAliases` and `checkContentRules`).
3. The processing of package pages had to be reworked (and split) so that
   it picked up the aliased export docs after their alias proeprties had
   been copied.

See FW-1207, FW-632, #29249

PR Close #29673
2019-04-04 10:52:38 -07:00
3f6b279033 style(docs-infra): fix typo (#29673)
PR Close #29673
2019-04-04 10:52:38 -07:00
b4c77d551b build: add render3 entry-point to legacy test systemjs config (#29675)
Until the legacy job has been migrated to Bazel w/ Saucelabs, we
need to add the `render3` entry-point to the test SystemJS configuration
so that developers don't get inconsistent behavior between Bazel tests
and the legacy tests. e.g. https://circleci.com/gh/angular/angular/270102

PR Close #29675
2019-04-04 10:52:13 -07:00
588a3ed79d docs: add ibm carbon ui library (#29706)
PR Close #29706
2019-04-04 10:50:07 -07:00
9e33dc3e6a fix(bazel): use //:tsconfig.json as the default for ng_module (#29670) (#29711)
This matches the behavior of ts_library

PR Close #29670

PR Close #29711
2019-04-04 10:49:35 -07:00
0930820748 docs: add angular.de to the workshops page (#28856)
undo unrelated whitespace change

PR Close #28856
2019-04-03 15:39:21 -07:00
96ff4971f2 docs: add ngx-smart-modal to doc resources (#29661)
PR Close #29661
2019-04-03 15:36:30 -07:00
c9b9be6c2e docs: update announcements for home page (#29671)
Updated dates, add tracking to links

PR Close #29671
2019-04-03 15:36:04 -07:00
736afea5f1 docs: remove trailing comma in BAZEL.md (#29682)
PR Close #29682
2019-04-03 15:30:22 -07:00
a14dc2d7a4 release: cut the v7.2.12 release 2019-04-03 13:27:46 -07:00
a357daeaf7 docs: update location API docs (#27010)
PR Close #27010
2019-04-02 16:11:29 -07:00
ec4dced523 docs: update examples region to match the description in the guide (#29259)
PR Close #29259
2019-04-02 16:09:20 -07:00
69786c2814 docs: add Deborah Kurata to Trusted Collaborators (#29605)
This commit adds Deborah to the Trusted Collaborators list, so she will appear in the Collaborators list on angular.io.
PR Close #29605
2019-04-02 15:53:49 -07:00
50bcaa6f5b refactor(upgrade): use Bazel packages to avoid symlinks in the source (#29656)
Previously we had to share code between upgrade/dynamic and upgrade/static
by symlinking the `src` folder, which allowed both packages to access
the upgrade/common files.

These symlinks are always problematic on Windows, where we had to run
a script to re-link them, and restore them.

This change uses Bazel packages to share the `upgrade/common` code,
which avoids the need for symlinking the `src` folder.

Also, the Windows specific scripts that fixup the symlinks have also
been removed as there is no more need for them.

PR Close #29656
2019-04-02 10:54:34 -07:00
6bf8e37827 docs: add info regarding injecting custom pipes (#28291)
Indicate that a pipe also needs to be included in the providers array

PR Close #28291
2019-04-02 10:38:38 -07:00
91f2b1e4d7 refactor(platform-browser): rename _singleTagWhitelist (#29592)
PR Close #29592
2019-04-02 10:37:30 -07:00
c7075fe710 refactor(compiler): rename INTERPOLATION_BLACKLIST_REGEXPS (#29593)
PR Close #29593
2019-04-02 10:36:28 -07:00
e2e0be4ac6 refactor: change error message (#29594)
Removes usage of whitelist from error messages, comments and test descriptions in ts_api_guardian

Related to #28539

PR Close #29594
2019-04-02 10:29:34 -07:00
f6864cce91 refactor(compiler): update docs (#29599)
PR Close #29599
2019-04-02 10:28:53 -07:00
ef038e8060 refactor(core): update docs (#29600)
PR Close #29600
2019-04-02 10:28:24 -07:00
3580a6c986 fix(docs-infra): fix scroll position restoration error in ScrollService (#29658)
Based on Google Analytics error report, the following error happens
occasionally (15% or total errors for 2019-03):

```
Cannot read property '0' of null TypeError: at t.scrollToPosition@main.js
```

This was a result of calling [ViewportScroller#scrollToPosition()][1]
with `null`, which in turn happens when calling
[ScrollService#scrollToPosition()][2] while `this.scrollPosition` is
`null`. This can be a result of a `popstate` event without an associated
history state.

This commit fixes the error by checking whether `this.scrollPosition` is
`null`, before using it with `scrollToPosition()`.

(It also refactors away the unneeded internal `popStateFired` property.)

[1]: https://github.com/angular/angular/blob/deca6a60d/packages/common/src/viewport_scroller.ts#L101-L105
[2]: https://github.com/angular/angular/blob/deca6a60d/aio/src/app/shared/scroll.service.ts#L158-L161

PR Close #29658
2019-04-02 10:26:51 -07:00
67e9812c14 perf(docs-infra): avoid unnecessary JSON parsing in ScrollService (#29658)
PR Close #29658
2019-04-02 10:26:51 -07:00
61b08629fd refactor(docs-infra): minor clean-up of ScrollService (#29658)
PR Close #29658
2019-04-02 10:26:51 -07:00
d727561c5e test(docs-infra): avoid click-related CI flake in e2e test (#29641)
One of the tests introduced in #29601 is susceptible to a kind of
WebDriver flake related to trying to click elements hidden behind fixed
positioned elements.
This commit works around the issue by clicking the elements directly
using JavaScript (instead of `WebElement#click()`).

PR Close #29641
2019-04-01 16:18:05 -07:00
39ecc7b5b6 docs: improve formatDate description (#29632)
PR #29289 reworded the description, making it less obvious that the value to format can be a `Date`.

PR Close #29632
2019-04-01 15:14:51 -07:00
23f0a04387 build: Remove --watchfs from bazelrc file (#29635)
--watchfs is causing the build to be flaky on Windows because Bazel
doesn't have proper support for watchfs on Windows, but it doesn't seem
to bring much performance on Linux, either.

Fixes: https://github.com/angular/angular/issues/29541

PR Close #29635
2019-04-01 14:58:05 -07:00
e884c0cd7b fix(router): adjust setting navigationTransition when a new navigation cancels an existing one (#29636)
Prior to this change, if a navigation was ongoing and a new one came in, the router could get into a state where `router.currentNavigation` was `null` even though a navigation was executing. This change moves where we set the `currentNavigation` value so it's inside a `switchMap`. This solves the problem because the `finally` on the `switchMap` had been setting `currentNavigation` to `null` but the new `currentNavigation` value would have already been set. Essentially this was a timing problem and is resolved with this change.

Fixes #29389 #29590

PR Close #29636
2019-04-01 12:11:57 -07:00
ab72e06cde docs: add custom-element ref to library overview (#28476)
PR Close #28476
2019-04-01 12:04:08 -07:00
5218f5a401 docs: update CODEOWNERS with missing or removed guides/examples (#29612)
PR Close #29612
2019-04-01 11:52:25 -07:00
e8b00ec404 docs: add missing window key shortcut to language service guide (#29613)
PR Close #29613
2019-04-01 11:28:14 -07:00
94b0e8f288 docs: update Jorge Cano GDE bio (#29622)
PR Close #29622
2019-04-01 11:26:41 -07:00
157b948e6b test(docs-infra): add e2e test for the contributors page (#29601)
Previously, if the shape of data in `contributors.json` was incorrect,
there would be a runtime error (when trying to parse the data), which
would result in a blank page. The likelihood for this happening is
higher after #29553, where the shape of data changed.

This commit adds some basic e2e tests that verify the page works as
expected and there are contributors listed.

PR Close #29601
2019-04-01 11:24:44 -07:00
7b9d62b2ed docs: use smaller image for Jason Bedard (since it was already available) (#29601)
PR Close #29601
2019-04-01 11:24:43 -07:00
e4eed73591 docs(http): add information about body content type to flush method for testing (#29214)
PR Close #29214
2019-04-01 11:03:51 -07:00
a52123f538 docs: add missing picture of Jason Bedard (#29596)
Related to #29141.

PR Close #29596
2019-04-01 11:00:38 -07:00
15193fce98 test(upgrade): work around SauceLabs issue with loading AngularJS files (#29603)
Sometimes (especially on mobile browsers on SauceLabs) the script may
fail to load due to a temporary issue with the internet connection. To
avoid flakes on CI when this happens, we retry the download after some
delay.

Related to #28578.

PR Close #29603
2019-04-01 10:59:46 -07:00
572a5e4bd3 build(docs-infra): remove unused PhantomJS dependency (#29611)
PhantomJS is [not being developed any more][1] and with modern
alternatives (such as Chrome headless) there is no reason to keep it as
a dependency.

[1]: https://github.com/ariya/phantomjs/issues/15344

PR Close #29611
2019-04-01 10:57:32 -07:00
601fae944d docs: added complete path to the spec file (#29621)
PR Close #29621
2019-04-01 10:54:31 -07:00
d46f1f28e2 docs: fix typo in description of angular.io/index.html (#29630)
change "compeling" to "compelling" in description meta tag

PR Close #29630
2019-04-01 10:54:10 -07:00
a2df6031ad build(docs-infra): add check to ensure all contributor pictures exist (#29553)
PR Close #29553
2019-03-29 11:01:48 -07:00
542b7cd925 docs: add Sam Julien to GDE group as well (#29553)
PR Close #29553
2019-03-29 11:01:48 -07:00
ce1b741255 feat(docs-infra): support contributors belonging to multiple groups (#29553)
PR Close #29553
2019-03-29 11:01:48 -07:00
4a1b4f17ae refactor(docs-infra): change unused classes to interfaces (#29553)
PR Close #29553
2019-03-29 11:01:48 -07:00
7ea8985500 docs: change the order of groups in contributors page (#29553)
Put "Collaborators" before "GDE", since they are semantically closer to
the core team.

PR Close #29553
2019-03-29 11:01:47 -07:00
eefae6d96d docs: change heading of contributors page to avoid confusion (Collaborators --> Contributors) (#29553)
Now that "Collaborators" is a separate group in the contributors page,
having "Angular Collaborators" as the heading of the page (which also
contains the "Angular" and "GDE" groups) is confusing.
Changing the title to "Angular Contributors" to avoid confusion.

PR Close #29553
2019-03-29 11:01:47 -07:00
9f430f0ad9 docs: change contributor group name (Collaborator --> Collaborators) (#29553)
The name of the group also serves as the group tab's title in the docs
app and having "Collaborator" (singular) as a title for a list of people
didn't read well.
Changing th group name (and thus tab title) to "Collaborators" (plural).

PR Close #29553
2019-03-29 11:01:47 -07:00
60b5bf7b99 docs: fix typo in renderer description (#29581)
PR Close #29581
2019-03-29 10:50:36 -07:00
7671c73655 fix(common): escape query selector used when anchor scrolling (#29577)
When an anchor scroll happens, we run document.querySelector. This value can be taken directly from the user. Therefore it's possible to throw an error on scrolling, which can cause the application to fail.

This PR escapes the selector before using it.

Related to #28193
[Internal discussion](https://groups.google.com/a/google.com/forum/#!topic/angular-users/d82GHfmRKLc)

PR Close #29577
2019-03-29 10:39:40 -07:00
6d7a4ff45e docs: move text that mention what is expected to fail up higher into the section (#27329)
PR Close #27329
2019-03-29 10:30:29 -07:00
cfa6ab1910 docs: remove coremodule references (#28434)
PR Close #28434
2019-03-29 10:29:52 -07:00
Jun
2f930a5d44 docs(service-worker): add info about no network request for performance strategy (#29288)
PR Close #29288
2019-03-29 10:28:47 -07:00
886df3e604 docs: api doc for i18n (#29289)
PR Close #29289
2019-03-29 10:27:43 -07:00
72900eccaf build: make VSCode settings opt-in (#29504)
PR Close #29504
2019-03-29 10:26:56 -07:00
d9b1136ba4 docs: add note about ngModel usage in structural directives guide (#29522)
When I tried one of the examples provided in the documention, encountered the " Can't bind to 'ngModel' since it isn't a known property of 'select' ” error. So to resolve it I imported the FormsModule in ngModule. If it is mentioned in the documentation, it would be handy for the beginners to try and learn. Thanks for considering.

Duplicate of pull Request# 29206, As I encountered few issues after multiple authors corrected, So I am creating the new pull request

PR Close #29522
2019-03-29 10:26:16 -07:00
97fc5f662e docs: sync contributors and related infra with master (#29584)
PR #28930 was originally only merged into master. Later is was
backported to the patch branch as 34395ae, but some changes were missed.

This commit backports the missing bits to get the contributors and
related infrastructure in sync with master. This makes it easier to
backport future changes.

PR Close #29584
2019-03-29 10:05:43 -07:00
3ad67e5ef6 docs: add asset config details (#28214)
PR Close #28214
2019-03-28 15:31:25 -07:00
baec8abc75 docs: reorg deployment guide with serve-from-disk info from CLI story (#28217)
PR Close #28217
2019-03-28 15:30:51 -07:00
1e3c015d89 docs: add Joost Koehoorn to Angular collaborators (#29554)
PR Close #29554
2019-03-28 10:12:01 -07:00
9419165079 build: add version name pre release check [patch] (#29552)
* build: use cross platform workspace_status_command

* build: add pre-release check that validates the version name

Currently with the release of "8.0.0-beta.10", the Bazel npm packag accidentally
was stamped with an incorrect version placeholder: `8.0.0-beta.10+1.sha-a28b3e3`.

This can happen because the placeholder is based on latest tag that matches the
Semver format. e.g. if `HEAD` equals to the commit that has the latest tag, the
version name will be correct and refer to the tag name (e.g. `8.0.0-beta.10`). Though
if the latest commit is not tagged with the most recent tag, the version
name will also include the SHA of the commit (e.g.  `8.0.0-beta.10+1.sha-a28b3e3`).

We can ensure that we don't accidentally release versions from a more recent commit
that shouldn't be part of the release by adding a pre-release check that ensures that
the `BUILD_SCM_VERSION` Bazel status variable matches the expected version format.
2019-03-27 15:11:47 -07:00
1f749fead5 ci: add packages/examples/common/ to fw-core (#29533)
PR Close #29533
2019-03-27 13:44:03 -07:00
3d60f6de5f ci: lock chrome and firefox versions for saucelabs (#29529)
With 0f1da49b86, the Chrome beta job has
been disabled because a new Chrome beta has been released, but Saucelabs
didn't support a chromedriver that is compatible with that given beta version.

Now the topic of pinning these external browsers to a specific version came up. In order to make the build less prone to unexpected new versions, we need to
_permanently_ disable the Chrome beta browser. Otherwise pinning `SL_CHROME`
to a specific version does not statisfy the requirement of making the CI jobs
more deterministic.

See original discussion: https://github.com/angular/angular/pull/29518#discussion_r269140676

Chrome can be pinned to Chrome v73 (latest stable version at time of this commit)
Firefox can be pinned to Firefox v65 (latest stable version available in Saucelabs platform)

PR Close #29529
2019-03-27 12:38:14 -07:00
8aefbfe7bc docs: add schematic details and links to config page (#29549) 2019-03-27 10:49:15 -07:00
2d12af3589 docs: update description text (#28507)
PR Close #28507
2019-03-27 09:57:37 -07:00
57bef7306c docs: fix lint error (#28507)
PR Close #28507
2019-03-27 09:57:37 -07:00
c09a879b69 docs: add details to HttpUrlEncodingCodec API description (#28507)
PR Close #28507
2019-03-27 09:57:36 -07:00
95d7144aba docs(router): clarify scrollPositionRestoration options, refactor example (#29260)
clarify scrollPositionRestoration enabled to fully describe the functionality it provides. refactor app module example to compile and remove dependency on unnecessary framework. Remove component example due to bug on reload.

PR Close #29260
2019-03-27 09:55:57 -07:00
29d2986454 docs: replaced hero with heroes component property (#29487)
PR Close #29487
2019-03-27 09:53:52 -07:00
4a73dfc500 docs: add Bonnie to GDE page (#29432)
PR Close #29432
2019-03-27 09:42:29 -07:00
ac3e91bc2e docs: update Shortcut keys for Developer tools in chrome Browser on Windows machine (#29485)
PR Close #29485
2019-03-27 09:41:40 -07:00
516956905d docs: update Shortcut keys for Developer tools in chrome Browser on Windows platform (#29485)
PR Close #29485
2019-03-27 09:41:40 -07:00
81e8eeb0d8 docs: add Sam Julien to Collaborator group (#29528)
PR Close #29528
2019-03-27 09:40:51 -07:00
cfae2c27cb release: cut the v7.2.11 release 2019-03-26 15:30:36 -07:00
f4be046d04 docs: fix typo in rxjs guide example (#29446)
PR Close #29446
2019-03-26 12:49:47 -07:00
7081e39a78 test(platform-browser): fix shadow dom test not working in firefox 65 (#29518)
With 093dc915ae9ad92a3baa602eb7cb7862ca4b6734, Firefox has been updated
to the latest available version within Saucelabs. Firefox added shadow DOM support
in Firefox 63 and therefore the shadow dom test in `platform-browser` now runs as well.

This test currently fails because Firefox does not support computed style property
shorthands. In order to make this test work on Firefox now, we just switch from `border`
to `background` (because of the overhead when comparing each `top`, `bottom`, `left`, `right`-border properties)

PR Close #29518
2019-03-26 10:40:27 -07:00
5d5bb58e5a ci: temporarily disable chrome beta for saucelabs (#29518)
We need to temporarily disable Chrome beta within the
`legacy-saucelabs-tests` and `test_saucelabs_bazel` job.

This is necessary because Saucelabs added Chrome v74 to their
beta version channel without providing the corresponding
chromedriver that is *required* for that beta version.

Explicitly specifying a chromedriver within the Selenium browser
capabilities doesn't seem to work because Saucelabs seems to mirror
their supported chromedriver versions. Meaning that we can't explicitly
use chromedriver.storage.googleapis.com/index.html?path=74.0.3729.6/

Re-enabling tracked with: FW-1205

PR Close #29518
2019-03-26 10:40:27 -07:00
f65fa3aa1c ci: update chrome and firefox saucelabs versions (#29518)
Updates the browser versions of Chrome and Firefox to the latest releases. This
matches what we claim to support according to https://angular.io/guide/browser-support.

PR Close #29518
2019-03-26 10:40:27 -07:00
dbbfab3ee8 docs: changing "struture" to "structure" (#29497)
PR Close #29497
2019-03-25 09:21:11 -07:00
b87ae8e5f9 ci: add .codefresh/ to the fw-dev-infra group (#29478)
PR Close #29478
2019-03-22 13:16:17 -07:00
8b1d28ff48 docs(core): close tags in example (#29474)
PR Close #29474
2019-03-22 13:15:04 -07:00
463e967abb docs: update developer guide for testing and IntelliJ (#29048)
provide the command for running all test suites
- don't make new contributors read the whole Bazel doc
add info about ClangFormatIJ plugin for IntelliJ
add info about Bazel plugin for IntelliJ
fix inconsistent casing of GitHub

PR Close #29048
2019-03-22 13:05:42 -07:00
71b8bbef95 docs: fix minor typo in testing.md (#29464)
PR Close #29464
2019-03-22 10:59:22 -07:00
a2cf677714 docs: add Siddharth Ajmera to GDE resources (#28456)
PR Close #28456
2019-03-21 15:58:21 -07:00
9e1ce318a8 docs: add NG-DE 2019 to events page (#29079)
PR Close #29079
2019-03-21 15:55:17 -07:00
a00c2a9210 docs: add app shell guide (#28591)
PR Close #28591
2019-03-21 15:54:27 -07:00
a677d8b330 docs(core): indicate OnPush applies to children (#28320)
Indicate the OnPush change detection strategy applies to all child directives.
Resolves confusion from #12480.
PR Close #28320
2019-03-21 15:48:47 -07:00
7fe89ba4ea docs: drop Coding Conventions section from style guide (#29331)
Closes #24153

PR Close #29331
2019-03-21 15:40:21 -07:00
a8af83bf36 ci: disable sauce-connect ssl bumping (#29447)
By default we disable SSL bumping for all requests. This is because SSL
bumping is not needed for our test setup and in order to perform the SSL
bumping, Saucelabs intercepts all HTTP requests in the tunnel VM and modifies
them. This can cause flakiness as it makes all requests dependent on the SSL bumping
middleware.

See: https://wiki.saucelabs.com/display/DOCS/Troubleshooting+Sauce+Connect#TroubleshootingSauceConnect-DisablingSSLBumping

PR Close #29447
2019-03-21 22:18:19 +00:00
c75e16aae8 docs: add Luis Aviles to GDE resources (#29405)
PR Close #29405
2019-03-20 19:06:24 -04:00
1affcc63c0 docs: add docs team to Angular Team section on collaborators page (#29396) (#29434)
PR Close #29396

PR Close #29434
2019-03-20 18:33:49 -04:00
34395aeea1 docs: update contributors.json on patch branch 2019-03-20 14:48:27 -07:00
f64de2e9f3 docs: add Justin Schwartzenberger to GDE page (#29430)
PR Close #29430
2019-03-20 16:14:04 -04:00
86ad77c1b1 ci: add devversion to fw-dev-infra (#29418)
PR Close #29418
2019-03-20 13:44:31 -04:00
d5c815f1f2 docs: add alexeagle to @angular/fw-global-approvers (#29287)
This is for large-scale refactorings

PR Close #29287
2019-03-20 13:43:23 -04:00
32c6d5313a Revert "build: add a nice Angular-themed color for the vscode status bar (#29407)" (#29419)
This reverts commit efcd6af17d.
Engineers on the team thought that the red color means something is
broken.
See slack discussion in #general

PR Close #29419
2019-03-20 13:31:28 -04:00
333 changed files with 5056 additions and 5217 deletions

View File

@ -33,8 +33,9 @@ test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test
# See https://github.com/bazelbuild/bazel/issues/4603
build --symlink_prefix=dist/
# Performance: avoid stat'ing input files
build --watchfs
# Disable watchfs as it causes tests to be flaky on Windows
# https://github.com/angular/angular/issues/29541
build --nowatchfs
# Turn off legacy external runfiles
run --nolegacy_external_runfiles
@ -57,7 +58,9 @@ test --incompatible_strict_action_env
###############################
# Releases should always be stamped with version control info
build:release --workspace_status_command=./tools/bazel_stamp_vars.sh
# This command assumes node on the path and is a workaround for
# https://github.com/bazelbuild/bazel/issues/4802
build:release --workspace_status_command="node ./tools/bazel_stamp_vars.js"
###############################
# Output #

View File

@ -170,10 +170,7 @@ jobs:
# 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,-local
# Now run RBE incompatible tests locally.
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
- run: yarn bazel test //... --build_tag_filters=-ivy-only,local --test_tag_filters=-ivy-only,local
- run: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
# Temporary job to test what will happen when we flip the Ivy flag to true
test_ivy_aot:
@ -269,12 +266,6 @@ jobs:
steps:
- *attach_workspace
- *init_environment
# Rename the Ivy packages dist folder to "dist/packages-dist" as the AIO
# package installer picks up the locally built packages from that location.
# *Note*: We could also adjust the packages installer, but given we won't have
# two different folders of Angular distributions in the future, it's likely not
# worth the efforts to change the AIO packages installer.
- run: mv dist/packages-dist-ivy-aot dist/packages-dist
# Build aio with Ivy (using local Angular packages)
- run: yarn --cwd aio build-with-ivy --progress=false
@ -555,7 +546,7 @@ workflows:
- build-npm-packages
# - test_aio_local_ivy:
# requires:
# - build-ivy-npm-packages
# - build-npm-packages
- test_aio_tools:
requires:
- build-npm-packages

View File

@ -141,7 +141,7 @@ function getJson(url) {
const opts = {headers: {Accept: 'application/json'}};
const onResponse = res => {
const statusCode = res.statusCode || -1;
const isSuccess = (200 <= statusCode) && (statusCode <= 400);
const isSuccess = (200 <= statusCode) && (statusCode < 400);
let responseText = '';
res.

8
.github/CODEOWNERS vendored
View File

@ -46,6 +46,7 @@
# andrewseguin - Andrew Seguin
# benlesh - Ben Lesh
# brandonroberts - Brandon Roberts
# devversion - Paul Gschwendtner
# filipesilva - Filipe Silva
# gkalpak - George Kalpakas
# hansl - Hans Larsen
@ -87,6 +88,7 @@
# - IgorMinar
# - kara
# - mhevery
# - alexeagle
# ===========================================================
@ -325,6 +327,7 @@
# ===========================================================
#
# - alexeagle
# - devversion
# - filipesilva
# - gkalpak
# - IgorMinar
@ -429,6 +432,7 @@
/packages/platform-browser-dynamic/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/platform-webworker/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/platform-webworker-dynamic/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/examples/common/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/architecture-components.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/architecture-modules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
@ -509,6 +513,7 @@
/aio/content/guide/module-types.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/template-syntax.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/built-in-template-functions/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/event-binding/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/interpolation/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/template-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
@ -624,6 +629,7 @@
/packages/service-worker/** @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/service-worker-getting-started.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/service-worker-getting-started/** @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/app-shell.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/service-worker-communications.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/service-worker-config.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/service-worker-devops.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
@ -776,7 +782,6 @@ testing/** @angular/fw-test
/aio/content/guide/npm-packages.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/browser-support.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/typescript-configuration.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/quickstart/** @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/setup.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/setup/** @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/build.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
@ -818,6 +823,7 @@ testing/** @angular/fw-test
/* @angular/fw-dev-infra
/.buildkite/** @angular/fw-dev-infra
/.circleci/** @angular/fw-dev-infra
/.codefresh/** @angular/fw-dev-infra
/.github/** @angular/fw-dev-infra
/.vscode/** @angular/fw-dev-infra
/docs/BAZEL.md @angular/fw-dev-infra

1
.gitignore vendored
View File

@ -14,6 +14,7 @@ pubspec.lock
.idea/
.settings/
.vscode/launch.json
.vscode/settings.json
*.swo
modules/.settings
modules/.vscode

23
.vscode/README.md vendored Normal file
View File

@ -0,0 +1,23 @@
# VSCode Configuration
This folder contains opt-in [Workspace Settings](https://code.visualstudio.com/docs/getstarted/settings) and [Extension Recommendations](https://code.visualstudio.com/docs/editor/extension-gallery#_workspace-recommended-extensions) that the Angular team recommends using when working on this repository.
## Usage
To use the recommended settings follow the steps below:
- install <https://marketplace.visualstudio.com/items?itemName=xaver.clang-format>
- copy `.vscode/recommended-settings.json` to `.vscode/settings.json`
- restart the editor
If you already have your custom workspace settings you should instead manually merge the file content.
This isn't an automatic process so you will need to repeat it when settings are updated.
To see the recommended extensions select "Extensions: Show Recommended Extensions" in the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette).
## Editing `.vscode/recommended-settings.json`
If you wish to add extra configuration items please keep in mind any settings you add here will be used by many users.
Try to keep these settings to things that help facilitate the development process and avoid altering the user workflow whenever possible.

View File

@ -1,4 +1,7 @@
{
// Format js and ts files on save with `clang-format.executable`
// If `clang-format.executable` is not being used, these two settings should be removed otherwise it will break existing formatting.
// You can instead run `yarn gulp format` to manually format your code.
"[javascript]": {
"editor.formatOnSave": true,
},
@ -8,6 +11,7 @@
// Please install https://marketplace.visualstudio.com/items?itemName=xaver.clang-format to take advantage of `clang-format` in VSCode.
// (See https://clang.llvm.org/docs/ClangFormat.html for more info `clang-format`.)
"clang-format.executable": "${workspaceRoot}/node_modules/.bin/clang-format",
// Exclude third party modules and build artifacts from the editor watchers/searches.
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
@ -16,9 +20,6 @@
"**/dist/**": true,
"**/aio/src/generated/**": true,
},
"workbench.colorCustomizations": {
"statusBar.background" : "#CB2B38",
},
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,

View File

@ -1,11 +1,15 @@
package(default_visibility = ["//visibility:public"])
exports_files([
"tsconfig.json",
"LICENSE",
"protractor-perf.conf.js",
])
alias(
name = "tsconfig.json",
actual = "//packages:tsconfig-build.json",
)
filegroup(
name = "web_test_bootstrap_scripts",
# do not sort

View File

@ -1,3 +1,43 @@
<a name="7.2.14"></a>
## [7.2.14](https://github.com/angular/angular/compare/7.2.13...7.2.14) (2019-04-23)
### Bug Fixes
* **common:** prevent repeated application of HttpParams mutations ([#29045](https://github.com/angular/angular/issues/29045)) ([ee65d0d](https://github.com/angular/angular/commit/ee65d0d)), closes [#20430](https://github.com/angular/angular/issues/20430)
<a name="7.2.13"></a>
## [7.2.13](https://github.com/angular/angular/compare/7.2.12...7.2.13) (2019-04-12)
### Bug Fixes
* **bazel:** use //:tsconfig.json as the default for ng_module ([#29670](https://github.com/angular/angular/issues/29670)) ([#29711](https://github.com/angular/angular/issues/29711)) ([9e33dc3](https://github.com/angular/angular/commit/9e33dc3))
* **platform-browser:** insert APP_ID in styles, contentAttr and hostAttr ([#17745](https://github.com/angular/angular/issues/17745)) ([ca14509](https://github.com/angular/angular/commit/ca14509))
<a name="7.2.12"></a>
## [7.2.12](https://github.com/angular/angular/compare/7.2.11...7.2.12) (2019-04-03)
### Bug Fixes
* **common:** escape query selector used when anchor scrolling ([#29577](https://github.com/angular/angular/issues/29577)) ([7671c73](https://github.com/angular/angular/commit/7671c73)), closes [#28193](https://github.com/angular/angular/issues/28193)
* **router:** adjust setting navigationTransition when a new navigation cancels an existing one ([#29636](https://github.com/angular/angular/issues/29636)) ([e884c0c](https://github.com/angular/angular/commit/e884c0c)), closes [#29389](https://github.com/angular/angular/issues/29389) [#29590](https://github.com/angular/angular/issues/29590)
<a name="7.2.11"></a>
## [7.2.11](https://github.com/angular/angular/compare/7.2.10...7.2.11) (2019-03-26)
This release contains various API docs improvements.
<a name="7.2.10"></a>
## [7.2.10](https://github.com/angular/angular/compare/7.2.9...7.2.10) (2019-03-20)

View File

@ -41,16 +41,6 @@ Here are the most important tasks you might need to use:
- `yarn example-e2e --filter=foo` - limit e2e tests to those containing the word "foo"
- `yarn example-e2e --setup --local` - run e2e tests with the local version of Angular contained in the "dist" folder
## Developing on Windows
The `packages/` directory may contain Linux-specific symlinks, which are not recognized by Windows.
These unresolved links cause the docs generation process to fail because it cannot locate certain files.
> Hint: The following steps require administration rights or [Windows Developer Mode](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) enabled!
To fix this problem, run `scripts/windows/create-symlinks.sh`. This command creates temporary files where the symlinks used to be. Make sure not to commit those files with your documentation changes.
When you are done making and testing your documentation changes, you can restore the original symlinks and delete the temporary files by running `scripts/windows/remove-symlinks.sh`.
It's necessary to remove the temporary files, because otherwise they're displayed as local changes in your git working copy and certain operations are blocked.
## Using ServiceWorker locally

View File

@ -1,5 +1,5 @@
# Image metadata and config
FROM debian:jessie
FROM debian:stretch
LABEL name="angular.io PR preview" \
description="This image implements the PR preview functionality for angular.io." \
@ -40,7 +40,7 @@ ARG AIO_TRUSTED_PR_LABEL="aio: preview"
ARG TEST_AIO_TRUSTED_PR_LABEL="aio: preview"
ARG AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
ARG TEST_AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
ARG AIO_ARTIFACT_MAX_SIZE=20971520
ARG AIO_ARTIFACT_MAX_SIZE=26214400
ARG TEST_AIO_ARTIFACT_MAX_SIZE=200
ARG AIO_PREVIEW_SERVER_PORT=3000
ARG TEST_AIO_PREVIEW_SERVER_PORT=3001
@ -76,21 +76,20 @@ RUN apt-get update -y && apt-get install -y curl
RUN curl --silent --show-error --location https://deb.nodesource.com/setup_10.x | bash -
RUN curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN echo "deb http://ftp.debian.org/debian jessie-backports main" | tee /etc/apt/sources.list.d/backports.list
RUN echo "deb http://ftp.debian.org/debian stretch-backports main" | tee /etc/apt/sources.list.d/backports.list
# Install packages
RUN apt-get update -y && apt-get install -y \
chkconfig \
cron \
dnsmasq \
nano \
nodejs \
openssl \
rsyslog \
yarn
RUN apt-get install -t jessie-backports -y nginx
RUN yarn global add pm2@2
cron=3.0pl1-128+deb9u1 \
dnsmasq=2.76-5+deb9u2 \
nano=2.7.4-1 \
nginx=1.10.3-1+deb9u2 \
nodejs=10.15.3-1nodesource1 \
openssl=1.1.0j-1~deb9u1 \
rsyslog=8.24.0-1 \
yarn=1.15.2-1
RUN yarn global add pm2@3.5.0
# Set up log rotation
@ -151,8 +150,7 @@ RUN sed -i "s|{{\$AIO_PREVIEW_SERVER_PORT}}|$TEST_AIO_PREVIEW_SERVER_PORT|g" /et
# Set up pm2
RUN pm2 startup systemv -u root > /dev/null
RUN chkconfig pm2-root on
RUN pm2 startup --user root > /dev/null
# Set up the shell scripts

View File

@ -1,7 +1,6 @@
// Imports
import * as cp from 'child_process';
import * as fs from 'fs';
import * as http from 'http';
import * as path from 'path';
import * as shell from 'shelljs';
import {AIO_DOWNLOADS_DIR, HIDDEN_DIR_PREFIX} from '../common/constants';
@ -105,18 +104,7 @@ class Helper {
Object.keys(this.portPerScheme).forEach(scheme => suiteFactory(scheme, this.portPerScheme[scheme]));
}
public verifyResponse(status: number | [number, string], regex: string | RegExp = /^/): VerifyCmdResultFn {
let statusCode: number;
let statusText: string;
if (Array.isArray(status)) {
statusCode = status[0];
statusText = status[1];
} else {
statusCode = status;
statusText = http.STATUS_CODES[statusCode] || 'UNKNOWN_STATUS_CODE';
}
public verifyResponse(status: number, regex: string | RegExp = /^/): VerifyCmdResultFn {
return (result: CmdResult) => {
const [headers, body] = result.stdout.
split(/(?:\r?\n){2,}/).
@ -131,7 +119,7 @@ class Helper {
}
expect(result.success).toBe(true);
expect(headers).toContain(`${statusCode} ${statusText}`);
expect(headers).toMatch(new RegExp(`HTTP/(?:1\\.1|2) ${status} `));
expect(body).toMatch(regex);
};
}

View File

@ -259,10 +259,10 @@ describe(`nginx`, () => {
it('should disallow non-GET requests', async () => {
await Promise.all([
h.runCmd(`curl -iLX POST ${baseUrl}/42`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX PUT ${baseUrl}/42`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX PATCH ${baseUrl}/42`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX DELETE ${baseUrl}/42`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX POST ${baseUrl}/42`).then(h.verifyResponse(405)),
h.runCmd(`curl -iLX PUT ${baseUrl}/42`).then(h.verifyResponse(405)),
h.runCmd(`curl -iLX PATCH ${baseUrl}/42`).then(h.verifyResponse(405)),
h.runCmd(`curl -iLX DELETE ${baseUrl}/42`).then(h.verifyResponse(405)),
]);
});
@ -295,10 +295,10 @@ describe(`nginx`, () => {
const url = `${scheme}://${host}/circle-build`;
Promise.all([
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse(405)),
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse(405)),
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse(405)),
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse(405)),
]).then(done);
});
@ -334,10 +334,10 @@ describe(`nginx`, () => {
it('should disallow non-POST requests', done => {
Promise.all([
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse(405)),
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse(405)),
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse(405)),
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse(405)),
]).then(done);
});

View File

@ -1,5 +1,5 @@
{
"$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json",
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"cli": {
"packageManager": "yarn",
@ -21,6 +21,7 @@
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"webWorkerTsConfig": "src/tsconfig.worker.json",
"aot": true,
"optimization": true,
"buildOptimizer": true,
@ -35,7 +36,6 @@
"assets": [
"src/assets",
"src/generated",
"src/app/search/search-worker.js",
"src/pwa-manifest.json",
"src/google385281288605d160.html",
{
@ -123,6 +123,7 @@
"karmaConfig": "src/karma.conf.js",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"webWorkerTsConfig": "src/tsconfig.worker.json",
"scripts": [],
"styles": [
"src/styles.scss"
@ -130,7 +131,6 @@
"assets": [
"src/assets",
"src/generated",
"src/app/search/search-worker.js",
"src/pwa-manifest.json",
"src/google385281288605d160.html",
{

View File

@ -92,3 +92,6 @@ upgrade-phonecat-3-final/tsconfig-aot.json
upgrade-phonecat-3-final/rollup-config.js
!upgrade-phonecat-*/**/karma.conf.js
!upgrade-phonecat-*/**/karma-test-shim.js
# schematics
!schematics-for-libraries/projects/my-lib/package.json

View File

@ -1,24 +0,0 @@
{
"description": "Contact NgModule v.1",
"files": [
"src/app/app.component.1b.ts",
"src/app/app.module.1b.ts",
"src/app/highlight.directive.ts",
"src/app/title.component.html",
"src/app/title.component.ts",
"src/app/user.service.ts",
"src/app/contact/awesome.pipe.ts",
"src/app/contact/contact.component.css",
"src/app/contact/contact.component.html",
"src/app/contact/contact.component.3.ts",
"src/app/contact/contact.service.ts",
"src/app/contact/contact-highlight.directive.ts",
"src/main.1b.ts",
"src/styles.css",
"src/index.1b.html"
],
"main": "src/index.1b.html",
"tags": ["NgModule"]
}

View File

@ -1,26 +0,0 @@
{
"description": "Contact NgModule v.2",
"files": [
"src/app/app.component.2.ts",
"src/app/app.module.2.ts",
"src/app/highlight.directive.ts",
"src/app/title.component.html",
"src/app/title.component.ts",
"src/app/user.service.ts",
"src/app/contact/contact.component.css",
"src/app/contact/contact.component.html",
"src/app/contact/contact.service.ts",
"src/app/contact/awesome.pipe.ts",
"src/app/contact/contact.component.3.ts",
"src/app/contact/contact.module.2.ts",
"src/app/contact/contact-highlight.directive.ts",
"src/main.2.ts",
"src/styles.css",
"src/index.2.html"
],
"main": "src/index.2.html",
"tags": ["NgModule"]
}

View File

@ -1,223 +0,0 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('NgModule', function () {
// helpers
const gold = 'rgba(255, 215, 0, 1)';
const powderblue = 'rgba(176, 224, 230, 1)';
const lightgray = 'rgba(211, 211, 211, 1)';
const white = 'rgba(0, 0, 0, 0)';
function getCommonsSectionStruct() {
const buttons = element.all(by.css('nav a'));
return {
title: element.all(by.tagName('h1')).get(0),
welcome: element.all(by.css('app-title p i')).get(0),
contactButton: buttons.get(0),
crisisButton: buttons.get(1),
heroesButton: buttons.get(2)
};
}
function getContactSectionStruct() {
const buttons = element.all(by.css('app-contact form button'));
return {
header: element.all(by.css('app-contact h2')).get(0),
popupMessage: element.all(by.css('app-contact div')).get(0),
contactNameHeader: element.all(by.css('app-contact form h3')).get(0),
input: element.all(by.css('app-contact form input')).get(0),
validationError: element.all(by.css('app-contact form .alert')).get(0),
saveButton: buttons.get(0), // can't be tested
nextContactButton: buttons.get(1),
newContactButton: buttons.get(2)
};
}
function getCrisisSectionStruct() {
return {
title: element.all(by.css('ng-component h3')).get(0),
items: element.all(by.css('ng-component a')),
itemId: element.all(by.css('ng-component div')).get(0),
listLink: element.all(by.css('ng-component a')).get(0),
};
}
function getHeroesSectionStruct() {
return {
header: element.all(by.css('ng-component h2')).get(0),
title: element.all(by.css('ng-component h3')).get(0),
items: element.all(by.css('ng-component a')),
itemId: element.all(by.css('ng-component ng-component div div')).get(0),
itemInput: element.all(by.css('ng-component ng-component input')).get(0),
listLink: element.all(by.css('ng-component ng-component a')).get(0),
};
}
// tests
function appTitleTests(color: string, name?: string) {
return function() {
it('should have a gray header', function() {
const commons = getCommonsSectionStruct();
expect(commons.title.getCssValue('backgroundColor')).toBe(color);
});
it('should welcome us', function () {
const commons = getCommonsSectionStruct();
expect(commons.welcome.getText()).toBe('Welcome, ' + (name || 'Sherlock Holmes'));
});
};
}
function contactTests(color: string, name?: string) {
return function() {
it('shows the contact\'s owner', function() {
const contacts = getContactSectionStruct();
expect(contacts.header.getText()).toBe('Contact of ' + (name || 'Sherlock Holmes'));
});
it('can cycle between contacts', function () {
const contacts = getContactSectionStruct();
const nextButton = contacts.nextContactButton;
expect(contacts.contactNameHeader.getText()).toBe('Awesome Sam Spade');
expect(contacts.contactNameHeader.getCssValue('backgroundColor')).toBe(color);
nextButton.click().then(function () {
expect(contacts.contactNameHeader.getText()).toBe('Awesome Nick Danger');
return nextButton.click();
}).then(function () {
expect(contacts.contactNameHeader.getText()).toBe('Awesome Nancy Drew');
});
});
it('can change an existing contact', function () {
const contacts = getContactSectionStruct();
contacts.input.sendKeys('a');
expect(contacts.input.getCssValue('backgroundColor')).toBe(color);
expect(contacts.contactNameHeader.getText()).toBe('Awesome Sam Spadea');
});
it('can create a new contact', function () {
const contacts = getContactSectionStruct();
const newContactButton = contacts.newContactButton;
newContactButton.click().then(function () {
expect(contacts.validationError.getText()).toBe('Name is required');
contacts.input.sendKeys('John Doe');
expect(contacts.contactNameHeader.getText()).toBe('Awesome John Doe');
expect(contacts.validationError.getText()).toBe('');
});
});
};
}
describe('index.html', function () {
beforeEach(function () {
browser.get('');
});
describe('app-title', appTitleTests(white, 'Miss Marple'));
describe('contact', contactTests(lightgray, 'Miss Marple'));
describe('crisis center', function () {
beforeEach(function () {
getCommonsSectionStruct().crisisButton.click();
});
it('shows a list of crisis', function () {
const crisis = getCrisisSectionStruct();
expect(crisis.title.getText()).toBe('Crisis List');
expect(crisis.items.count()).toBe(4);
expect(crisis.items.get(0).getText()).toBe('1 - Dragon Burning Cities');
});
it('can navigate to one crisis details', function () {
const crisis = getCrisisSectionStruct();
crisis.items.get(0).click().then(function() {
expect(crisis.itemId.getText()).toBe('Crisis id: 1');
return crisis.listLink.click();
}).then(function () {
// We are back to the list
expect(crisis.items.count()).toBe(4);
});
});
});
describe('heroes', function () {
beforeEach(function () {
getCommonsSectionStruct().heroesButton.click();
});
it('shows a list of heroes', function() {
const heroes = getHeroesSectionStruct();
expect(heroes.header.getText()).toBe('Heroes of Miss Marple');
expect(heroes.title.getText()).toBe('Hero List');
expect(heroes.items.count()).toBe(6);
expect(heroes.items.get(0).getText()).toBe('11 - Mr. Nice');
});
it('can navigate and edit one hero details', function () {
const heroes = getHeroesSectionStruct();
heroes.items.get(0).click().then(function () {
expect(heroes.itemId.getText()).toBe('Id: 11');
heroes.itemInput.sendKeys(' try');
return heroes.listLink.click();
}).then(function () {
// We are back to the list
expect(heroes.items.count()).toBe(6);
expect(heroes.items.get(0).getText()).toBe('11 - Mr. Nice try');
});
});
});
});
// describe('index.0.html', function() {
// beforeEach(function () {
// browser.get('index.0.html');
// });
// it('has a title', function () {
// const title = element.all(by.tagName('h1')).get(0);
// expect(title.getText()).toBe('Minimal NgModule');
// });
// });
// describe('index.1.html', function () {
// beforeEach(function () {
// browser.get('index.1.html');
// });
// describe('app-title', appTitleTests(powderblue));
// });
// describe('index.1b.html', function () {
// beforeEach(function () {
// browser.get('index.1b.html');
// });
// describe('app-title', appTitleTests(powderblue));
// describe('contact', contactTests(powderblue));
// });
// describe('index.2.html', function () {
// beforeEach(function () {
// browser.get('index.2.html');
// });
// describe('app-title', appTitleTests(gold));
// describe('contact', contactTests(powderblue));
// });
// describe('index.3.html', function () {
// beforeEach(function () {
// browser.get('index.3.html');
// });
// describe('app-title', appTitleTests(gold));
// });
});

View File

@ -1,12 +0,0 @@
{
"description": "Minimal NgModule",
"files": [
"src/app/app.component.0.ts",
"src/app/app.module.0.ts",
"src/main.0.ts",
"src/styles.css",
"src/index.0.html"
],
"main": "src/index.0.html",
"tags": ["NgModule"]
}

View File

@ -1,40 +0,0 @@
{
"description": "NgModule v.3",
"files": [
"src/app/app.component.3.ts",
"src/app/app.module.3.ts",
"src/app/app-routing.module.3.ts",
"src/app/highlight.directive.ts",
"src/app/title.component.html",
"src/app/title.component.ts",
"src/app/user.service.ts",
"src/app/contact/contact.component.css",
"src/app/contact/contact.component.html",
"src/app/contact/contact.service.ts",
"src/app/contact/awesome.pipe.ts",
"src/app/contact/contact.component.3.ts",
"src/app/contact/contact.module.3.ts",
"src/app/contact/contact-routing.module.3.ts",
"src/app/contact/contact-highlight.directive.ts",
"src/app/crisis/*.ts",
"src/app/hero/hero-detail.component.ts",
"src/app/hero/hero-list.component.ts",
"src/app/hero/hero.service.ts",
"src/app/hero/hero.component.3.ts",
"src/app/hero/hero.module.3.ts",
"src/app/hero/hero-routing.module.3.ts",
"src/app/hero/highlight.directive.ts",
"src/main.3.ts",
"src/styles.css",
"src/index.3.html"
],
"main": "src/index.3.html",
"tags": ["NgModule"]
}

View File

@ -1,19 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ContactModule } from './contact/contact.module.3';
const routes: Routes = [
{ path: '', redirectTo: 'contact', pathMatch: 'full'},
{ path: 'crisis', loadChildren: './crisis/crisis.module#CrisisModule' },
{ path: 'heroes', loadChildren: './hero/hero.module.3#HeroModule' }
];
@NgModule({
imports: [
ContactModule,
RouterModule.forRoot(routes)
],
exports: [RouterModule]
})
export class AppRoutingModule {}

View File

@ -1,30 +0,0 @@
// #docregion
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ContactModule } from './contact/contact.module';
// #docregion routes
const routes: Routes = [
{ path: '', redirectTo: 'contact', pathMatch: 'full'},
// #docregion lazy-routes
{ path: 'crisis', loadChildren: './crisis/crisis.module#CrisisModule' },
{ path: 'heroes', loadChildren: './hero/hero.module#HeroModule' }
// #enddocregion lazy-routes
];
// #enddocregion routes
@NgModule({
// #docregion imports
imports: [
ContactModule,
// #docregion forRoot
RouterModule.forRoot(routes),
// #enddocregion forRoot
],
// #enddocregion imports
// #docregion exports
exports: [RouterModule]
// #enddocregion exports
})
export class AppRoutingModule {}

View File

@ -1,17 +0,0 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// #enddocregion
/*
// #docregion template
template: '<h1 highlight>{{title}}</h1>'
// #enddocregion template
*/
// #docregion
template: '<app-title></app-title>'
})
export class AppComponent {}
// #enddocregion

View File

@ -1,13 +0,0 @@
// #docregion
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// #docregion template
template: `
<app-title></app-title>
<app-contact></app-contact>
`
// #enddocregion template
})
export class AppComponent {}

View File

@ -1,10 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-title></app-title>
<app-contact></app-contact>
`
})
export class AppComponent {}

View File

@ -1,17 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// #docregion template
template: `
<app-title></app-title>
<nav>
<a routerLink="contact" routerLinkActive="active">Contact</a>
<a routerLink="crisis" routerLinkActive="active">Crisis Center</a>
<a routerLink="heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
`
// #enddocregion template
})
export class AppComponent {}

View File

@ -1,17 +0,0 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-title></app-title>
<nav>
<a routerLink="contact" routerLinkActive="active">Contact</a>
<a routerLink="crisis" routerLinkActive="active">Crisis Center</a>
<a routerLink="heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
`
})
export class AppComponent {}

View File

@ -1,13 +0,0 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component.0';
@NgModule({
// #docregion imports
imports: [ BrowserModule ],
// #enddocregion imports
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }

View File

@ -1,52 +0,0 @@
// #docplaster
// #docregion
/* Angular Imports */
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
/* App Imports */
// #enddocregion
import { AppComponent } from './app.component.1';
/*
// #docregion
import { AppComponent } from './app.component';
// #enddocregion
*/
// #docregion
import { HighlightDirective } from './highlight.directive';
import { TitleComponent } from './title.component';
import { UserService } from './user.service';
/* Contact Related Imports */
import { FormsModule } from '@angular/forms';
import { AwesomePipe } from './contact/awesome.pipe';
import { ContactComponent } from './contact/contact.component.3';
import {
ContactHighlightDirective as ContactHighlightDirective
} from './contact/contact-highlight.directive';
@NgModule({
// #docregion imports
imports: [ BrowserModule, FormsModule ],
// #enddocregion imports
// #docregion declarations, directive, component
declarations: [
AppComponent,
HighlightDirective,
// #enddocregion directive
TitleComponent,
// #enddocregion component
AwesomePipe,
ContactComponent,
ContactHighlightDirective
// #docregion directive, component
],
// #enddocregion declarations, directive, component
// #docregion providers
providers: [ UserService ],
// #enddocregion providers
bootstrap: [ AppComponent ]
})
export class AppModule { }

View File

@ -1,46 +0,0 @@
// #docplaster
// #docregion
/* Angular Imports */
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
/* App Imports */
// #enddocregion
import { AppComponent } from './app.component.1b';
/*
// #docregion
import { AppComponent } from './app.component';
// #enddocregion
*/
// #docregion
import { HighlightDirective } from './highlight.directive';
import { TitleComponent } from './title.component';
import { UserService } from './user.service';
/* Contact Imports */
// #enddocregion
import { ContactComponent } from './contact/contact.component.3';
/*
// #docregion
import { ContactComponent } from './contact/contact.component';
// #enddocregion
*/
// #docregion
import { AwesomePipe } from './contact/awesome.pipe';
import { ContactService } from './contact/contact.service';
import { ContactHighlightDirective } from './contact/contact-highlight.directive';
@NgModule({
imports: [ BrowserModule, FormsModule ],
// #docregion declarations
declarations: [
AppComponent, HighlightDirective, TitleComponent,
AwesomePipe, ContactComponent, ContactHighlightDirective
],
// #docregion providers
providers: [ ContactService, UserService ],
// #enddocregion providers
bootstrap: [ AppComponent ]
})
export class AppModule { }

View File

@ -1,36 +0,0 @@
// #docplaster
// #docregion
/* Angular Imports */
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
/* App Imports */
// #enddocregion
import { AppComponent } from './app.component.2';
/*
// #docregion
import { AppComponent } from './app.component';
// #enddocregion
*/
// #docregion
import { HighlightDirective } from './highlight.directive';
import { TitleComponent } from './title.component';
import { UserService } from './user.service';
/* Contact Imports */
// #enddocregion
import { ContactModule } from './contact/contact.module.2';
/*
// #docregion
import { ContactModule } from './contact/contact.module';
// #enddocregion
*/
// #docregion
@NgModule({
imports: [ BrowserModule, ContactModule ],
declarations: [ AppComponent, HighlightDirective, TitleComponent ],
providers: [ UserService ],
bootstrap: [ AppComponent ],
})
export class AppModule { }

View File

@ -1,39 +0,0 @@
// #docplaster
// #docregion
// #docregion v4
/* Angular Imports */
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
/* App Imports */
import { AppComponent } from './app.component';
/* Core Modules */
import { CoreModule } from './core/core.module';
/* Routing Module */
import { AppRoutingModule } from './app-routing.module';
@NgModule({
// #docregion import-for-root
imports: [
BrowserModule,
// #enddocregion v4
// #enddocregion import-for-root
/*
// #docregion v4
CoreModule,
// #enddocregion v4
*/
// #docregion import-for-root
CoreModule.forRoot({userName: 'Miss Marple'}),
// #docregion v4
AppRoutingModule
],
// #enddocregion import-for-root
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
// #enddocregion v4
// #enddocregion

View File

@ -1,14 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { ContactComponent } from './contact.component.3';
const routes = [
{ path: 'contact', component: ContactComponent}
];
@NgModule({
imports: [ RouterModule.forChild(routes) ],
exports: [ RouterModule ]
})
export class ContactRoutingModule {}

View File

@ -1,16 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { ContactComponent } from './contact.component';
// #docregion routing
const routes = [
{ path: 'contact', component: ContactComponent}
];
@NgModule({
imports: [ RouterModule.forChild(routes) ],
exports: [ RouterModule ]
})
export class ContactRoutingModule {}
// #enddocregion

View File

@ -1,53 +0,0 @@
// #docregion
import { Component, OnInit } from '@angular/core';
import { Contact, ContactService } from './contact.service';
import { UserService } from '../user.service';
@Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: [ './contact.component.css' ]
})
export class ContactComponent implements OnInit {
contact: Contact;
contacts: Contact[];
msg = 'Loading contacts ...';
userName = '';
constructor(private contactService: ContactService, userService: UserService) {
this.userName = userService.userName;
}
ngOnInit() {
this.contactService.getContacts().subscribe(contacts => {
this.msg = '';
this.contacts = contacts;
this.contact = contacts[0];
});
}
next() {
let ix = 1 + this.contacts.indexOf(this.contact);
if (ix >= this.contacts.length) { ix = 0; }
this.contact = this.contacts[ix];
}
onSubmit() {
// POST-DEMO TODO: do something like save it
this.displayMessage('Saved ' + this.contact.name);
}
newContact() {
this.displayMessage('New contact');
this.contact = {id: 42, name: ''};
this.contacts.push(this.contact);
}
/** Display a message briefly, then remove it. */
displayMessage(msg: string) {
this.msg = msg;
setTimeout(() => this.msg = '', 1500);
}
}

View File

@ -1,32 +0,0 @@
/* #docregion */
.ng-valid[required] {
border-left: 5px solid #42A948; /* green */
}
.ng-invalid {
border-left: 5px solid #a94442; /* red */
}
.alert {
padding: 15px;
margin: 8px 0;
border: 1px solid transparent;
border-radius: 4px;
}
.alert-danger {
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
.msg {
color: blue;
background-color: whitesmoke;
border: 1px solid transparent;
border-radius: 4px;
margin-bottom: 20px;
}
.button-group {
padding-top: 12px;
}

View File

@ -1,37 +0,0 @@
<!-- #docregion -->
<h2>Contact of {{userName}}</h2>
<div *ngIf="msg" class="msg">{{msg}}</div>
<form *ngIf="contacts" (ngSubmit)="onSubmit()" #contactForm="ngForm">
<!-- #docregion awesome -->
<h3 highlight>{{ contact.name | awesome }}</h3>
<!-- #enddocregion awesome -->
<div class="form-group">
<label for="name">Name</label>
<!-- #docregion ngModel -->
<input type="text" class="form-control" required
[(ngModel)]="contact.name"
name="name" #name="ngModel" >
<!-- #enddocregion ngModel -->
<div [hidden]="name.valid" class="alert alert-danger">
Name is required
</div>
</div>
<div class="button-group">
<button type="submit" class="btn btn-default"
[disabled]="!contactForm.form.valid">
Save</button>
<button type="button" class="btn" (click)="next()"
[disabled]="!contactForm.form.valid">
Next Contact</button>
<button type="button" class="btn" (click)="newContact()">
New Contact</button>
</div>
</form>
<!-- #enddocregion -->

View File

@ -1,54 +0,0 @@
// Exact copy except import UserService from core
// #docregion
import { Component, OnInit } from '@angular/core';
import { Contact, ContactService } from './contact.service';
import { UserService } from '../core/user.service';
@Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: [ './contact.component.css' ]
})
export class ContactComponent implements OnInit {
contact: Contact;
contacts: Contact[];
msg = 'Loading contacts ...';
userName = '';
constructor(private contactService: ContactService, userService: UserService) {
this.userName = userService.userName;
}
ngOnInit() {
this.contactService.getContacts().subscribe(contacts => {
this.msg = '';
this.contacts = contacts;
this.contact = contacts[0];
});
}
next() {
let ix = 1 + this.contacts.indexOf(this.contact);
if (ix >= this.contacts.length) { ix = 0; }
this.contact = this.contacts[ix];
}
onSubmit() {
// POST-DEMO TODO: do something like save it
this.displayMessage('Saved ' + this.contact.name);
}
newContact() {
this.displayMessage('New contact');
this.contact = {id: 42, name: ''};
this.contacts.push(this.contact);
}
/** Display a message briefly, then remove it. */
displayMessage(msg: string) {
this.msg = msg;
setTimeout(() => this.msg = '', 1500);
}
}

View File

@ -1,11 +0,0 @@
// #docregion
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class ContactModule { }

View File

@ -1,37 +0,0 @@
// #docplaster
// #docregion
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { AwesomePipe } from './awesome.pipe';
// #enddocregion
import { ContactComponent } from './contact.component.3';
/*
// #docregion
import { ContactComponent } from './contact.component';
// #enddocregion
*/
// #docregion
import { ContactHighlightDirective } from './contact-highlight.directive';
import { ContactService } from './contact.service';
// #docregion class
@NgModule({
imports: [
CommonModule,
FormsModule
],
declarations: [
AwesomePipe,
ContactComponent,
ContactHighlightDirective
],
// #docregion exports
exports: [ ContactComponent ],
// #enddocregion exports
providers: [ ContactService ]
})
export class ContactModule { }
// #enddocregion class
// #enddocregion

View File

@ -1,44 +0,0 @@
// #docplaster
// #docregion
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { AwesomePipe } from './awesome.pipe';
// #enddocregion
import { ContactComponent } from './contact.component.3';
/*
// #docregion
import { ContactComponent } from './contact.component';
// #enddocregion
*/
// #docregion
import { ContactHighlightDirective } from './contact-highlight.directive';
import { ContactService } from './contact.service';
// #enddocregion
import { ContactRoutingModule } from './contact-routing.module.3';
/*
// #docregion
import { ContactRoutingModule } from './contact-routing.module';
// #enddocregion
*/
// #docregion
// #docregion class
@NgModule({
imports: [
CommonModule,
FormsModule,
ContactRoutingModule
],
declarations: [
AwesomePipe,
ContactComponent,
ContactHighlightDirective
],
providers: [ ContactService ]
})
export class ContactModule { }
// #enddocregion class
// #enddocregion

View File

@ -1,19 +0,0 @@
// #docregion
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { ContactComponent } from './contact.component';
import { ContactService } from './contact.service';
import { ContactRoutingModule } from './contact-routing.module';
// #docregion class
@NgModule({
imports: [
SharedModule,
ContactRoutingModule
],
declarations: [ ContactComponent ],
providers: [ ContactService ]
})
export class ContactModule { }
// #enddocregion class

View File

@ -1,37 +0,0 @@
// #docplaster
// #docregion
import { Injectable, OnDestroy } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
export class Contact {
constructor(public id: number, public name: string) { }
}
const CONTACTS: Contact[] = [
new Contact(21, 'Sam Spade'),
new Contact(22, 'Nick Danger'),
new Contact(23, 'Nancy Drew')
];
const FETCH_LATENCY = 500;
/** Simulate a data service that retrieves contacts from a server */
@Injectable()
export class ContactService implements OnDestroy {
// #enddocregion
constructor() { console.log('ContactService instance created.'); }
ngOnDestroy() { console.log('ContactService instance destroyed.'); }
// #docregion
getContacts(): Observable<Contact[]> {
return of(CONTACTS).pipe(delay(FETCH_LATENCY));
}
getContact(id: number | string): Observable<Contact> {
return of(CONTACTS.find(contact => contact.id === +id))
.pipe(delay(FETCH_LATENCY));
}
}
// #enddocregion

View File

@ -1,48 +0,0 @@
/* tslint:disable:member-ordering no-unused-variable */
// #docplaster
// #docregion
// #docregion v4
import {
ModuleWithProviders, NgModule,
Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TitleComponent } from './title.component';
import { UserService } from './user.service';
// #enddocregion
import { UserServiceConfig } from './user.service';
// #docregion v4
@NgModule({
imports: [ CommonModule ],
declarations: [ TitleComponent ],
exports: [ TitleComponent ],
providers: [ UserService ]
})
export class CoreModule {
// #enddocregion v4
// #docregion ctor
constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
if (parentModule) {
throw new Error(
'CoreModule is already loaded. Import it in the AppModule only');
}
}
// #enddocregion ctor
// #docregion for-root
static forRoot(config: UserServiceConfig): ModuleWithProviders {
return {
ngModule: CoreModule,
providers: [
{provide: UserServiceConfig, useValue: config }
]
};
}
// #enddocregion for-root
// #docregion v4
}
// #enddocregion v4
// #enddocregion

View File

@ -1,6 +0,0 @@
<!-- Exact copy from earlier app.component.html -->
<h1 highlight>{{title}}</h1>
<p *ngIf="user">
<i>Welcome, {{user}}</i>
<p>

View File

@ -1,16 +0,0 @@
// Exact copy of app/title.component.ts except import UserService from shared
import { Component, Input } from '@angular/core';
import { UserService } from '../core/user.service';
@Component({
selector: 'app-title',
templateUrl: './title.component.html',
})
export class TitleComponent {
title = 'Angular Modules';
user = '';
constructor(userService: UserService) {
this.user = userService.userName;
}
}

View File

@ -1,32 +0,0 @@
// Crazy copy of the app/user.service
// Proves that UserService is an app-wide singleton and only instantiated once
// IFF shared.module follows the `forRoot` pattern
//
// If it didn't, a new instance of UserService would be created
// after each lazy load and the userName would double up.
import { Injectable, Optional } from '@angular/core';
let nextId = 1;
export class UserServiceConfig {
userName = 'Philip Marlowe';
}
@Injectable()
export class UserService {
id = nextId++;
private _userName = 'Sherlock Holmes';
// #docregion ctor
constructor(@Optional() config: UserServiceConfig) {
if (config) { this._userName = config.userName; }
}
// #enddocregion ctor
get userName() {
// Demo: add a suffix if this service has been created more than once
const suffix = this.id > 1 ? ` times ${this.id}` : '';
return this._userName + suffix;
}
}

View File

@ -1,19 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
template: `
<h3 highlight>Crisis Detail</h3>
<div>Crisis id: {{id}}</div>
<br>
<a routerLink="../list">Crisis List</a>
`
})
export class CrisisDetailComponent implements OnInit {
id: number;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.id = parseInt(this.route.snapshot.paramMap.get('id'), 10);
}
}

View File

@ -1,21 +0,0 @@
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { Crisis,
CrisisService } from './crisis.service';
@Component({
template: `
<h3 highlight>Crisis List</h3>
<div *ngFor='let crisis of crises | async'>
<a routerLink="{{'../' + crisis.id}}">{{crisis.id}} - {{crisis.name}}</a>
</div>
`
})
export class CrisisListComponent {
crises: Observable<Crisis[]>;
constructor(private crisisService: CrisisService) {
this.crises = this.crisisService.getCrises();
}
}

View File

@ -1,18 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
import { CrisisListComponent } from './crisis-list.component';
import { CrisisDetailComponent } from './crisis-detail.component';
const routes: Routes = [
{ path: '', redirectTo: 'list', pathMatch: 'full'},
{ path: 'list', component: CrisisListComponent },
{ path: ':id', component: CrisisDetailComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class CrisisRoutingModule {}

View File

@ -1,14 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CrisisListComponent } from './crisis-list.component';
import { CrisisDetailComponent } from './crisis-detail.component';
import { CrisisService } from './crisis.service';
import { CrisisRoutingModule } from './crisis-routing.module';
@NgModule({
imports: [ CommonModule, CrisisRoutingModule ],
declarations: [ CrisisDetailComponent, CrisisListComponent ],
providers: [ CrisisService ]
})
export class CrisisModule {}

View File

@ -1,33 +0,0 @@
import { Injectable, OnDestroy } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
export class Crisis {
constructor(public id: number, public name: string) { }
}
const CRISES: Crisis[] = [
new Crisis(1, 'Dragon Burning Cities'),
new Crisis(2, 'Sky Rains Great White Sharks'),
new Crisis(3, 'Giant Asteroid Heading For Earth'),
new Crisis(4, 'Procrastinators Meeting Delayed Again'),
];
const FETCH_LATENCY = 500;
/** Simulate a data service that retrieves crises from a server */
@Injectable()
export class CrisisService implements OnDestroy {
constructor() { console.log('CrisisService instance created.'); }
ngOnDestroy() { console.log('CrisisService instance destroyed.'); }
getCrises(): Observable<Crisis[]> {
return of(CRISES).pipe(delay(FETCH_LATENCY));
}
getCrisis(id: number | string): Observable<Crisis> {
return of(CRISES.find(crisis => crisis.id === +id))
.pipe(delay(FETCH_LATENCY));
}
}

View File

@ -1,31 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Hero,
HeroService } from './hero.service';
@Component({
template: `
<h3 highlight>Hero Detail</h3>
<div *ngIf="hero">
<div>Id: {{hero.id}}</div><br>
<label>Name:
<input [(ngModel)]="hero.name">
</label>
</div>
<br>
<a routerLink="../">Hero List</a>
`
})
export class HeroDetailComponent implements OnInit {
hero: Hero;
constructor(
private route: ActivatedRoute,
private heroService: HeroService) { }
ngOnInit() {
let id = parseInt(this.route.snapshot.paramMap.get('id'), 10);
this.heroService.getHero(id).subscribe(hero => this.hero = hero);
}
}

View File

@ -1,20 +0,0 @@
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { Hero,
HeroService } from './hero.service';
@Component({
template: `
<h3 highlight>Hero List</h3>
<div *ngFor='let hero of heroes | async'>
<a routerLink="{{hero.id}}">{{hero.id}} - {{hero.name}}</a>
</div>
`
})
export class HeroListComponent {
heroes: Observable<Hero[]>;
constructor(private heroService: HeroService) {
this.heroes = this.heroService.getHeroes();
}
}

View File

@ -1,23 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
import { HeroComponent } from './hero.component.3';
import { HeroListComponent } from './hero-list.component';
import { HeroDetailComponent } from './hero-detail.component';
const routes: Routes = [
{ path: '',
component: HeroComponent,
children: [
{ path: '', component: HeroListComponent },
{ path: ':id', component: HeroDetailComponent }
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HeroRoutingModule {}

View File

@ -1,23 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
import { HeroComponent } from './hero.component';
import { HeroListComponent } from './hero-list.component';
import { HeroDetailComponent } from './hero-detail.component';
const routes: Routes = [
{ path: '',
component: HeroComponent,
children: [
{ path: '', component: HeroListComponent },
{ path: ':id', component: HeroDetailComponent }
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HeroRoutingModule {}

View File

@ -1,18 +0,0 @@
import { Component } from '@angular/core';
import { HeroService } from './hero.service';
import { UserService } from '../user.service';
@Component({
template: `
<h2>Heroes of {{userName}}</h2>
<router-outlet></router-outlet>
`,
providers: [ HeroService ]
})
export class HeroComponent {
userName = '';
constructor(userService: UserService) {
this.userName = userService.userName;
}
}

View File

@ -1,19 +0,0 @@
// Exact copy except import UserService from core
import { Component } from '@angular/core';
import { HeroService } from './hero.service';
import { UserService } from '../core/user.service';
@Component({
template: `
<h2>Heroes of {{userName}}</h2>
<router-outlet></router-outlet>
`,
providers: [ HeroService ]
})
export class HeroComponent {
userName = '';
constructor(userService: UserService) {
this.userName = userService.userName;
}
}

View File

@ -1,21 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HeroComponent } from './hero.component.3';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroListComponent } from './hero-list.component';
import { HeroRoutingModule } from './hero-routing.module.3';
import { HighlightDirective } from './highlight.directive';
// #docregion class
@NgModule({
imports: [ CommonModule, FormsModule, HeroRoutingModule ],
declarations: [
HeroComponent, HeroDetailComponent, HeroListComponent,
HighlightDirective
]
})
export class HeroModule { }
// #enddocregion class

View File

@ -1,16 +0,0 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { HeroComponent } from './hero.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroListComponent } from './hero-list.component';
import { HeroRoutingModule } from './hero-routing.module';
@NgModule({
imports: [ SharedModule, HeroRoutingModule ],
declarations: [
HeroComponent, HeroDetailComponent, HeroListComponent,
]
})
export class HeroModule { }

View File

@ -1,36 +0,0 @@
import { Injectable, OnDestroy } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
export class Hero {
constructor(public id: number, public name: string) { }
}
const HEROES: Hero[] = [
new Hero(11, 'Mr. Nice'),
new Hero(12, 'Narco'),
new Hero(13, 'Bombasto'),
new Hero(14, 'Celeritas'),
new Hero(15, 'Magneta'),
new Hero(16, 'RubberMan')
];
const FETCH_LATENCY = 500;
/** Simulate a data service that retrieves heroes from a server */
@Injectable()
export class HeroService implements OnDestroy {
constructor() { console.log('HeroService instance created.'); }
ngOnDestroy() { console.log('HeroService instance destroyed.'); }
getHeroes(): Observable<Hero[]> {
return of(HEROES).pipe(delay(FETCH_LATENCY));
}
getHero(id: number | string): Observable<Hero> {
return of(HEROES.find(hero => hero.id === +id))
.pipe(delay(FETCH_LATENCY));
}
}

View File

@ -1,10 +0,0 @@
// Exact copy of contact.awesome.pipe
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'awesome' })
/** Precede the input string with the word "Awesome " */
export class AwesomePipe implements PipeTransform {
transform(phrase: string) {
return phrase ? 'Awesome ' + phrase : '';
}
}

View File

@ -1,12 +0,0 @@
// Exact copy of contact/highlight.directive except for color and message
import { Directive, ElementRef } from '@angular/core';
@Directive({ selector: '[highlight], input' })
// Highlight the host element or any InputElement in gray
export class HighlightDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'lightgray';
console.log(
`* Shared highlight called for ${el.nativeElement.tagName}`);
}
}

View File

@ -1,18 +0,0 @@
// #docregion
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { AwesomePipe } from './awesome.pipe';
import { HighlightDirective } from './highlight.directive';
// #docregion module
@NgModule({
imports: [ CommonModule ],
declarations: [ AwesomePipe, HighlightDirective ],
exports: [ AwesomePipe, HighlightDirective,
CommonModule, FormsModule ]
})
export class SharedModule { }
// #enddocregion module
// #enddocregion

View File

@ -1,8 +0,0 @@
// #docregion
import { Injectable } from '@angular/core';
@Injectable()
/** Dummy version of an authenticated user service */
export class UserService {
userName = 'Sherlock Holmes';
}

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<title>NgModule Deluxe</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -1,13 +0,0 @@
// #docplaster
/*
// #docregion
// The browser platform without a compiler
import { platformBrowser } from '@angular/platform-browser';
// The app module factory produced by the static offline compiler
import { AppModuleNgFactory } from './app/app.module.ngfactory';
// Launch with the app module factory.
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
// #enddocregion
*/

View File

@ -1,11 +0,0 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module.0';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -1,11 +0,0 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module.1';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -1,11 +0,0 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module.1b';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -1,11 +0,0 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module.2';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -1,11 +0,0 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module.3';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -1,40 +0,0 @@
{
"description": "NgModule Final",
"files": [
"src/app/app.component.ts",
"src/app/app.module.ts",
"src/app/app-routing.module.ts",
"src/app/contact/contact.component.css",
"src/app/contact/contact.component.html",
"src/app/contact/contact.service.ts",
"src/app/contact/contact.component.ts",
"src/app/contact/contact.module.ts",
"src/app/contact/contact-routing.module.ts",
"src/app/crisis/*.ts",
"src/app/hero/hero-detail.component.ts",
"src/app/hero/hero-list.component.ts",
"src/app/hero/hero.service.ts",
"src/app/hero/hero.component.ts",
"src/app/hero/hero.module.ts",
"src/app/hero/hero-routing.module.ts",
"src/app/core/*.css",
"src/app/core/*.html",
"src/app/core/*.ts",
"src/app/shared/*.css",
"src/app/shared/*.html",
"src/app/shared/*.ts",
"src/main.ts",
"src/styles.css",
"src/index.html"
],
"main": "src/index.html",
"tags": ["NgModule"]
}

View File

@ -5,8 +5,6 @@ import { browser, element, by } from 'protractor';
describe('NgModule-example', function () {
// helpers
const gold = 'rgba(255, 215, 0, 1)';
const powderblue = 'rgba(176, 224, 230, 1)';
const lightgray = 'rgba(239, 238, 237, 1)';
const white = 'rgba(0, 0, 0, 0)';
@ -15,7 +13,7 @@ describe('NgModule-example', function () {
return {
title: element.all(by.tagName('h1')).get(0),
subtitle: element.all(by.css('app-title p i')).get(0),
subtitle: element.all(by.css('app-root p i')).get(0),
contactButton: buttons.get(0),
itemButton: buttons.get(1),
customersButton: buttons.get(2)
@ -67,7 +65,7 @@ describe('NgModule-example', function () {
it('should welcome us', function () {
const commons = getCommonsSectionStruct();
expect(commons.subtitle.getText()).toBe('Welcome, ' + (name || 'Sherlock Holmes'));
expect(commons.subtitle.getText()).toBe('Welcome, ' + (name || 'Miss Marple'));
});
};
}
@ -76,7 +74,7 @@ describe('NgModule-example', function () {
return function() {
it('shows the contact\'s owner', function() {
const contacts = getContactSectionStruct();
expect(contacts.header.getText()).toBe('Contact of ' + (name || 'Sherlock Holmes'));
expect(contacts.header.getText()).toBe((name || 'Miss Marple') + '\'s Contacts');
});
it('can cycle between contacts', function () {
@ -92,21 +90,22 @@ describe('NgModule-example', function () {
});
});
it('can change an existing contact', function () {
const contacts = getContactSectionStruct();
contacts.input.sendKeys('a');
expect(contacts.input.getCssValue('backgroundColor')).toBe(color);
expect(contacts.contactNameHeader.getText()).toBe('Awesome Yashaa');
});
it('can create a new contact', function () {
const contacts = getContactSectionStruct();
const newContactButton = contacts.newContactButton;
const nextButton = contacts.nextContactButton;
const input = contacts.input;
const saveButton = contacts.saveButton;
newContactButton.click().then(function () {
expect(contacts.validationError.getText()).toBe('Name is required');
contacts.input.sendKeys('John Doe');
expect(contacts.contactNameHeader.getText()).toBe('Awesome John Doe');
expect(contacts.validationError.getText()).toBe('');
input.click();
nextButton.click()
expect(contacts.validationError.getText()).toBe('Name is required.');
input.click();
contacts.input.sendKeys('Watson');
saveButton.click()
expect(contacts.contactNameHeader.getText()).toBe('Awesome Watson');
});
});
};

View File

@ -3,7 +3,7 @@ import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-title></app-title>
<app-greeting></app-greeting>
<nav>
<a routerLink="contact" routerLinkActive="active">Contact</a>
<a routerLink="items" routerLinkActive="active">Items</a>

View File

@ -1,3 +1,5 @@
// #docplaster
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
@ -7,28 +9,30 @@ import { AppComponent } from './app.component';
/* Feature Modules */
import { ContactModule } from './contact/contact.module';
// #docregion import-for-root
import { CoreModule } from './core/core.module';
import { GreetingModule } from './greeting/greeting.module';
// #enddocregion import-for-root
/* Routing Module */
import { AppRoutingModule } from './app-routing.module';
// #docregion import-for-root
@NgModule({
imports: [
// #enddocregion import-for-root
BrowserModule,
ContactModule,
CoreModule.forRoot({userName: 'Miss Marple'}),
// #docregion import-for-root
GreetingModule.forRoot({userName: 'Miss Marple'}),
// #enddocregion import-for-root
AppRoutingModule
// #docregion import-for-root
],
// #enddocregion import-for-root
providers: [],
declarations: [
AppComponent
],
bootstrap: [AppComponent]
// #docregion import-for-root
})
export class AppModule { }
// #enddocregion import-for-root
export class AppModule { }

View File

@ -0,0 +1,17 @@
.button-group {
display: flex;
flex-direction: row;
}
button {
margin: 1rem 1rem 0 0;
}
input {
padding: .5rem;
margin-left: .5rem;
}
.alert {
color: red;
font-style: italic;
}

View File

@ -1,32 +1,30 @@
<h2>Contact of {{userName}}</h2>
<h2>{{userName}}'s Contacts</h2>
<div *ngIf="msg" class="msg">{{msg}}</div>
<form *ngIf="contacts" (ngSubmit)="onSubmit()" #contactForm="ngForm">
<form *ngIf="contacts" (ngSubmit)="onSubmit()" [formGroup]="contactForm">
<h3 highlight>{{ contact.name | awesome }}</h3>
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" required
[(ngModel)]="contact.name"
name="name" #name="ngModel" >
<div [hidden]="name.valid" class="alert alert-danger">
Name is required
<input type="text" formControlName="name" required>
<div *ngIf="!contactForm.controls['name'].valid && contactForm.controls['name'].touched" class="alert">
Name is required.
</div>
</div>
<div class="button-group">
<button type="submit" class="btn btn-default"
[disabled]="!contactForm.form.valid">
[disabled]="!contactForm.valid">
Save</button>
<button type="button" class="btn" (click)="next()"
[disabled]="!contactForm.form.valid">
[disabled]="!contactForm.valid && contactForm.touched">
Next Contact</button>
<button type="button" class="btn" (click)="newContact()">
New Contact</button>
</div>
</form>

View File

@ -1,8 +1,9 @@
// Exact copy except import UserService from core
// Exact copy except import UserService from greeting
import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { Contact, ContactService } from './contact.service';
import { UserService } from '../core/user.service';
import { UserService } from '../greeting/user.service';
@Component({
selector: 'app-contact',
@ -16,15 +17,24 @@ export class ContactComponent implements OnInit {
msg = 'Loading contacts ...';
userName = '';
constructor(private contactService: ContactService, userService: UserService) {
contactForm = this.fb.group({
name: ['', Validators.required]
});
constructor(private contactService: ContactService, userService: UserService, private fb: FormBuilder) {
this.userName = userService.userName;
}
ngOnInit() {
this.setupForm();
}
setupForm() {
this.contactService.getContacts().subscribe(contacts => {
this.msg = '';
this.contacts = contacts;
this.contact = contacts[0];
this.contactForm.get('name').setValue(this.contact.name);
});
}
@ -32,15 +42,18 @@ export class ContactComponent implements OnInit {
let ix = 1 + this.contacts.indexOf(this.contact);
if (ix >= this.contacts.length) { ix = 0; }
this.contact = this.contacts[ix];
console.log(this.contacts[ix]);
}
onSubmit() {
// POST-DEMO TODO: do something like save it
this.displayMessage('Saved ' + this.contact.name);
let newName = this.contactForm.get('name').value;
this.displayMessage('Saved ' + newName);
this.contact.name = newName;
}
newContact() {
this.displayMessage('New contact');
this.contactForm.get('name').setValue('');
this.contact = {id: 42, name: ''};
this.contacts.push(this.contact);
}
@ -51,4 +64,3 @@ export class ContactComponent implements OnInit {
setTimeout(() => this.msg = '', 1500);
}
}

View File

@ -1,5 +1,6 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { ReactiveFormsModule } from '@angular/forms';
import { ContactComponent } from './contact.component';
import { ContactService } from './contact.service';
@ -8,7 +9,8 @@ import { ContactRoutingModule } from './contact-routing.module';
@NgModule({
imports: [
SharedModule,
ContactRoutingModule
ContactRoutingModule,
ReactiveFormsModule
],
declarations: [ ContactComponent ],
providers: [ ContactService ]

View File

@ -1,46 +0,0 @@
// #docregion whole-core-module
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TitleComponent } from './title.component';
// #docregion user-service
import { UserService } from './user.service';
// #enddocregion user-service
import { UserServiceConfig } from './user.service';
// #docregion user-service
@NgModule({
// #enddocregion user-service
imports: [ CommonModule ],
declarations: [ TitleComponent ],
exports: [ TitleComponent ],
// #docregion user-service
providers: [ UserService ]
})
export class CoreModule {
// #enddocregion user-service
// #docregion ctor
constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
if (parentModule) {
throw new Error(
'CoreModule is already loaded. Import it in the AppModule only');
}
}
// #enddocregion ctor
// #docregion for-root
static forRoot(config: UserServiceConfig): ModuleWithProviders {
return {
ngModule: CoreModule,
providers: [
{provide: UserServiceConfig, useValue: config }
]
};
}
// #enddocregion for-root
// #docregion user-service
}
// #enddocregion user-service
// #enddocregion whole-core-module

View File

@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { CustomersService } from './customers.service';
import { UserService } from '../core/user.service';
import { UserService } from '../greeting/user.service';
@Component({
template: `

View File

@ -1,11 +1,11 @@
import { Component, Input } from '@angular/core';
import { UserService } from '../core/user.service';
import { UserService } from '../greeting/user.service';
@Component({
selector: 'app-title',
templateUrl: './title.component.html',
selector: 'app-greeting',
templateUrl: './greeting.component.html',
})
export class TitleComponent {
export class GreetingComponent {
title = 'NgModules';
user = '';

View File

@ -0,0 +1,36 @@
// #docregion whole-greeting-module
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { GreetingComponent } from './greeting.component';
import { UserServiceConfig } from './user.service';
@NgModule({
imports: [ CommonModule ],
declarations: [ GreetingComponent ],
exports: [ GreetingComponent ]
})
export class GreetingModule {
// #docregion ctor
constructor (@Optional() @SkipSelf() parentModule: GreetingModule) {
if (parentModule) {
throw new Error(
'GreetingModule is already loaded. Import it in the AppModule only');
}
}
// #enddocregion ctor
// #docregion for-root
static forRoot(config: UserServiceConfig): ModuleWithProviders {
return {
ngModule: GreetingModule,
providers: [
{provide: UserServiceConfig, useValue: config }
]
};
}
// #enddocregion for-root
}
// #enddocregion whole-greeting-module

View File

@ -1,8 +1,3 @@
// Proves that UserService is an app-wide singleton and only instantiated once
// IFF shared.module follows the `forRoot` pattern.
//
// If it didn't, a new instance of UserService would be created
// after each lazy load and the userName would double up.
import { Injectable, Optional } from '@angular/core';
@ -12,7 +7,9 @@ export class UserServiceConfig {
userName = 'Philip Marlowe';
}
@Injectable()
@Injectable({
providedIn: 'root'
})
export class UserService {
id = nextId++;
private _userName = 'Sherlock Holmes';

View File

@ -3,7 +3,9 @@
<ul class="items">
<li *ngFor="let hero of heroes$ | async">
<!-- #docregion nav-to-detail -->
<!-- #docregion link-parameters-array -->
<a [routerLink]="['/hero', hero.id]">
<!-- #enddocregion link-parameters-array -->
<span class="badge">{{ hero.id }}</span>{{ hero.name }}
</a>
<!-- #enddocregion nav-to-detail -->

View File

@ -28,9 +28,3 @@ export class HeroListComponent implements OnInit {
}
}
// #enddocregion
/* A link parameters array
// #docregion link-parameters-array
['/hero', hero.id] // { 15 }
// #enddocregion link-parameters-array
*/

View File

@ -15,7 +15,7 @@ const squareOddVals = pipe(
// Create an Observable that will run the filter and map functions
const squareOdd = squareOddVals(nums);
// Suscribe to run the combined functions
// Subscribe to run the combined functions
squareOdd.subscribe(x => console.log(x));
// #enddocregion

View File

@ -0,0 +1,3 @@
{
"projectType": "schematics"
}

View File

@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/my-lib",
"lib": {
"entryFile": "src/public_api.ts"
}
}

View File

@ -0,0 +1,20 @@
// #docplaster
// #docregion collection
{
"name": "my-lib",
"version": "0.0.1",
// #enddocregion collection
"scripts": {
"build": "../../node_modules/.bin/tsc -p tsconfig.schematics.json",
"copy:schemas": "cp --parents schematics/*/schema.json ../../dist/my-lib/",
"copy:files": "cp --parents -p schematics/*/files/** ../../dist/my-lib/",
"copy:collection": "cp schematics/collection.json ../../dist/my-lib/schematics/collection.json",
"postbuild": "npm run copy:schemas && npm run copy:files && npm run copy:collection"
},
"peerDependencies": {
"@angular/common": "^7.2.0",
"@angular/core": "^7.2.0"
},
// #docregion collection
"schematics": "./schematics/collection.json"
}

View File

@ -0,0 +1,9 @@
{
"$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"ng-add": {
"description": "Add my library to the project.",
"factory": "./ng-add/index#ngAdd"
}
}
}

View File

@ -0,0 +1,14 @@
{
"$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"ng-add": {
"description": "Add my library to the project.",
"factory": "./ng-add/index#ngAdd"
},
"my-service": {
"description": "Generate a service in the project.",
"factory": "./my-service/index#myService",
"schema": "./my-service/schema.json"
}
}
}

View File

@ -0,0 +1,10 @@
// #docregion template
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class <%= classify(name) %>Service {
constructor(private http: HttpClient) { }
}

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