Compare commits

...

94 Commits

Author SHA1 Message Date
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
276 changed files with 4047 additions and 3654 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:

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.

4
.github/CODEOWNERS vendored
View File

@ -432,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
@ -512,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
@ -627,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
@ -779,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

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,

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,25 @@
<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)

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

@ -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

@ -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) { }
}

View File

@ -0,0 +1,11 @@
import { Rule, Tree } from '@angular-devkit/schematics';
import { Schema as MyServiceSchema } from './schema';
// #docregion factory
export function myService(options: MyServiceSchema): Rule {
return (tree: Tree) => {
return tree;
};
}
// #enddocregion factory

View File

@ -0,0 +1,66 @@
// #docplaster
// #docregion schematics-imports, schema-imports, workspace
import {
Rule, Tree, SchematicsException,
apply, url, applyTemplates, move,
chain, mergeWith
} from '@angular-devkit/schematics';
import { strings, normalize, experimental } from '@angular-devkit/core';
// #enddocregion schematics-imports
import { Schema as MyServiceSchema } from './schema';
// #enddocregion schema-imports
export function myService(options: MyServiceSchema): Rule {
return (tree: Tree) => {
const workspaceConfig = tree.read('/angular.json');
if (!workspaceConfig) {
throw new SchematicsException('Could not find Angular workspace configuration');
}
// convert workspace to string
const workspaceContent = workspaceConfig.toString();
// parse workspace string into JSON object
const workspace: experimental.workspace.WorkspaceSchema = JSON.parse(workspaceContent);
// #enddocregion workspace
// #docregion project-fallback
if (!options.project) {
options.project = workspace.defaultProject;
}
// #enddocregion project-fallback
// #docregion project-info
const projectName = options.project as string;
const project = workspace.projects[projectName];
const projectType = project.projectType === 'application' ? 'app' : 'lib';
// #enddocregion project-info
// #docregion path
if (options.path === undefined) {
options.path = `${project.sourceRoot}/${projectType}`;
}
// #enddocregion path
// #docregion template
const templateSource = apply(url('./files'), [
applyTemplates({
classify: strings.classify,
dasherize: strings.dasherize,
name: options.name
}),
move(normalize(options.path as string))
]);
// #enddocregion template
// #docregion chain
return chain([
mergeWith(templateSource)
]);
// #enddocregion chain
// #docregion workspace
};
}

View File

@ -0,0 +1,28 @@
{
"$schema": "http://json-schema.org/schema",
"id": "SchematicsMyService",
"title": "My Service Schema",
"type": "object",
"properties": {
"name": {
"description": "The name of the service.",
"type": "string"
},
"path": {
"type": "string",
"format": "path",
"description": "The path to create the service.",
"visible": false
},
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "projectName"
}
}
},
"required": [
"name"
]
}

View File

@ -0,0 +1,10 @@
export interface Schema {
// The name of the service.
name: string;
// The path to create the service.
path?: string;
// The name of the project.
project?: string;
}

View File

@ -0,0 +1,10 @@
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
// Just return the tree
export function ngAdd(_options: any): Rule {
return (tree: Tree, _context: SchematicContext) => {
_context.addTask(new NodePackageInstallTask());
return tree;
};
}

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