Compare commits

...

266 Commits
7.0.2 ... 5.2.7

Author SHA1 Message Date
a10bf34471 docs: add changelog for 5.2.7 2018-02-28 15:01:38 -08:00
38146a2cd1 release: cut the 5.2.7 release 2018-02-28 15:00:20 -08:00
eccce1772d docs: fix community tab in GitHub by copying CoC 2018-02-27 19:04:00 -08:00
4aef9de37e fix(upgrade): correctly destroy nested downgraded component (#22400)
Previously, when a downgraded component was destroyed in a way that did
not trigger the `$destroy` event on the element (e.g. when a parent
element was removed from the DOM by Angular, not AngularJS), the
`ComponentRef` was not destroyed and unregistered.
This commit fixes it by listening for the `$destroy` event on both the
element and the scope.

Fixes #22392

PR Close #22400
2018-02-27 18:41:03 -08:00
f2fa7a289f docs(aio): add Observable and Rx docs (#21423)
PR Close #21423
2018-02-27 11:24:31 -08:00
e1fbe20d98 style(aio): updated padding-right for the .alert class in _heading-anchors.scss (#22431)
The h3 element is overflowing over its surrounding div element. Modified padding-right to align consistently with the remainder of div contents.

fixes: #22407

PR Close #22431
2018-02-26 17:52:30 -08:00
38bd8d49a5 style(aio): added padding-left to h3 in _subsection.scss (#22431)
The h3 element is overflowing over its surrounding div element. Modified padding-left to align consistently with the remainder of div contents.

fixes: #22407

PR Close #22431
2018-02-26 17:52:30 -08:00
d033106adb docs: update i18n guide for projects that don't use the cli (#21767)
PR Close #21767
2018-02-26 17:51:58 -08:00
de02a7a5de fix(platform-server): generate correct stylings for camel case names (#22263)
* Add correct mapping from camel case to kebab case for CSS style
names
* Remove internal CSS methods in favor of native Domino APIs

Fixes #19235

PR Close #22263
2018-02-26 17:46:21 -08:00
c30a942329 docs: testing guide for CLI (#20697)
- updates tests
- heavy prose revisions
- uses HttpClient (with angular-in-memory-web-api)
- test HeroService using `HttpClientTestingModule`
- scrub away most By.CSS
- fake async observable with `asyncData()`
- extensive Twain work
- different take on retryWhen
- remove app barrels (& systemjs.extras) which troubled plunker/systemjs
- add dummy export const to hero.ts (plunkr/systemjs fails w/o it)
- shrink and re-organize TOC
- add marble testing package and tests
- demonstrate the "no beforeEach()" test coding style
- add section on Http service testing
- prepare for stackblitz
- confirm works in plunker except excluded marble test
- add tests for avoidFile class feature of CodeExampleComponent

PR Close #20697
2018-02-26 13:40:24 -08:00
2a38d93171 docs(aio): fix doc typo referring to httpOptions (#22456)
The variable name mention should match the actual tutorial code.

PR Close #22456
2018-02-26 13:32:44 -08:00
a9a0e27e94 fix(upgrade): fix empty transclusion content with AngularJS@>=1.5.8 (#22167)
The function provided by `ngUpgrade` as `parentBoundTranscludeFn` when
upgrading a component with transclusion, will break in AngularJS v1.5.8+
if no transclusion content is provided. The reason is that AngularJS
will try to destroy the transclusion scope (which would not be needed
any more). But since the transcluded content comes from Angular, not
AngularJS, there is no transclusion scope to destroy.
This commit fixes it by providing a dummy scope object with a no-op
`$destroy()` method.

Fixes #22175

PR Close #22167
2018-02-25 10:06:14 -08:00
66383901a6 fix(upgrade): correctly handle = bindings in @angular/upgrade (#22167)
Previously, having a `=` binding on an upgraded components would result
in setting the corresponding property to an EventEmitter function. This
should only happen for `&` bindings.
This commit rstrores the correct behavior.

Note:
The issue was only present in the dynamic version of `ngUpgrade`. The
static version worked as expected.
The error did not show up in tests, because in AngularJS v1.5.x a
function would be serialized to an empty string in interpolations, thus
making them indistinguishable from uninitialized properties (in the
view). The serialization behavior changed in AngularJS v1.6.x, making
the errors visible.

PR Close #22167
2018-02-25 10:06:14 -08:00
1eb54132e4 test(upgrade): run tests against multiple AngularJS versions (#22167)
Fixes #19332

PR Close #22167
2018-02-25 10:06:14 -08:00
6c9c173e1e refactor(upgrade): use correct paths for imports (#22167)
`packages/upgrade/static/src` is anymlink to `packages/upgrade/src`.
Still, using the correct paths (e.g. using
`@angular/upgrade/static/src/...` for `@angula/upgrade/static` specs
ensures that the module loader (e.g. SystemJS) can map the imports to
the same instances.

PR Close #22167
2018-02-25 10:06:14 -08:00
1e08a945e1 test(platform-browser): remove stray debugger statement (#22167)
PR Close #22167
2018-02-25 10:06:14 -08:00
4a08745d3e build: add support for the "merge-assistance" label in merge-pr (#22414)
fixes #22256

PR Close #22414
2018-02-23 12:58:30 -08:00
cf91906d8f docs(aio): Essential JS 2 url updated (#19739)
PR Close #19739
2018-02-23 11:18:12 -08:00
0723c04a01 docs(aio): Essential JS 2 UI Components. (#19739)
PR Close #19739
2018-02-23 11:18:12 -08:00
2b7188906b build: fix 5.2.x merge (#22408)
PR Close #22408
2018-02-23 10:21:33 -08:00
17c1577de9 ci: don't use bazel git_repository rule (#22406)
It's currently broken on CircleCI because of a TLS change made by GitHub.
This is okay as a permanent change, we don't really want bazel to fetch a full git history.

Fixes #22405

PR Close #22406
2018-02-23 09:41:11 -08:00
150bac310f docs: fix deployment sample path (#22048)
PR Close #22048
2018-02-22 13:40:57 -08:00
8f0a0641e2 fix(router): don't mutate route configs (#22358)
Fixes #22203

PR Close #22358
2018-02-22 13:35:38 -08:00
17762390c9 build: disable bazel-out symlink (#22375)
It causes headaches on MacOS High Sierra, see https://github.com/bazelbuild/bazel/issues/4603

PR Close #22375
2018-02-22 13:33:12 -08:00
da1b4d5ea7 docs: fix ngmodules-jsmodules pre-req (#22316)
closes #22157

PR Close #22316
2018-02-22 11:20:48 -08:00
aa100f69f2 docs: edit styleguide recommendation on components as elements (#22074)
Change recommendation on using attributes for components since there are use cases including the use of <button mat-button> in MD

Closes #19401.

PR Close #22074
2018-02-22 11:20:21 -08:00
9cca5a8c9c build: allow passing node options to ngc. (#22245)
PR Close #22245
2018-02-22 10:23:54 -08:00
7c3b95b4ab docs: add changelog for 5.2.6 2018-02-21 16:45:10 -08:00
e4e8a68c06 release: cut the 5.2.6 release 2018-02-21 16:45:09 -08:00
6460ac0add Revert "feat(platform-browser): fix #19604, can config hammerOptions (#21979)"
This reverts commit fdbfd21bcd.
2018-02-21 16:45:09 -08:00
ee91de9d5a feat(core): support metadata reflection for native class types (#22356)
closes #21731

PR Close #22356
2018-02-21 16:09:27 -08:00
5ec38f2f47 fix(core): properly handle function without prototype in reflector (#22284)
closes #19978

PR Close #22284
2018-02-21 14:52:05 -08:00
612cfeca14 docs(aio): updates directive event hooks real capabilities (#16654)
Minor documentation update to include event hooks that were assumed to only work on components.

Closes angular/angular#10221

PR Close #16654
2018-02-21 14:51:05 -08:00
dfdade25ea docs(aio): Wrong code example. Form status field was added later in the guide. (#21275)
PR Close #21275
2018-02-21 11:06:48 -08:00
c2f78e1ca3 docs(http): fix a typo in code comment (#22327)
PR Close #22327
2018-02-21 11:06:06 -08:00
484802cd2a build: make git revert messages valid (#22339)
`git revert` default message is "Revert <original message>" (no semi-colon)

PR Close #22339
2018-02-21 11:05:35 -08:00
ee535777bb docs: add ngStyle to cheat sheet (#22070)
PR Close #22070
2018-02-20 16:08:16 -08:00
94756eb4bd docs(aio): fix incorrect quote mark usage (#22335)
PR Close #22335
2018-02-20 15:42:55 -08:00
23b0707707 docs(aio): fix the css of the heroes component's buttons (#22333)
Fixes #22222

PR Close #22333
2018-02-20 15:41:57 -08:00
a2cb0109f1 docs(aio): Fix name of component (#22332)
PR Close #22332
2018-02-20 15:41:35 -08:00
d20a08bc48 docs(aio): update installed mobile tool list (#22331)
PR Close #22331
2018-02-20 15:41:15 -08:00
5bdb3acace build: update tsickle dep from compiler-cli (#22295)
PR Close #22295
2018-02-20 15:40:45 -08:00
c5418c7abe fix(compiler-cli): add missing entry point to package, update tsickle (#22295)
PR Close #22295
2018-02-20 15:40:45 -08:00
09b4612bdd docs(aio): add Nx and Angular Enterprise Playbook to resources (#22321)
PR Close #22321
2018-02-20 10:09:34 -08:00
a346d28df6 test(language-service): fix minor typos (#21372)
PR Close #21372
2018-02-20 10:08:55 -08:00
bf07837d5d test(common): fix ngIf tests
The failing test was ported for the master branch which ignores whitespaces
2018-02-18 20:12:47 -08:00
cdfedc1e49 fix(common): fix merge error in ng_if.ts 2018-02-18 19:51:15 -08:00
af6a0563de fix(common): then and else template might be set to null (#22298)
PR Close #22298
2018-02-18 19:28:36 -08:00
c726d1d6d3 docs(aio): add angular-buch to resources (#22163)
adds a link to the website of our book. second version of the text. thanks!

PR Close #22163
2018-02-18 15:12:14 -08:00
2030846df7 docs(aio): add angular-buch to resources (#22163)
adds a link to the website of our book. many thanks for reviewing this

PR Close #22163
2018-02-18 15:12:14 -08:00
9dae97c5d9 docs: correct grammar mistakes in CONTRIBUTING.md (#22285)
Various grammar mistakes were present in the contribution guidelines
This commit corrects some of them

PR Close #22285
2018-02-18 13:27:24 -08:00
228eb9feef fix(aio): improve announcement-bar layout with wide logos (#22272)
PR Close #22272
2018-02-18 13:16:30 -08:00
debf01d7a6 docs(aio): added ngconf announcement (#22272)
PR Close #22272
2018-02-18 13:16:30 -08:00
51abe69b60 fix: merge-pr script (#22290)
PR Close #22290
2018-02-18 13:13:29 -08:00
396bc0d9e9 build: use authenticated mode for the merge script (#22269)
`TOKEN` is the name with use for other GH scripts

PR Close #22269
2018-02-18 13:01:51 -08:00
861250b4e2 docs: fix changelog errors (#22228)
PR Close #22228
2018-02-16 18:03:04 -08:00
81c1e0a3c3 docs: replace plnkr with StackBlitz (#20365)
PR Close #20365
2018-02-16 15:12:10 -08:00
ce5e8fad9e fix(common): correct mapping of Observable methods (#20518)
fixes #20516
PR Close #20518
2018-02-16 15:10:31 -08:00
185a6ab562 build: add esm5 build (#22258)
This is a partial cherry-pick of 370ab66c4f
which included this along with a new feature for ivy.

PR Close #22258
2018-02-16 14:49:24 -08:00
6b457843b9 test(aio): increase docs-test timeouts to prevent flakes on Travis (#22261)
PR Close #22261
2018-02-16 14:46:23 -08:00
5f52ea3d06 feat(bazel): ng_module produces bundle index (#22176)
It creates the bundle index .d.ts and .metadata.json files.
The names are based on the ng_module target.

PR Close #22176
2018-02-15 14:17:16 -08:00
6c1e7ac40e feat(bazel): introduce a binary stamping feature (#22176)
This grabs version control metadata and makes it available in the build, eg. to put in the version field for released artifacts

PR Close #22176
2018-02-15 14:08:54 -08:00
6597616aac refactor(bazel): convert most ts_library to ng_module (#22176)
This is necessary so we can produce ng metadata for our packages that are published as libraries

PR Close #22176
2018-02-15 14:08:54 -08:00
6a57264d38 Revert: "build: allow bazel build ... (#22168)"
This reverts commit 3237f1dbfc.
2018-02-15 14:07:41 -08:00
1e3e0fad49 fix(aio): improve printing styles (#19651)
printfix

PR Close #19651
2018-02-14 18:49:59 -05:00
0c88d5dedd style: fix typos boostrap to bootstrap (#21917)
PR Close #21917
2018-02-14 18:21:52 -05:00
0b8b06ee8b build: comment-out chromium version checking code temporarily (#22232)
Related #22231

PR Close #22232
2018-02-14 17:26:43 -05:00
Pat
edd6cd4e29 docs: typo - components should be possessive (#22172)
PR Close #22172
2018-02-14 15:06:52 -05:00
fdbfd21bcd feat(platform-browser): fix #19604, can config hammerOptions (#21979)
PR Close #21979
2018-02-14 15:02:59 -05:00
0a5283da1a docs(aio): fix extraneous divs (#22069)
PR Close #22069
2018-02-14 15:02:36 -05:00
3237f1dbfc build: allow bazel build ... (#22168)
Note, the reason this commit removes `firebase-tools` is:

1) firebase-tools has an optional dependency on
https://www.npmjs.com/package/@google-cloud/functions-emulator
2) yarn's `--ignore-optional` doesn't work for transitive deps, so
there's no way to yarn install without getting that functions-emulator
package
3) functions-emulator has a transitive dep on `grpc`
4) the version of `grpc` we get has `BUILD` files and no `WORKSPACE`
file so it always breaks `bazel build ...`

It could be solved by any of:
1) remove firebase-tools - this is what I did
2) fix yarn so you can omit optional deps of a transitive dep
3) make functions-emulator depend transitively on a more recent `grpc`
version
4) patch `grpc` after install by doing an `rm` command in our
postinstall or something

In its place we must install protobufjs. This is needed by the
ngc-wrapped test, which needs jasmine as well as bazel's worker mode
dependencies, and therefore cannot simply rely on
node_modules =
"@build_bazel_rules_typescript_tsc_wrapped_deps//:node_modules"

PR Close #22168
2018-02-14 15:01:42 -05:00
106b435297 docs: add changelog for 5.2.5 2018-02-13 21:07:53 -08:00
ca69dfde31 release: cut the 5.2.5 release 2018-02-13 21:06:54 -08:00
1b8ea1005a fix(compiler): make unary plus operator consistent to JavaScript (#22154)
fixes #22089

PR Close #22154
2018-02-13 13:04:30 -08:00
da6ab91095 fix(platform-browser): support 0/false/null values in transfer_state (#22179)
Issue #22178

PR Close #22179
2018-02-13 11:28:22 -08:00
c4f841f031 fix(core): add stacktrace in log when error during cleanup component in TestBed (#22162)
PR Close #22162
2018-02-13 11:28:08 -08:00
e576b69d10 ci: update ngbot config file (#22173)
Fixes #22053
PR Close #22173
2018-02-13 10:26:06 -08:00
f938774748 ci: remove conditional clause for bazel install (#22170)
No longer needed since we don't have a bazel job
PR Close #22170
2018-02-13 10:25:51 -08:00
c0b78067c4 ci: remove bazel job from Travis (#22170)
This saves us an executor on Travis.

Note that we still do a bazel build on travis when we run the integration tests under e2e_2.

We expect that CircleCI is the only place we'll ever consume bazel-built artifacts.

PR Close #22170
2018-02-13 10:25:51 -08:00
2931018b84 ci: enable bazel remote caching on CircleCI (#21784)
This should cause Bazel builds to be incremental, only re-building parts of Angular affected by changes since the last build.
It also fixes a potential version skew, where CI was running the Bazel linter binaries in the ngcontainer docker image, but developers built them using the versions in WORKSPACE

PR Close #21784
2018-02-13 10:14:22 -08:00
e97afae67a docs(aio): fix typo in "preserveWhitespaces" example (#22182)
Fixes #22147

PR Close #22182
2018-02-12 15:57:42 -08:00
c6bdc831c1 fix(common): weaken AsyncPipe transform signature (#22169)
The AsyncPipe type signature was changed to allow
deferred creation of promises and observalbes that
is supported by the implementation by allowing
`Promise<T>|null|undefined` and by allowing
`Observable<T>|null|undefined`.

PR Close #22169
2018-02-12 15:57:29 -08:00
1a897e4f48 docs(aio): add angular-playground to resources (#22042)
PR Close #22042
2018-02-12 14:30:58 -08:00
603e50d3bf refactor(router): move activation to private method (#22144)
PR Close #22144
2018-02-12 10:41:18 -08:00
ca5b72461c docs(aio): add angular.schule to resources (#22164)
adds a link to our website. many thanks for reviewing this

PR Close #22164
2018-02-12 10:01:23 -08:00
a616dd6c17 fix(aio): remove broken span closing tag (#22146)
PR Close #22146
2018-02-12 10:01:10 -08:00
0ed64af5bc docs(aio): put structural directives back in the nav (#21856)
PR Close #21856
2018-02-12 10:00:14 -08:00
d57fd0b84f fix(bazel): allow TS to read ambient typings (#21876)
Same fix as e70d7a2a7c
This is because the CompilerOptions needs to have directoryExists undefined in order to get the google3 behavior,
so we have to set the property outside the constructor.

Fixes #21872

PR Close #21876
2018-02-09 17:16:25 -08:00
9b280eef63 build: merge-pr new checks that all requested changes have been addressed (#21817)
PR Close #21817
2018-02-09 17:14:17 -08:00
d4a9db2af5 fix(aio): do not rewrite /styleguide URL in Service Worker (#22104)
This URL needs to be redirected via the server, so
we must exclude it from being rewitten.

Closes #22078

PR Close #22104
2018-02-09 13:10:48 -08:00
2740b69023 docs(aio): update docs changelog with links to ts-to-js guide (#21763) (#22104)
PR Close #21763

PR Close #22104
2018-02-09 13:10:48 -08:00
15ff7ba880 fix(aio): update Firebase redirects and SW routes (#21763) (#22104)
Closes #21377

PR Close #21763

PR Close #22104
2018-02-09 13:10:48 -08:00
615bb95138 build(aio): test Service Worker "routing" configuration (#21763) (#22104)
PR Close #21763

PR Close #22104
2018-02-09 13:10:48 -08:00
2b67400652 build(aio): test Firebase hosting redirection configuration (#21763) (#22104)
PR Close #21763

PR Close #22104
2018-02-09 13:10:48 -08:00
ac815f7281 docs: fix typo in http.md (#22058)
PR Close #22058
2018-02-09 13:10:24 -08:00
854f0ff33a feat(aio): enable data driven homepage announcements (#22043)
PR Close #22043
2018-02-09 13:10:12 -08:00
c55523466f docs(aio): several fix for ngmodule guides (#21517)
PR Close #21517
2018-02-09 13:03:47 -08:00
f80b9da18a Revert "docs(common): add HttpParamsOptions to the public API (#20332)"
This reverts commit de561f36e1.
2018-02-08 14:38:13 -08:00
c5ec8d952a fix(bazel): improve error message for missing assets (#22096)
fixes #22095

PR Close #22096
2018-02-08 10:01:27 -08:00
de561f36e1 docs(common): add HttpParamsOptions to the public API (#20332)
Fixes #20276

PR Close #20332
2018-02-08 09:44:37 -08:00
47b71d98ae fix(core): use appropriate inert document strategy for Firefox & Safari (#17019)
Both Firefox and Safari are vulnerable to XSS if we use an inert document
created via `document.implementation.createHTMLDocument()`.

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

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

Thanks to @cure53 for the heads up on this issue.

PR Close #17019
2018-02-08 08:55:15 -08:00
22d548f4ed docs(aio): remove ngATL banner from homepage (#22060)
Closes #22029

PR Close #22060
2018-02-07 16:10:18 -08:00
799edd1e2f docs(aio): remove lifecycle hooks img (#21425)
PR Close #21425
2018-02-07 16:09:44 -08:00
ffcb5dd264 build(aio): update examples to CLI to 1.6.5 (#21222)
PR Close #21222
2018-02-07 16:09:27 -08:00
75897d4977 feat(aio): report logger.error calls to Google Analytics (#22011)
We have a number of observables that have `catch` handlers to recover
from errors without causing the stream to close, and breaking the app.
We also have some `try ... catch` blocks for synchronous code for a
similar reason.

In these cases we conventionally then call `logger.error` in the catch
handler. We are interested in these errors so we are going to capture them
by reporting them to Google Analytics via the new `ReportingErrorHandler`.

PR Close #22011
2018-02-07 12:09:38 -08:00
eb0da530a7 feat(aio): report application errors to Google Analytics (#22011)
This is a basic implementation of error logging using the limited
facilities provided by Google Analytics.

Errors within the Angular app itself will be handled by a new
`ReportingErrorHandler` service, which overrides and extends the
built-in `ErrorHandler`.

Further, errors outside the app, which arrive at `window.onerror`
will also be reported to Google Analytics.

Closes #21943

PR Close #22011
2018-02-07 12:09:38 -08:00
0b23573573 fix(language-service): correct instructions to install the language service (#22000)
Fixes: #21956

PR Close #22000
2018-02-07 12:09:00 -08:00
47b73fd153 fix(core): ensure initial value of QueryList length (#21980) (#21982)
Set initial value of `length` to `0`.

Fixes regression introduced by e544742156 (diff-a85dbe0991a7577ea24b49374e9ae90b) where the `length` property ceased to have initial value.

Closes #21980

PR Close #21982
2018-02-07 12:08:44 -08:00
07769e5caa test(common): disable deprecated date pipe tests on chrome mobile (#21933)
Closes #21907
PR Close #21933
2018-02-07 12:07:31 -08:00
9fdb804b63 test(forms): update test name with correct wording (#21833)
Use the term primitive value instead of standalone

Fixes #21831

PR Close #21833
2018-02-07 12:07:15 -08:00
9d02db3254 fix(aio): ignore .header-link when selecting the heading text (#21695)
Implemented @maxkorz's
[suggestion](https://github.com/angular/angular/issues/21515#issuecomment-357453634).

Fixes #21515

PR Close #21695
2018-02-07 12:06:45 -08:00
920b0df32a fix(aio): prevent heading misplacement while styles load (#21695)
During the initial load of the page (probably until the icon styles are
loaded and/or applied), the `.header-link` element is wider, pushing the
heading text slightly to the right (for a brief moment).

This commit prevents this slight shift by explicitly setting the width
for the `.header-link` element.

PR Close #21695
2018-02-07 12:06:45 -08:00
b313976ac1 fix(aio): ensure header-links are visible at <600px (#21695)
PR Close #21695
2018-02-07 12:06:45 -08:00
2d19e7bbea refactor(aio): simplify .header-link styles (#21695)
PR Close #21695
2018-02-07 12:06:45 -08:00
c3c92d7796 fix(aio): reduce flicker and reflows for initial rendering (#21695)
For the initial rendering, where there is no transition from a previous
visual state to a new one, animations make little sense. The page should
load with as few reflows as possible.
Similarly, while we typically want to defer updating the SideNav state
(e.g. opened/closed) until the "leaving" document is animated out of the
page, on the initial rendering (where there is no "leaving" document)
this leads to the SideNav flashing (from closed to open).

These worked as expected before, but several parts (mostly related to
documents with a SideNav) have been accidentally broken in recent
commits (e.g. when upgraded to latest material, or enabled animations
for DocViewer transitions, etc.).

This commit restores the previous behavior by ensuring that (on the
initial rendering) the SideNav state is updated as soon as possible and
that there will be no animations when:

1. The hamburger button appears.
2. The SideNav is opened.
3. The main section's width is adjusted to make room for the SideNav.

PR Close #21695
2018-02-07 12:06:45 -08:00
a887c9339f refactor(aio): preserve HttpClient asynchronicity in tests (#21695)
Previously, the mocked `HttpClient` was synchronous in tests (despite
the actual `HttpClient` being asynchronous). Although we use observables
(which generally make the implementation sync/async-agnostic), the fact
that we have no control over when Angular updates/checks views and calls
lifecycle hooks resulted in different behavior (and errors) in tests
(with sync `HttpClient`) vs actual app (with async `HttpClient`).

This commit ensures that the behavior (and errors) are consistent
between the tests and the actual app by making the mocked `HttpClient`
asynchronous.

PR Close #21695
2018-02-07 12:06:45 -08:00
89051a0452 fix(aio): remove links from sub-menu toggles (#21695)
Navigating to a document while trying to expand or collapse a sub-menu
is undesirable and confusing. All sub-menu toggles should have no other
effect than expanding/collapsing the corresponding sub-menu.

PR Close #21695
2018-02-07 12:06:45 -08:00
97dafa8460 docs(animations): fix typo (disbled --> disabled) (#21695)
PR Close #21695
2018-02-07 12:06:45 -08:00
56b9591746 fix(forms): prevent event emission on enable/disable when emitEvent is false (#12366) (#21018)
Previously, the emitEvent flag was only checked when emitting on the current control.
Thus, if  the control was part of a hierarchy, events were emitted on the parent and the childrens.
This fixes the issue by properly passing the emitEvent flag to both parent and childrens.

Fixes #12366

PR Close #21018
2018-02-07 12:05:26 -08:00
b0ddb5ad0e build(aio): blacklist unwanted URLs from the generated sitemap.xml (#22061)
Closes #22017

PR Close #22061
2018-02-07 12:02:01 -08:00
103727aadf docs(aio): fix TOH inclusion of HeroesService. (#21228)
Change docs where the MessageService is referenced

Fixes #20398

PR Close #21228
2018-02-07 12:01:32 -08:00
44ea80b797 docs(forms): Custom Validator example selector name incorrect. (#20464)
Added bobby e2e test for template form.

Fixes: #20206

PR Close #20464
2018-02-07 12:01:12 -08:00
41046e4a90 docs(forms): Custom Validator example selector name incorrect. (#20464)
Name of selector in ForbiddenName example is not consistent with Validator class nor Html selector example. Added the selector name 'appForbiddenName' as an alias name for the input of the Validator class, and updated the view accordingly.

Fixes: #20206

PR Close #20464
2018-02-07 12:01:12 -08:00
530b824faa docs: add changelog for 5.2.4 2018-02-07 10:19:39 -08:00
e22d3a605c release: cut the 5.2.4 release 2018-02-07 10:16:42 -08:00
c6645e7a04 fix(core): fix proper propagation of subscriptions in EventEmitter (#22016)
Closes #21999

PR Close #22016
2018-02-06 07:56:34 -08:00
f0396f1e54 docs(aio): fix swap value (#20905)
'http.get' has been swapped in for 'of'

PR Close #20905
2018-02-05 13:05:58 -08:00
adb1d62967 docs: clarify npm/yarn commands, add blank lines to mix md/html in table (#21606)
PR Close #21606
2018-02-05 13:02:14 -08:00
cfe83939a4 docs: update browser support (#21606)
PR Close #21606
2018-02-05 13:02:14 -08:00
973607fe9d ci: mark PRs with rejection as not green (#21922)
PR Close #21922
2018-02-05 13:01:12 -08:00
664f7fa477 build(aio): add API static members to search index (#21988)
Previously searching for `compose` did not include `Validators`
in the search results because we were not including all the
`static` members of API docs in the index.

PR Close #21988
2018-02-05 13:00:47 -08:00
b155ae116b ci: add config for g3 status (#21996)
Ref #21642
PR Close #21996
2018-02-05 12:59:59 -08:00
ce51ea93a1 fix(core): fix #20582, don't need to wrap zone in location change listener (#22007)
PR Close #22007
2018-02-05 12:59:05 -08:00
d38e08812e feat(aio): dynamically, pre-emptively, add noindex (#21992)
These tags are removed when the doc is ready and valid, but this will
allow us to block indexing in the case that the Angular app fails to
bootstrap or load the document for some non-404 reason.

This should get around the problem with hardcoded tags. See
c3fb820473

Closes #21941

PR Close #21992
2018-02-05 12:58:27 -08:00
aa9ba7f9fe fix(core): should check Zone existance when scheduleMicroTask (#20656)
PR Close #20656
2018-02-02 07:53:55 -08:00
102d06b974 docs: consistency fix in describing a custom tag (#21747)
PR Close #21747
2018-02-02 07:53:18 -08:00
11ec80a053 docs: add docs for IE (#21824)
PR Close #21824
2018-02-02 07:51:47 -08:00
75eecdc351 docs: add missing underline (#21892)
PR Close #21892
2018-02-02 07:49:33 -08:00
965eecc587 build(aio): move zip and live-example generation to yarn predocs task (#21970)
This will prevent the confusing errors for first time users who
try to generate the docs with `yarn docs` and are told there are
dangling links.

Closes #21944

PR Close #21970
2018-02-02 07:48:42 -08:00
c4fb696189 fix(common): don't convert null to a string when flushing a mock request (#21417)
A bug in TestRequest caused null response bodies to be stringified. This
change causes null to be treated faithfully.

Fixes #20744

PR Close #21417
2018-02-01 08:32:44 -08:00
72df747dd6 docs(aio): add missing closing <code-examle> tag (#21771)
PR Close #21771
2018-02-01 08:31:21 -08:00
579bed1a7a docs: add changelog for 5.2.3 2018-01-31 12:47:02 -08:00
b59fb23f4a release: cut the 5.2.3 release 2018-01-31 12:45:17 -08:00
2aa460b30e docs: add http guide sample and adjust text (#21326)
PR Close #21326
2018-01-31 10:24:43 -08:00
e0022ae9cd docs: Fix platform-detection example for Universal (#21796)
PR Close #21796
2018-01-31 10:21:04 -08:00
f2e923edd8 build(aio): upgrade to dgeni-packages 0.24.0 (#21802)
This has two benefits:

* it prepares the way for the API docs update, which need parameter docs
* it doesn't incorrectly report dangling links for non-latin anchors

Closes #21306

PR Close #21802
2018-01-31 10:20:37 -08:00
c2f5ed545c fix(common): generate closure-locale data file with exported plural functions (#21873)
Fixes #21870
PR Close #21873
2018-01-30 11:42:31 -08:00
5d75df8fb1 ci: unblock master by ignoring date pipe tests while we fix it (#21906)
PR Close #21906
2018-01-30 11:33:46 -08:00
ed2b71799c fix(common): allow HttpInterceptors to inject HttpClient (#19809)
Previously, an interceptor attempting to inject HttpClient directly
would receive a circular dependency error, as HttpClient was
constructed via a factory which injected the interceptor instances.
Users want to inject HttpClient into interceptors to make supporting
requests (ex: to retrieve an authentication token). Currently this is
only possible by injecting the Injector and using it to resolve
HttpClient at request time.

Either HttpClient or the user has to deal specially with the circular
dependency. This change moves that responsibility into HttpClient
itself. By utilizing a new class HttpInterceptingHandler which lazily
loads the set of interceptors at request time, it's possible to inject
HttpClient directly into interceptors as construction of HttpClient no
longer requires the interceptor chain to be constructed.

Fixes #18224.

PR Close #19809
2018-01-29 16:12:32 -08:00
fad99cca0e fix(forms): inserting and removing controls should work in re-bound form arrays (#21822)
Closes #21501

PR Close #21822
2018-01-29 16:11:41 -08:00
3f5ead3845 fix(aio): missing plural s in preserveWhiteSpaces example (#21854)
PR Close #21854
2018-01-29 11:35:13 -08:00
a89e709515 docs: change ”it's" to "its" as needed in several docs. (#21867)
Most of them are in content but one is in common and needs special approval.

PR Close #21867
2018-01-29 11:34:47 -08:00
6a7689d4ea build: update to latest bazel rules (#21821)
PR Close #21821
2018-01-27 10:55:45 -08:00
696ba01a4e fix(aio): don't set noindex metatag in the static index.html (#21816)
This seems to be causing crawling issues for google.

Ref #21665

PR Close #21816
2018-01-26 16:08:31 -08:00
81d64d6bec fix(core): fix retrieving the binding name when an expression changes (#21814)
fixes #21735
fixes #21788

PR Close #21814
2018-01-26 15:34:48 -08:00
7410941a7c build: merge-pr now checks that PR status is green before proceeding (#21810)
Optionally one can use `--force` to override and merge no non-green PR.

PR Close #21810
2018-01-26 14:50:41 -08:00
d159ad8b88 build(aio): prevent Windows error on serve-and-sync (#21806)
Running `yarn start` (which watches the `src/` directory) and
`yarn docs-watch` (which cleans up files in `src/generated/api/`) often
results in `ENOTEMPTY` errors.

This commit solves it by ensuring that `yarn docs` has been completed
before running `yarn start`.

PR Close #21806
2018-01-26 14:50:15 -08:00
250c8da768 fix(language-service): ensure correct paths are passed to TypeScript (#21812)
The 2.6 version of TypeScript's `resolveModuleName`  started to
require paths passed to be separated by '/' instead of being
able to handle '\'.

`ngc` and `ng` already do this transformation.

Fixes: #21811

PR Close #21812
2018-01-26 14:49:23 -08:00
778e6e759f fix(language-service): spell diagnostics correctly (#21812)
PR Close #21812
2018-01-26 14:49:23 -08:00
35a0721217 fix(router): remove @internal tag on ParamInheritanceType (#21773)
This is a more defensive approach to ensure that references to
ParamInheritanceType from the published declarations do not cause
compilation errors when compiling Angular from the published packages.

Fixes #21456

PR Close #21773
2018-01-26 10:28:34 -08:00
ba045e88d7 docs: add notes on email used for CLA (#21754)
Closes #20034

PR Close #21754
2018-01-26 10:28:18 -08:00
67806a7b25 fix(aio): close SideNav on non-sidenav doc on wide screen (#21538)
Partly addresses #21520.

PR Close #21538
2018-01-26 10:25:16 -08:00
9778a23be8 fix(aio): fix SideNav height on narrow screens (#21538)
Since we specify `bottom: 0`, specifying the height is unnecessary and
leads to wrong height (unless updated) on narrow screens where the
topbar height is decreased.

Partly addresses #21520.

PR Close #21538
2018-01-26 10:25:15 -08:00
87e06d765e ci: Add back the CLI integration test with pinning (#21555)
The CLI app is now checked in, rather than generated dynamically with
`ng new`. This loses some assertion power, but gains hermeticity.
It also checks in lock files for all integration tests, avoiding
floating version numbers.

We'll need another place to integration test between changes in
the various repositories - but the angular/angular PR-blocking status
is not the right place to do this.

PR Close #21555
2018-01-25 22:18:56 -08:00
56f3e18c1c fix(forms): allow FormBuilder to create controls with any formState type (#20917)
Align formState type in FormBuilder#control with FormControl#constructor

Fixes #20368

PR Close #20917
2018-01-25 22:17:43 -08:00
637515e71b build: autosquashes SHAs as part of merge-pr script (#21791)
To support `git checkin --fixup` and `git checkin —squash`
we need to make sure that `merge-pr` squashes the sepecial
commits before they are merged.

For more details see:
https://robots.thoughtbot.com/autosquashing-git-commits

PR Close #21791
2018-01-25 22:12:11 -08:00
27ecd077d4 docs(aio): fix missing stylesheet in component-styles example (#21772)
The code in the example was referring to `hero-app.component.css` but this did
not exist.

PR Close #21772
2018-01-25 13:38:14 -08:00
4db1be0292 docs(aio): fix paths to imported CSS stylesheets (#21772)
The AOT compiler needs relative paths so that it can find
the imported stylesheets.

PR Close #21772
2018-01-25 13:38:14 -08:00
a0dbef9ea4 build(aio): upgrade CLI version to cope with new Angular 6.0.0-beta.1 release (#21772)
Before version 1.6 of Angular CLI there was a check that prevented use of Angular
compiler CLI with major version 6.

PR Close #21772
2018-01-25 13:38:13 -08:00
3aaf43f73c docs: add changelog for 5.2.2 2018-01-24 21:26:17 -08:00
d952ae24dd release: cut the 5.2.2 release 2018-01-24 21:23:04 -08:00
da9e57b3d5 build: Update to latest rules_typescript. (#21675)
Fixes #21481

PR Close #21675
2018-01-24 20:47:41 -08:00
44d4f82dae docs(aio): added a link to Angular-RU (#21687)
Angular-RU Community on GitHub is a single point for all resources, chats, podcasts and meetups for Angular in Russia

PR Close #21687
2018-01-24 20:47:40 -08:00
bde2b4425c ci: use sudo: false on Travis (#21641)
Related to #21422.

PR Close #21641
2018-01-24 20:47:40 -08:00
2a3de802a0 fix(aio): fix code highlight in API docs templates (#21630)
Fixes #21108

PR Close #21630
2018-01-24 20:47:40 -08:00
71f9eaa743 fix(common): extract plural function from i18n locale data files for TS 2.6 (#21626)
Fixes #21608

PR Close #21626
2018-01-24 20:47:40 -08:00
a62c186d15 fix(common): don't remove special characters when extracting CLDR data (#21626)
PR Close #21626
2018-01-24 20:47:40 -08:00
c8bf281174 build(aio): generate sitemap from the generated pages (#21689)
Closes #21684

PR Close #21689
2018-01-24 20:47:40 -08:00
de6c6445af fix(compiler): Don't strip /*# sourceURL ... */ (#16088)
Currently, `shimCssText` only keep `/*# sourceMappingUrl ... */` comments and strip `/*# sourceURL ... */` comments. So, Chrome can't find the source maps for component style(that's created in new `style` tags)

PR Close #16088
2018-01-24 12:35:31 -08:00
54238822e6 build: merge PR to all branches per target: label (#21739)
PR Close #21739
2018-01-24 12:35:13 -08:00
8b3fbb5bf4 fix(router): don't use ParamsInheritanceStrategy in declarations (#21574)
ParamsInheritanceStrategy is internal, so any references to it from the
published .d.ts files will fail.

Fixes #21456.

PR Close #21574
2018-01-23 21:34:37 -08:00
2f61d3c320 fix(aio): remove remaining plnkr references (#20165)
PR Close #20165
2018-01-23 21:33:55 -08:00
5894f6ee1c build(aio): check for obsolete plnkr.json and missing main files (#20165)
Also, remove `plnkr.json` for `service-worker-getting-started` guide,
since it is not used and ServiceWorker cannot work correctly in
plnkr/stackblitz anyway (e.g. no build step to re-compute hashes).
A zipper might be useful and can be added in a subsequent PR, but it is
currently broken (e.g. no dependency on `@angular/service-worker`).

PR Close #20165
2018-01-23 21:33:55 -08:00
6d9fcd62de build(aio): upgrade sample package.json files to jasmine@~2.8.0 (#20165)
- Update tooling to support revised testing guide (PR #20697).
- Require jasmine upgrade for examples that use marble testing.
- Copy `cli/package.json` to `testing/` and add `jasmine-marbles`.
- Resolve merge conflicts created by `NgModules` guides.

PR Close #20165
2018-01-23 21:33:55 -08:00
0cbccc06dd build(aio): migrate plunker to stackblitz (#20165)
PR Close #20165
2018-01-23 21:33:52 -08:00
ed670a36fb docs: update ICU select messages to use male/female (#21713)
fixes #21694

PR Close #21713
2018-01-23 16:32:24 -08:00
8e44577df3 fix(compiler): fix ICU select messages to use male/female/other (#21713)
related to #21694

PR Close #21713
2018-01-23 16:32:24 -08:00
6921c20ea1 test(forms): Better description and coverage for #19256 (#21652)
fixes #21575

PR Close #21652
2018-01-23 16:31:45 -08:00
52970c09e1 fix(compiler-cli): do not fold errors past calls in the collector (#21708)
Folding errors passed calls prevented the static reflector from
begin able to ignore errors in annotations it doesn't know as
the call to the unknown annotation was elided from the metadata.

Fixes: #21273

PR Close #21708
2018-01-23 13:33:26 -08:00
eecdf3414e docs: fix #19989, add zone flags(blacklist/module) in guide (#21701)
PR Close #21701
2018-01-23 13:33:11 -08:00
21f766968d refactor(bazel): pass around tsconfig as a file, not a path (#21614)
this unlocks the ability to replay ts compilations with different settings

PR Close #21614
2018-01-23 10:06:05 -08:00
4b68fdce6f build: Update to latest rules_typescript. (#21675)
Fixes #21481

PR Close #21675
2018-01-22 15:34:48 -08:00
c12ea3a1f0 fix(common): A null value should remove the style on IE (#21679)
fixes #21064

PR Close #21679
2018-01-22 12:57:23 -08:00
d7dbdc5c36 docs: fix stray div and reformat paragraph (#21676)
PR Close #21676
2018-01-19 20:42:04 -08:00
0112a903f9 ci: add github bot config to triage issues (#21672)
Fixes #21635
PR Close #21672
2018-01-19 20:41:15 -08:00
66bbc84127 ci(aio): do not limit size of gzip7 and gzip 9 (#21601)
PR Close #21601
2018-01-19 20:41:01 -08:00
554129d6fe feat(aio): update metatags to control search engine crawling (#21665)
The `<meta name="robots" content="noindex">` tag is used
to indicate to search engine crawlers that they should not index
the current page. This is set dynamically by the the document
viewer component to ensure that 404 and other erroring pages
are not added to the search index.

This relies upon the idea that the crawling bot will run the JS
and wait to see if this meta tag has been added or not.

Since we believe that the `googebot` will do this, we also
pre-emptively add a hard-coded noindex tag specifically for
this bot, so that if anything else fails in bootstrapping the app,
the failed page will not be added to the index.

Closes #21317

PR Close #21665
2018-01-19 20:31:45 -08:00
e32a0cabfe fix(aio): add a required comma in firebase.json (#21618)
PR Close #21618
2018-01-19 20:31:30 -08:00
c828e5627b build: Remove angular_src nested workspace (#21096)
PR Close #21096
2018-01-19 13:10:09 -08:00
1626e74c59 docs: clarify the use of classes and interfaces in style guide (#20919)
PR Close #20919
2018-01-19 13:09:58 -08:00
a15a2b46d1 ci: add "PR action: cleanup" to the bot's forbiddenLabels list (#21562)
PR Close #21562
2018-01-19 13:09:41 -08:00
379ed75593 docs: improve/simplify example for providers guide (#21589)
PR Close #21589
2018-01-19 13:09:31 -08:00
0f619896b3 docs: fix/improve example for singleton-services guide (#21589)
PR Close #21589
2018-01-19 13:09:31 -08:00
7060655806 docs: several minor NgModule guide fixes/improvements (#21589)
PR Close #21589
2018-01-19 13:09:31 -08:00
b5fc3eb9de docs: minor fixes (anchor tags, redundant whitespace, consistent code-snippets lang) (#21589)
PR Close #21589
2018-01-19 13:09:31 -08:00
451bdb9a75 docs: change titles to sentence case (#21620)
PR Close #21620
2018-01-19 13:09:25 -08:00
983ccc02ad build(aio): fix zips testing commands (#21629)
PR Close #21629
2018-01-19 13:09:17 -08:00
00f99b3c4c ci: update github bot messages (#21634)
Fixes #21633
PR Close #21634
2018-01-19 13:09:11 -08:00
ba4ea82f68 fix(compiler-cli): do not lower expressions in non-modules (#21649)
Fixes: #21651

PR Close #21649
2018-01-19 13:09:04 -08:00
982eb7bba8 fix(common): fallback to last defined value for named date and time formats (#21299)
closes #21282

PR Close #21299
2018-01-19 13:08:57 -08:00
3606c55410 docs: edit entry component FAQ (#21487)
PR Close #21487
2018-01-19 13:08:50 -08:00
2c65027391 docs: add server side redirect and fix NgModule FAQ links (#21487)
PR Close #21487
2018-01-19 13:08:50 -08:00
4ee92f14a6 docs: fix lazy loading example dir name (#21475)
PR Close #21475
2018-01-19 13:08:30 -08:00
c9b65914d3 build: add mhevery to bazel approvers (#21314)
PR Close #21314
2018-01-19 13:08:19 -08:00
02352bcd9e fix(compiler): add support for marker tags in xliff serializers (#21250)
The Xliff serializer now supports the tags `seg-source` and `mrk`, while the Xliff2 serializer now supports `mrk`.
Fixes #21078
PR Close #21250
2018-01-19 13:08:10 -08:00
0d55600fd8 Revert "fix(core): fix chained http call (#20924)"
This reverts commit 54e75766ad.
2018-01-19 13:06:33 -08:00
af4eb00c91 docs: add changelog for 5.2.1 2018-01-17 09:39:46 -08:00
d3e7ebb3b4 release: cut the 5.2.1 release 2018-01-17 09:37:50 -08:00
420f5c4275 build(bazel): remove spurious file (#21455)
PR Close #21455
2018-01-17 08:24:34 -08:00
b773a4ab98 docs(aio): change df-question to app-question (#21438)
closes: #21404
PR Close #21438
2018-01-17 07:20:52 -08:00
55f15c54d9 docs(aio): add description and docs links for code samples (#21561)
PR Close #21561
2018-01-17 07:19:37 -08:00
4556532c26 feat(core): add binding name to content changed error (#20352)
Adding the binding name to the error message recieved by the user gives
extra context on what exactly changed. The tests are also updated to
reflect the new error message.

PR Close #20352
2018-01-17 07:17:00 -08:00
54e75766ad fix(core): fix chained http call (#20924)
Fixes an issue where chained http calls would prematurely call
testability whenStable callbacks after the first http call.

Fixes #20921

PR Close #20924
2018-01-17 07:14:55 -08:00
d3333f04ba refactor(core): refactor WrappedValue (#20997)
- Improve `WrappedValue` by adding `unwrap` symetrical to `wrap`.
- remove dead code - `ValueUnwrapper`

The property `wrapped` is an implementation details and should never be accessed
directly - use `unwrap(wrappedValue)`. Will change to protected in Angular 7.

PR Close #20997
2018-01-16 07:13:24 -08:00
75f8522b8d feat(forms): handle string with and without line boundary on pattern validator (#19256)
PR Close #19256
2018-01-16 07:12:14 -08:00
a771ee5d90 build(aio): move file cleaning to later in the doc gen (#21540)
Previously the generated files were cleaned out before
doc-gen began (via a yarn pre-script). This can cause a
race condition in the CLI server, which prevents the new
generated files from being picked up.

Now we delay the cleaning until the last minute to ensure
that they ar still picked up by the webpack server.

PR Close #21540
2018-01-16 07:11:01 -08:00
a4cbe3542a ci: disable integration/cli-hello-world test (#21492)
it is non-hermetic and breaks often due to unpinned dependencies.

PR Close #21492
2018-01-12 14:57:45 -08:00
cc9419d1ca fix(language-service): Clear caches when program changes (#21337)
This commit fixes a bug whereby the caches are not cleared when the
program changes. This subsequently produces the incorrect error of
'Component ... is not included in a module ...'.

PR Close #19405

PR Close #21337
2018-01-12 14:43:34 -08:00
d5393c7f91 docs(compiler): document the $any type cast function (#20968)
Closes #20966

PR Close #20968
2018-01-12 14:38:42 -08:00
71dd92bbb8 style(aio): enforce strict TypeScript checks (#21342)
Closes #20646

PR Close #21342
2018-01-12 14:37:02 -08:00
977978edb5 test(common): make date pipe tests work in more timezones (#21379)
Fixes #21112
PR Close #21379
2018-01-12 13:50:06 -08:00
eb70966065 fix(aio): correctly handle redirects (#21416)
- Fixes handling of some redirects by the ServiceWorker.
- Fixes redirect for old `NgFor` to new `NgForOf` URL.

Fixes #21318

PR Close #21416
2018-01-12 13:49:10 -08:00
cf4bea587d ci(aio): run e2e tests in production mode (#21470)
This will enable catching errors introduced by build optimizations that
do not appear in `development` mode.

Fixes #21446

PR Close #21470
2018-01-12 13:46:00 -08:00
8e9cd57951 fix(common): set correct timezone for ISO8601 dates in Safari (#21506)
Fixes #21491
PR Close #21506
2018-01-12 13:21:11 -08:00
f23896f519 docs: fix release schedule date (#21474)
PR Close #21474
2018-01-12 12:10:26 -08:00
0e59d18fc2 ci: add angular robot config file (#21489)
PR Close #21489
2018-01-12 10:12:28 -08:00
54c8a321a9 ci: update pullapprove rules now that we allow selfapprovals (#21494)
PR Close #21494
2018-01-12 10:11:37 -08:00
9005a6f3cd feat(aio): implement survey notification link (#21371)
Closes #21094

PR Close #21371
2018-01-12 10:06:08 -08:00
3c6a5063f7 fix(benchpress): should still support selenium_webdriver < 3.6.0 (#21477)
PR Close #21477
2018-01-11 10:54:11 -08:00
b49d54e606 docs(forms): clarify note in reactive forms doc (#21134)
PR Close #21134
2018-01-11 07:04:15 -08:00
55fd82e587 docs: fix release schedule date (#21469)
PR Close #21469
2018-01-11 06:45:49 -08:00
7f3d0bbf97 build: update polyfill size (#21461)
PR Close #21461
2018-01-10 17:07:32 -08:00
3db02d244a docs: fix lazy-loading example (#20306)
PR Close #20306
2018-01-10 16:26:28 -08:00
50b605686e docs: fix yarn.lock (#20306)
PR Close #20306
2018-01-10 16:26:28 -08:00
64d4aafbc7 docs(aio): add NgModule docs (#20306)
PR Close #20306
2018-01-10 16:26:28 -08:00
a931a419fa fix(compiler): make .ngsummary.json files idempotent (#21448)
Fixes: #21432

PR Close #21448
2018-01-10 16:21:32 -08:00
6a97b5b722 docs(ivy): add Ivy as a recognized label (#21428)
PR Close #21428
2018-01-10 15:32:59 -08:00
aad1126446 docs: update the release schedule with v6 info (#21435)
Fixes #20649

PR Close #21435
2018-01-10 15:20:27 -08:00
7b463df52b fix(aio): preserve static Observable methods (#21351)
PR Close #21351
2018-01-10 14:30:42 -08:00
a2432c9f10 build(aio): turn on namedChunks option to make debugging, profiling and tracking easier (#21351)
PR Close #21351
2018-01-10 14:30:42 -08:00
e500484ccc build(aio): upgrade to angular@5.2.0 (#21351)
-rw-r--r--  1 iminar  eng   72498 Jan  9 22:20 dist/0.0d2802b63e9f4f4615cd.chunk.js
-rw-r--r--  1 iminar  eng   14872 Jan  9 22:20 dist/4.c719ac5645940382cdce.chunk.js
-rw-r--r--  1 iminar  eng    1558 Jan  9 22:20 dist/inline.cb4b7a639193fc489d3b.bundle.js
-rw-r--r--  1 iminar  eng  454654 Jan  9 22:20 dist/main.b65f32ffc92e075e64e0.bundle.js
-rw-r--r--  1 iminar  eng   40264 Jan  9 22:20 dist/polyfills.87edf5d695f14a29bf91.bundle.js
-rw-r--r--  1 iminar  eng   54001 Jan  9 22:20 dist/worker-basic.min.js

PR Close #21351
2018-01-10 14:30:42 -08:00
a0dcb0b828 ci(aio): track sizes of all js files (#21351)
PR Close #21351
2018-01-10 14:30:42 -08:00
c4b71920d1 build(aio): upgrade to angular@5.2.0-rc.0 (#21351)
Because of c2b3792 one of the test assertions had to be adjusted (more info: https://github.com/angular/angular/pull/21351#issuecomment-356083940).

PR Close #21351
2018-01-10 14:30:42 -08:00
650f5fb5c7 build(bazel): fix merge conflict between c4f02e2 and ef956a2 (#21453)
PR Close #21453
2018-01-10 12:57:38 -08:00
c32e83334b fix(compiler): cache external reference resolution (#21359)
Cache reference resolution for external references as finding
the declaration of a symbol is expensive and does not change
for a program once created.

This resolves a signficant performance regression in the langauge
service.

PR Close #21359
2018-01-10 12:34:07 -08:00
7bdd9aecbd build: upgrade yarn to 1.3.2 (#21406)
Fixes #20566
PR Close #21406
2018-01-10 12:32:47 -08:00
5ede67c345 build: move repeated tsconfig attributes to a macro (#20964)
This helps ensure we use the same tsconfig.json file for all compilations.
Next steps are to make it the same tsconfig.json file used by the editor

PR Close #20964
2018-01-10 12:30:44 -08:00
31b671ab54 fix(aio): support multibyte character in heading (#21414)
PR Close #21414
2018-01-10 12:25:45 -08:00
3804d20b6d docs(bazel): document an installation issue (#21401)
observed by Igor on a Mac

PR Close #21401
2018-01-10 12:24:03 -08:00
0a5a87887e build(aio): upgrade to uglify-es@3.3.5 (#21350)
This should fix the size regression spotted via the previous commit.

-rw-r--r--  1 iminar  eng   72498 Jan  8 00:03 dist/0.86a7a08f7866e6cdc36f.chunk.js
-rw-r--r--  1 iminar  eng   14872 Jan  8 00:03 dist/4.c719ac5645940382cdce.chunk.js
-rw-r--r--  1 iminar  eng    1558 Jan  8 00:03 dist/inline.7722895d8c844f710bcd.bundle.js
-rw-r--r--  1 iminar  eng  453905 Jan  8 00:03 dist/main.faff0e2da95443f759f5.bundle.js
-rw-r--r--  1 iminar  eng   40264 Jan  8 00:03 dist/polyfills.87edf5d695f14a29bf91.bundle.js
-rw-r--r--  1 iminar  eng   54001 Jan  8 00:03 dist/worker-basic.min.js

PR Close #21350
2018-01-10 12:22:12 -08:00
c46afce0f5 build(aio): upgrade to @angular/cli@1.6.3 (#21350)
This change introduces a size regression of 9kb for main.js :-(

I filed an issue for this: https://github.com/angular/angular-cli/issues/9108

-rw-r--r--  1 iminar  eng   72546 Jan  5 19:27 dist.cli-1.6.3/0.86a7a08f7866e6cdc36f.chunk.js
-rw-r--r--  1 iminar  eng   14893 Jan  5 19:27 dist.cli-1.6.3/4.c719ac5645940382cdce.chunk.js
-rw-r--r--  1 iminar  eng    1558 Jan  5 19:27 dist.cli-1.6.3/inline.7722895d8c844f710bcd.bundle.js
-rw-r--r--  1 iminar  eng  466484 Jan  5 19:27 dist.cli-1.6.3/main.faff0e2da95443f759f5.bundle.js
-rw-r--r--  1 iminar  eng   40363 Jan  5 19:27 dist.cli-1.6.3/polyfills.87edf5d695f14a29bf91.bundle.js
-rw-r--r--  1 iminar  eng   54001 Jan  5 19:27 dist.cli-1.6.3/worker-basic.min.js

PR Close #21350
2018-01-10 12:22:12 -08:00
76c781fd37 docs(router): remove extra brackets (#21294)
PR Close #21294
2018-01-10 12:20:48 -08:00
51eb3d418e fix(service-worker): properly handle invalid hashes in all scenarios (#21288)
When the SW fetches URLs listed in a manifest with hashes, it checks
the content hash against the manifest to make sure it has the correct
version of the URL. In the event of a mismatch, the SW is supposed to
consider the manifest invalid, and avoid using it. There are 3 cases
to consider by which this can happen.

Case 1: during the initial SW installation, a manifest is activated
without waiting for every URL to be fully loaded. In the background,
every prefetch URL listed by the manifest is requested and cached.
One such prefetch request could fail the hash test, and cause the
manifest to be treated as invalid. In such a case, the SW should
enter a state of EXISTING_CLIENTS_ONLY, as the latest manifest is
invalid.

This case works today.

Case 2: during the initial SW installation, as in Case 1, a manifest
is activated without waiting for each URL to fully load. However,
it's possible that the application could request a URL with a bad
hash before background initialization tries to load that URL. This
happens if, for example, the application has a broken index.html.

In this case, the SW should enter a state of EXISTING_CLIENTS_ONLY,
and serve the request from the network instead.

What happens today is that the internal error escapes the SW and
is returned as a rejected Promise to respondWith(), causing a
browser-level error that the site cannot be loaded, breaking the
site.

This change allows the SW to detect the error and enter the correct
state, falling back on the network if needed.

Case 3: during checkForUpdate(), the SW will try to fully cache the
new update before making it the latest version. Failure here is
complicated - if the page fails to load due to transient network
conditions (timeouts, 500s, etc), then it makes sense to continue
serving the existing cached version, and attempt to activate the
update on the next cycle.

If the page fails due to non-transient conditions though (400 error,
hash mismatch, etc), then the SW should consider the updated
manifest invalid, and enter a state of EXISTING_CLIENTS_ONLY.

Currently, all errors are treated as transient.

This change causes the SW to treat all errors during updates as
non-transient, which can cause the SW to unnecessarily enter a
safe mode. A future change can allow the SW to remain in normal mode
if the error is provably transient.

PR Close #21288
2018-01-10 12:18:55 -08:00
48c18985cc fix(animations): fix increment/decrement aliases example (#18323)
PR Close #18323
2018-01-10 12:07:24 -08:00
167cbed266 build: add bazel rulse for benchmarks (#21436)
PR Close #21436
2018-01-10 12:01:17 -08:00
70e8802540 docs(core): provide note for unit test changes with the ng-star-inserted className (#21450)
PR Close #21450
2018-01-10 11:14:28 -08:00
1573 changed files with 53159 additions and 17201 deletions

25
.circleci/bazel.rc Normal file
View File

@ -0,0 +1,25 @@
# These options are enabled when running on CI
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
# See remote cache documentation in /docs/BAZEL.md
# Don't be spammy in the logs
build --noshow_progress
# Don't run manual tests
test --test_tag_filters=-manual
# Enable experimental CircleCI bazel remote cache proxy
# See remote cache documentation in /docs/BAZEL.md
build --experimental_remote_spawn_cache --remote_rest_cache=http://localhost:7643
# Prevent unstable environment variables from tainting cache keys
build --experimental_strict_action_env
# Workaround https://github.com/bazelbuild/bazel/issues/3645
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
# Limit Bazel to consuming resources that fit in CircleCI "medium" class which is the default:
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
build --local_resources=3072,2.0,1.0
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
test --flaky_test_attempts=2

View File

@ -15,6 +15,13 @@
var_1: &docker_image angular/ngcontainer:0.1.0
var_2: &cache_key angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.1.0
# See remote cache documentation in /docs/BAZEL.md
var_3: &setup-bazel-remote-cache
run:
name: Start up bazel remote cache proxy
command: ~/bazel-remote-proxy -backend circleci://
background: true
# Settings common to each job
anchor_1: &job_defaults
working_directory: ~/ng
@ -34,14 +41,16 @@ jobs:
steps:
- checkout:
<<: *post_checkout
# Check BUILD.bazel formatting before we have a node_modules directory
# Then we don't need any exclude pattern to avoid checking those files
- run: 'buildifier -mode=check $(find . -type f \( -name BUILD.bazel -or -name BUILD \)) ||
(echo "BUILD files not formatted. Please run ''yarn buildifier''" ; exit 1)'
# Run the skylark linter to check our Bazel rules
- run: 'find . -type f -name "*.bzl" |
xargs java -jar /usr/local/bin/Skylint_deploy.jar ||
# See remote cache documentation in /docs/BAZEL.md
- run: .circleci/setup_cache.sh
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
- *setup-bazel-remote-cache
- run: 'yarn buildifier -mode=check ||
(echo -e "\nBUILD files not formatted. Please run ''yarn buildifier''" ; exit 1)'
- run: 'yarn skylint ||
(echo -e "\n.bzl files have lint errors. Please run ''yarn skylint''"; exit 1)'
- restore_cache:
key: *cache_key
@ -54,6 +63,11 @@ jobs:
steps:
- checkout:
<<: *post_checkout
# See remote cache documentation in /docs/BAZEL.md
- run: .circleci/setup_cache.sh
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
- *setup-bazel-remote-cache
- restore_cache:
key: *cache_key
@ -62,7 +76,7 @@ jobs:
# Use bazel query so that we explicitly ask for all buildable targets to be built as well
# This avoids waiting for a build command to finish before running the first test
# See https://github.com/bazelbuild/bazel/issues/4257
- run: bazel query --output=label '//packages/... union @angular//...' | xargs bazel test --config=ci
- run: bazel query --output=label '//modules/... union //packages/... union //tools/...' | xargs bazel test
- save_cache:
key: *cache_key

11
.circleci/setup_cache.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
# Install bazel remote cache proxy
# This is temporary until the feature is no longer experimental on CircleCI.
# See remote cache documentation in /docs/BAZEL.md
set -u -e
readonly DOWNLOAD_URL="https://5-116431813-gh.circle-artifacts.com/0/pkg/bazel-remote-proxy-$(uname -s)_$(uname -m)"
curl --fail -o ~/bazel-remote-proxy "$DOWNLOAD_URL"
chmod +x ~/bazel-remote-proxy

View File

@ -25,7 +25,7 @@ ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION.
## Minimal reproduction of the problem with instructions
<!--
For bug reports please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
https://stackblitz.com or similar (you can use this template as a starting point: https://stackblitz.com/fork/angular-gitter).
-->
## What is the motivation / use case for changing the behavior?

100
.github/angular-robot.yml vendored Normal file
View File

@ -0,0 +1,100 @@
# Configuration for angular-robot
# options for the merge plugin
merge:
# the status will be added to your pull requests
status:
# set to true to disable
disabled: false
# the name of the status
context: "ci/angular: merge status"
# text to show when all checks pass
successText: "All checks passed!"
# text to show when some checks are failing
failureText: "The following checks are failing:"
# the g3 status will be added to your pull requests if they include files that match the patterns
g3Status:
# set to true to disable
disabled: false
# the name of the status
context: "google3"
# text to show when the status is pending
pendingDesc: "Googler: test this change in google3 http://go/angular-g3sync"
# text to show when the status is success
successDesc: "Does not affect google3"
# list of patterns to check for the files changed by the PR
# this list must be manually kept in sync with google3/third_party/javascript/angular2/copy.bara.sky
include:
- "BUILD.bazel"
- "LICENSE"
- "WORKSPACE"
- "modules/**"
- "packages/**"
# list of patterns to ignore for the files changed by the PR
exclude:
- "packages/language-service/**"
- "**/.gitignore"
- "**/.gitkeep"
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.
\nPlease help to unblock it by resolving these conflicts. Thanks!"
# label to monitor
mergeLabel: "PR action: merge"
# list of checks that will determine if the merge label can be added
checks:
# whether the PR shouldn't have a conflict with the base branch
noConflict: true
# list of labels that a PR needs to have, checked with a regexp (e.g. "PR target:" will work for the label "PR target: master")
requiredLabels:
- "PR target: *"
- "cla: yes"
# list of labels that a PR shouldn't have, checked after the required labels with a regexp
forbiddenLabels:
- "PR target: TBD"
- "PR action: cleanup"
- "PR action: review"
- "PR state: blocked"
- "cla: no"
# list of PR statuses that need to be successful
requiredStatuses:
- "continuous-integration/travis-ci/pr"
- "code-review/pullapprove"
- "ci/circleci: build"
- "ci/circleci: lint"
# the comment that will be added when the merge label is added despite failing checks, leave empty or set to false to disable
# {{MERGE_LABEL}} will be replaced by the value of the mergeLabel option
# {{PLACEHOLDER}} will be replaced by the list of failing checks
mergeRemovedComment: "I see that you just added the `{{MERGE_LABEL}}` label, but the following checks are still failing:
\n{{PLACEHOLDER}}
\n
\n**If you want your PR to be merged, it has to pass all the CI checks.**
\n
\nIf you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help."
# options for the triage plugin
triage:
# number of the milestone to apply when the issue is triaged
defaultMilestone: 82,
# arrays of labels that determine if an issue is triaged
triagedLabels:
-
- "type: bug"
- "severity*"
- "freq*"
- "comp: *"
-
- "type: feature"
- "comp: *"
-
- "type: refactor"
- "comp: *"
-
- "type: RFC / Discussion / question"
- "comp: *"

View File

@ -10,11 +10,11 @@
# brocco - Mike Brocchi
# chuckjaz - Chuck Jazdzewski
# filipesilva - Filipe Silva
# Foxandxss - Jesús Rodríguez
# gkalpak - George Kalpakas
# hansl - Hans Larsen
# IgorMinar - Igor Minar
# jasonaden - Jason Aden
# kapunahelewong - Kapunahele Wong
# kara - Kara Erickson
# matsko - Matias Niemelä
# mhevery - Misko Hevery
@ -44,6 +44,7 @@ groups:
all:
users: all
required: 1
rejection_value: -999
# In this group, your self-approval does not count
author_approval:
auto: false
@ -91,7 +92,8 @@ groups:
users:
- alexeagle #primary
- chuckjaz
- IgorMinar
- IgorMinar #fallback
- mhevery
- vikerman #fallback
build-and-ci:
@ -127,8 +129,9 @@ groups:
files:
- "packages/core/*"
users:
- chuckjaz #primary
- mhevery
- mhevery #primary
- chuckjaz
- kara
- vicb
- IgorMinar #fallback
@ -139,7 +142,6 @@ groups:
- "packages/platform-browser/animations/*"
users:
- matsko #primary
- chuckjaz #fallback
- mhevery #fallback
- IgorMinar #fallback
@ -171,6 +173,7 @@ groups:
- hansl
- filipesilva #fallback
- brocco #fallback
- IgorMinar #fallback
compiler-cli:
conditions:
@ -216,9 +219,8 @@ groups:
- "packages/common/http/*"
- "packages/http/*"
users:
- vikerman #primary
- alxhub
- IgorMinar #fallback
- alxhub #primary
- IgorMinar
- mhevery #fallback
language-service:
@ -237,7 +239,7 @@ groups:
files:
- "packages/router/*"
users:
- jasonaden
- jasonaden #primary
- vicb
- IgorMinar #fallback
- mhevery #fallback
@ -268,8 +270,7 @@ groups:
- "packages/platform-server/*"
users:
- vikerman #primary
# needs secondary
- alxhub
- alxhub #secondary
- vicb
- IgorMinar #fallback
- mhevery #fallback
@ -327,11 +328,11 @@ groups:
- "aio/content/navigation.json"
- "aio/content/license.md"
users:
- kapunahelewong
- stephenfluin
- Foxandxss
- petebacondarwin
- gkalpak
- IgorMinar #fallback
- IgorMinar
- mhevery #fallback
angular.io-marketing:
@ -345,5 +346,5 @@ groups:
- stephenfluin
- petebacondarwin
- gkalpak
- IgorMinar #fallback
- IgorMinar
- mhevery #fallback

View File

@ -1,7 +1,5 @@
language: node_js
# Work-around for https://github.com/travis-ci/travis-ci/issues/8836#issuecomment-356362524.
# (Restore `sudo: false` once that is resolved.)
sudo: required
sudo: false
dist: trusty
node_js:
- '8.9.1'
@ -58,7 +56,6 @@ env:
- CI_MODE=aio
- CI_MODE=aio_e2e AIO_SHARD=0
- CI_MODE=aio_e2e AIO_SHARD=1
- CI_MODE=bazel
matrix:
fast_finish: true

View File

@ -24,9 +24,7 @@ filegroup(
"typescript",
"zone.js",
"tsutils",
"@types/jasmine",
"@types/node",
"@types/source-map",
"@types",
"tsickle",
"hammerjs",
"protobufjs",
@ -34,6 +32,7 @@ filegroup(
"reflect-metadata",
"source-map-support",
"minimist",
"tslib",
] for ext in [
"*.js",
"*.json",

View File

@ -1,3 +1,120 @@
<a name="5.2.7"></a>
## [5.2.7](https://github.com/angular/angular/compare/5.2.6...5.2.7) (2018-02-28)
### Bug Fixes
* **platform-server:** generate correct stylings for camel case names ([#22263](https://github.com/angular/angular/issues/22263)) ([de02a7a](https://github.com/angular/angular/commit/de02a7a)), closes [#19235](https://github.com/angular/angular/issues/19235)
* **router:** don't mutate route configs ([#22358](https://github.com/angular/angular/issues/22358)) ([8f0a064](https://github.com/angular/angular/commit/8f0a064)), closes [#22203](https://github.com/angular/angular/issues/22203)
* **upgrade:** correctly destroy nested downgraded component ([#22400](https://github.com/angular/angular/issues/22400)) ([4aef9de](https://github.com/angular/angular/commit/4aef9de)), closes [#22392](https://github.com/angular/angular/issues/22392)
* **upgrade:** correctly handle `=` bindings in `[@angular](https://github.com/angular)/upgrade` ([#22167](https://github.com/angular/angular/issues/22167)) ([6638390](https://github.com/angular/angular/commit/6638390))
* **upgrade:** fix empty transclusion content with AngularJS@>=1.5.8 ([#22167](https://github.com/angular/angular/issues/22167)) ([a9a0e27](https://github.com/angular/angular/commit/a9a0e27)), closes [#22175](https://github.com/angular/angular/issues/22175)
<a name="5.2.6"></a>
## [5.2.6](https://github.com/angular/angular/compare/5.2.5...5.2.6) (2018-02-22)
### Bug Fixes
* **common:** correct mapping of Observable methods ([#20518](https://github.com/angular/angular/issues/20518)) ([ce5e8fa](https://github.com/angular/angular/commit/ce5e8fa)), closes [#20516](https://github.com/angular/angular/issues/20516)
* **common:** then and else template might be set to null ([#22298](https://github.com/angular/angular/issues/22298)) ([af6a056](https://github.com/angular/angular/commit/af6a056))
* **compiler-cli:** add missing entry point to package, update tsickle ([#22295](https://github.com/angular/angular/issues/22295)) ([c5418c7](https://github.com/angular/angular/commit/c5418c7))
* **core:** properly handle function without prototype in reflector ([#22284](https://github.com/angular/angular/issues/22284)) ([5ec38f2](https://github.com/angular/angular/commit/5ec38f2)), closes [#19978](https://github.com/angular/angular/issues/19978)
* **core:** support metadata reflection for native class types ([#22356](https://github.com/angular/angular/issues/22356)) ([ee91de9](https://github.com/angular/angular/commit/ee91de9)), closes [#21731](https://github.com/angular/angular/issues/21731)
<a name="5.2.5"></a>
## [5.2.5](https://github.com/angular/angular/compare/5.2.4...5.2.5) (2018-02-14)
### Bug Fixes
* **aio:** update Firebase redirects and SW routes ([#21763](https://github.com/angular/angular/pull/21763)) ([#22104](https://github.com/angular/angular/pull/22104)) ([15ff7ba](https://github.com/angular/angular/commit/15ff7ba)), closes [#21377](https://github.com/angular/angular/issues/21377)
* **bazel:** allow TS to read ambient typings ([#21876](https://github.com/angular/angular/issues/21876)) ([d57fd0b](https://github.com/angular/angular/commit/d57fd0b)), closes [#21872](https://github.com/angular/angular/issues/21872)
* **bazel:** improve error message for missing assets ([#22096](https://github.com/angular/angular/issues/22096)) ([c5ec8d9](https://github.com/angular/angular/commit/c5ec8d9)), closes [#22095](https://github.com/angular/angular/issues/22095)
* **common:** weaken AsyncPipe transform signature ([#22169](https://github.com/angular/angular/issues/22169)) ([c6bdc83](https://github.com/angular/angular/commit/c6bdc83))
* **compiler:** make unary plus operator consistent to JavaScript ([#22154](https://github.com/angular/angular/issues/22154)) ([1b8ea10](https://github.com/angular/angular/commit/1b8ea10)), closes [#22089](https://github.com/angular/angular/issues/22089)
* **core:** add stacktrace in log when error during cleanup component in TestBed ([#22162](https://github.com/angular/angular/issues/22162)) ([c4f841f](https://github.com/angular/angular/commit/c4f841f))
* **core:** ensure initial value of QueryList length ([#21980](https://github.com/angular/angular/issues/21980)) ([#21982](https://github.com/angular/angular/issues/21982)) ([47b73fd](https://github.com/angular/angular/commit/47b73fd)), closes [#21980](https://github.com/angular/angular/issues/21980)
* **core:** use appropriate inert document strategy for Firefox & Safari ([#17019](https://github.com/angular/angular/issues/17019)) ([47b71d9](https://github.com/angular/angular/commit/47b71d9))
* **forms:** prevent event emission on enable/disable when emitEvent is false ([#12366](https://github.com/angular/angular/issues/12366)) ([#21018](https://github.com/angular/angular/issues/21018)) ([56b9591](https://github.com/angular/angular/commit/56b9591))
* **language-service:** correct instructions to install the language service ([#22000](https://github.com/angular/angular/issues/22000)) ([0b23573](https://github.com/angular/angular/commit/0b23573))
* **platform-browser:** support 0/false/null values in transfer_state ([#22179](https://github.com/angular/angular/issues/22179)) ([da6ab91](https://github.com/angular/angular/commit/da6ab91))
<a name="5.2.4"></a>
## [5.2.4](https://github.com/angular/angular/compare/5.2.3...5.2.4) (2018-02-07)
### Bug Fixes
* **common:** don't convert null to a string when flushing a mock request ([#21417](https://github.com/angular/angular/issues/21417)) ([c4fb696](https://github.com/angular/angular/commit/c4fb696)), closes [#20744](https://github.com/angular/angular/issues/20744)
* **core:** fix [#20582](https://github.com/angular/angular/issues/20582), don't need to wrap zone in location change listener ([#22007](https://github.com/angular/angular/issues/22007)) ([ce51ea9](https://github.com/angular/angular/commit/ce51ea9))
* **core:** fix proper propagation of subscriptions in EventEmitter ([#22016](https://github.com/angular/angular/issues/22016)) ([c6645e7](https://github.com/angular/angular/commit/c6645e7)), closes [#21999](https://github.com/angular/angular/issues/21999)
* **core:** should check Zone existance when scheduleMicroTask ([#20656](https://github.com/angular/angular/issues/20656)) ([aa9ba7f](https://github.com/angular/angular/commit/aa9ba7f))
<a name="5.2.3"></a>
## [5.2.3](https://github.com/angular/angular/compare/5.2.2...5.2.3) (2018-01-31)
### Bug Fixes
* **common:** allow HttpInterceptors to inject HttpClient ([#19809](https://github.com/angular/angular/issues/19809)) ([ed2b717](https://github.com/angular/angular/commit/ed2b717)), closes [#18224](https://github.com/angular/angular/issues/18224)
* **common:** generate closure-locale data file with exported plural functions ([#21873](https://github.com/angular/angular/issues/21873)) ([c2f5ed5](https://github.com/angular/angular/commit/c2f5ed5)), closes [#21870](https://github.com/angular/angular/issues/21870)
* **core:** fix retrieving the binding name when an expression changes ([#21814](https://github.com/angular/angular/issues/21814)) ([81d64d6](https://github.com/angular/angular/commit/81d64d6)), closes [#21735](https://github.com/angular/angular/issues/21735) [#21788](https://github.com/angular/angular/issues/21788)
* **forms:** allow FormBuilder to create controls with any formState type ([#20917](https://github.com/angular/angular/issues/20917)) ([56f3e18](https://github.com/angular/angular/commit/56f3e18)), closes [#20368](https://github.com/angular/angular/issues/20368)
* **forms:** inserting and removing controls should work in re-bound form arrays ([#21822](https://github.com/angular/angular/issues/21822)) ([fad99cc](https://github.com/angular/angular/commit/fad99cc)), closes [#21501](https://github.com/angular/angular/issues/21501)
* **language-service:** ensure correct paths are passed to TypeScript ([#21812](https://github.com/angular/angular/issues/21812)) ([250c8da](https://github.com/angular/angular/commit/250c8da))
* **language-service:** spell diagnostics correctly ([#21812](https://github.com/angular/angular/issues/21812)) ([778e6e7](https://github.com/angular/angular/commit/778e6e7))
* **router:** remove [@internal](https://github.com/internal) tag on ParamInheritanceType ([#21773](https://github.com/angular/angular/issues/21773)) ([35a0721](https://github.com/angular/angular/commit/35a0721)), closes [#21456](https://github.com/angular/angular/issues/21456)
<a name="5.2.2"></a>
## [5.2.2](https://github.com/angular/angular/compare/5.2.1...5.2.2) (2018-01-25)
### Bug Fixes
* **common:** A null value should remove the style on IE ([#21679](https://github.com/angular/angular/issues/21679)) ([c12ea3a](https://github.com/angular/angular/commit/c12ea3a)), closes [#21064](https://github.com/angular/angular/issues/21064)
* **common:** don't remove special characters when extracting CLDR data ([#21626](https://github.com/angular/angular/issues/21626)) ([a62c186](https://github.com/angular/angular/commit/a62c186))
* **common:** extract plural function from i18n locale data files for TS 2.6 ([#21626](https://github.com/angular/angular/issues/21626)) ([71f9eaa](https://github.com/angular/angular/commit/71f9eaa)), closes [#21608](https://github.com/angular/angular/issues/21608)
* **common:** fallback to last defined value for named date and time formats ([#21299](https://github.com/angular/angular/issues/21299)) ([982eb7b](https://github.com/angular/angular/commit/982eb7b)), closes [#21282](https://github.com/angular/angular/issues/21282)
* **compiler:** add support for marker tags in xliff serializers ([#21250](https://github.com/angular/angular/issues/21250)) ([02352bc](https://github.com/angular/angular/commit/02352bc)), closes [#21078](https://github.com/angular/angular/issues/21078)
* **compiler:** Don't strip `/*# sourceURL ... */` ([#16088](https://github.com/angular/angular/issues/16088)) ([de6c644](https://github.com/angular/angular/commit/de6c644))
* **compiler:** fix ICU select messages to use male/female/other ([#21713](https://github.com/angular/angular/issues/21713)) ([8e44577](https://github.com/angular/angular/commit/8e44577))
* **compiler-cli:** do not fold errors past calls in the collector ([#21708](https://github.com/angular/angular/issues/21708)) ([52970c0](https://github.com/angular/angular/commit/52970c0))
* **compiler-cli:** do not lower expressions in non-modules ([#21649](https://github.com/angular/angular/issues/21649)) ([ba4ea82](https://github.com/angular/angular/commit/ba4ea82))
* **router:** don't use ParamsInheritanceStrategy in declarations ([#21574](https://github.com/angular/angular/issues/21574)) ([8b3fbb5](https://github.com/angular/angular/commit/8b3fbb5)), closes [#21456](https://github.com/angular/angular/issues/21456)
<a name="5.2.1"></a>
## [5.2.1](https://github.com/angular/angular/compare/5.2.0...5.2.1) (2018-01-17)
### Bug Fixes
* **animations:** fix increment/decrement aliases example ([#18323](https://github.com/angular/angular/issues/18323)) ([48c1898](https://github.com/angular/angular/commit/48c1898))
* **benchpress:** should still support selenium_webdriver < 3.6.0 ([#21477](https://github.com/angular/angular/issues/21477)) ([3c6a506](https://github.com/angular/angular/commit/3c6a506))
* **common:** set correct timezone for ISO8601 dates in Safari ([#21506](https://github.com/angular/angular/issues/21506)) ([8e9cd57](https://github.com/angular/angular/commit/8e9cd57)), closes [#21491](https://github.com/angular/angular/issues/21491)
* **compiler:** cache external reference resolution ([#21359](https://github.com/angular/angular/issues/21359)) ([c32e833](https://github.com/angular/angular/commit/c32e833))
* **compiler:** make `.ngsummary.json` files idempotent ([#21448](https://github.com/angular/angular/issues/21448)) ([a931a41](https://github.com/angular/angular/commit/a931a41))
* **core:** fix chained http call ([#20924](https://github.com/angular/angular/issues/20924)) ([54e7576](https://github.com/angular/angular/commit/54e7576)), closes [#20921](https://github.com/angular/angular/issues/20921)
* **language-service:** Clear caches when program changes ([#21337](https://github.com/angular/angular/issues/21337)) ([cc9419d](https://github.com/angular/angular/commit/cc9419d)), closes [#19405](https://github.com/angular/angular/issues/19405)
* **service-worker:** properly handle invalid hashes in all scenarios ([#21288](https://github.com/angular/angular/issues/21288)) ([51eb3d4](https://github.com/angular/angular/commit/51eb3d4))
### Features
* **core:** add binding name to content changed error ([#20352](https://github.com/angular/angular/issues/20352)) ([4556532](https://github.com/angular/angular/commit/4556532))
* **forms:** handle string with and without line boundary on pattern validator ([#19256](https://github.com/angular/angular/issues/19256)) ([75f8522](https://github.com/angular/angular/commit/75f8522))
<a name="5.2.0"></a>
# [5.2.0](https://github.com/angular/angular/compare/5.2.0-rc.0...5.2.0) (2018-01-10)
@ -9,6 +126,7 @@
* **benchpress:** work around missing events from Chrome 63 ([#21396](https://github.com/angular/angular/issues/21396)) ([fa03ae1](https://github.com/angular/angular/commit/fa03ae1))
* **common:** export currencies via `getCurrencySymbol` ([#20983](https://github.com/angular/angular/issues/20983)) ([fecf768](https://github.com/angular/angular/commit/fecf768))
Note: Due to an animation fix back in 5.1.1 ([c2b3792](https://github.com/angular/angular/commit/c2b3792a3b5fa5215fe2ef7e0ac6511086ffe4c1)) which allows for nested :leave queries to work within animation sequences, all elements that are dynamically inserted (*ngIf, *ngFor, etc…) now contain the special CSS class: “ng-star-inserted”. This may cause failures within unit tests if there are any assertions that match against element.className directly. (An easy fix for this is to match using a regular expression instead of asserting the className string directly.)
<a name="5.2.0-rc.0"></a>
# [5.2.0-rc.0](https://github.com/angular/angular/compare/5.2.0-beta.1...5.2.0-rc.0) (2018-01-04)

12
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,12 @@
# Contributor Code of Conduct
## Version 0.3b-angular
As contributors and maintainers of the Angular project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.
Communication through any of Angular's channels (GitHub, Gitter, IRC, mailing lists, Google+, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Angular project to do the same.
If any member of the community violates this code of conduct, the maintainers of the Angular project may take action, removing issues, comments, and PRs or blocking accounts as deemed appropriate.
If you are subject to or witness unacceptable behavior, or have any other concerns, please email us at [conduct@angular.io](mailto:conduct@angular.io).

View File

@ -51,7 +51,7 @@ and help you to craft the change so that it is successfully accepted into the pr
Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs we will systematically ask you to provide a minimal reproduction scenario using http://plnkr.co. Having a live, reproducible scenario gives us wealth of important information without going back & forth to you with additional questions like:
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs, we will systematically ask you to provide a minimal reproduction scenario using http://plnkr.co. Having a live, reproducible scenario gives us a wealth of important information without going back & forth to you with additional questions like:
- version of Angular used
- 3rd-party libraries and their versions
@ -61,7 +61,7 @@ A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm
We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal plunk. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
Unfortunately we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you we are going to close an issue that don't have enough info to be reproduced.
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you we are going to close an issue that doesn't have enough info to be reproduced.
You can file new issues by filling out our [new issue form](https://github.com/angular/angular/issues/new).
@ -72,7 +72,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
that relates to your submission. You don't want to duplicate effort.
1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
We cannot accept code without this.
We cannot accept code without this. Make sure you sign with the primary email address of the Git identity that has been granted access to the Angular repository.
1. Fork the angular/angular repo.
1. Make your changes in a new git branch:
@ -173,12 +173,12 @@ The **header** is mandatory and the **scope** of the header is optional.
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
to read on GitHub as well as in various git tools.
Footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
The footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
Samples: (even more [samples](https://github.com/angular/angular/commits/master))
```
docs(changelog): update change log to beta.5
docs(changelog): update changelog to beta.5
```
```
fix(release): need to depend on latest rxjs and zone.js
@ -203,7 +203,7 @@ Must be one of the following:
* **test**: Adding missing tests or correcting existing tests
### Scope
The scope should be the name of the npm package affected (as perceived by person reading changelog generated from commit messages.
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages.
The following is the list of supported scopes:
@ -232,10 +232,10 @@ There are currently a few exceptions to the "use package name" rule:
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`)
### Subject
The subject contains succinct description of the change:
The subject contains a succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize first letter
* don't capitalize the first letter
* no dot (.) at the end
### Body
@ -259,6 +259,19 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
* For corporations we'll need you to
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
<hr>
If you have more than one Git identity, you must make sure that you sign the CLA using the primary email address associated with the ID that has been granted access to the Angular repository. Git identities can be associated with more than one email address, and only one is primary. Here are some links to help you sort out multiple Git identities and email addresses:
* https://help.github.com/articles/setting-your-commit-email-address-in-git/
* https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean
* https://help.github.com/articles/about-commit-email-addresses/
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
Note that if you have more than one Git identity, it is important to verify that you are logged in with the same ID with which you signed the CLA, before you commit changes. If not, your PR will fail the CLA check.
<hr>
[angular-group]: https://groups.google.com/forum/#!forum/angular
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md

View File

@ -1,11 +1,14 @@
workspace(name = "angular_src")
workspace(name = "angular")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
# Using a pre-release snapshot to pick up a commit that makes all nodejs_binary
# programs produce source-mapped stack traces.
RULES_NODEJS_VERSION = "926349cea4cd360afcd5647ccdd09d2d2fb471aa"
git_repository(
http_archive(
name = "build_bazel_rules_nodejs",
remote = "https://github.com/bazelbuild/rules_nodejs.git",
tag = "0.3.1",
url = "https://github.com/bazelbuild/rules_nodejs/archive/%s.zip" % RULES_NODEJS_VERSION,
strip_prefix = "rules_nodejs-%s" % RULES_NODEJS_VERSION,
sha256 = "5ba3c8c209078c2e3f0c6aa4abd01a1a561f92a5bfda04e25604af5f4734d69d",
)
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories")
@ -13,34 +16,34 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_reposi
check_bazel_version("0.9.0")
node_repositories(package_json = ["//:package.json"])
git_repository(
RULES_TYPESCRIPT_VERSION = "d3cc5cd72d89aee0e4c2553ae1b99c707ecbef4e"
http_archive(
name = "build_bazel_rules_typescript",
remote = "https://github.com/bazelbuild/rules_typescript.git",
# tag = "0.7.1+",
commit = "89d2c75066bea3d9c942f29dd1d2ea543c58d6d5"
url = "https://github.com/bazelbuild/rules_typescript/archive/%s.zip" % RULES_TYPESCRIPT_VERSION,
strip_prefix = "rules_typescript-%s" % RULES_TYPESCRIPT_VERSION,
sha256 = "a233fcca41c3e59f639ac71c396edb30e9e9716cf8ed5fb20b51ff8910d5d895",
)
load("@build_bazel_rules_typescript//:setup.bzl", "ts_setup_workspace")
load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
ts_setup_workspace()
local_repository(
name = "angular",
path = "packages/bazel",
)
local_repository(
name = "rxjs",
path = "node_modules/rxjs/src",
)
git_repository(
# This commit matches the version of buildifier in angular/ngcontainer
# If you change this, also check if it matches the version in the angular/ngcontainer
# version in /.circleci/config.yml
BAZEL_BUILDTOOLS_VERSION = "b3b620e8bcff18ed3378cd3f35ebeb7016d71f71"
http_archive(
name = "com_github_bazelbuild_buildtools",
remote = "https://github.com/bazelbuild/buildtools.git",
# Note, this commit matches the version of buildifier in angular/ngcontainer
# If you change this, also check if it matches the version in the angular/ngcontainer
# version in /.circleci/config.yml
commit = "b3b620e8bcff18ed3378cd3f35ebeb7016d71f71",
url = "https://github.com/bazelbuild/buildtools/archive/%s.zip" % BAZEL_BUILDTOOLS_VERSION,
strip_prefix = "buildtools-%s" % BAZEL_BUILDTOOLS_VERSION,
sha256 = "dad19224258ed67cbdbae9b7befb785c3b966e5a33b04b3ce58ddb7824b97d73",
)
http_archive(

View File

@ -62,6 +62,9 @@
"styleExt": "scss",
"component": {
"inlineStyle": true
},
"build": {
"namedChunks": true
}
}
}

View File

@ -4,7 +4,7 @@ Everything in this folder is part of the documentation project. This includes
* the web site for displaying the documentation
* the dgeni configuration for converting source files to rendered files that can be viewed in the web site.
* the tooling for setting up examples for development; and generating plunkers and zip files from the examples.
* the tooling for setting up examples for development; and generating live-example and zip files from the examples.
## Developer tasks
@ -13,7 +13,7 @@ You should run all these tasks from the `angular/aio` folder.
Here are the most important tasks you might need to use:
* `yarn` - install all the dependencies.
* `yarn setup` - install all the dependencies, boilerplate, plunkers, zips and run dgeni on the docs.
* `yarn setup` - install all the dependencies, boilerplate, stackblitz, zips and run dgeni on the docs.
* `yarn setup-local` - same as `setup`, but use the locally built Angular packages for aio and docs examples boilerplate.
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
@ -32,7 +32,7 @@ Here are the most important tasks you might need to use:
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally. Add the option `--local` to use your local version of Angular contained in the "dist" folder.
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
* `yarn generate-plunkers` - generate the plunker files that are used by the `live-example` tags in the docs.
* `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
* `yarn example-e2e` - run all e2e tests for examples
@ -68,6 +68,11 @@ The content is written in markdown.
All other content is written using markdown in text files, located in the `angular/aio/content` folder.
More specifically, there are sub-folders that contain particular types of content: guides, tutorial and marketing.
* **Code examples**: code examples need to be testable to ensure their accuracy.
Also, our examples have a specific look and feel and allow the user to copy the source code. For larger
examples they are rendered in a tabbed interface (e.g. template, HTML, and TypeScript on separate
tabs). Additionally, some are live examples, which provide links where the code can be edited, executed, and/or downloaded. For details on working with code examples, please read the [Code snippets](https://angular.io/guide/docs-style-guide#code-snippets), [Source code markup](https://angular.io/guide/docs-style-guide#source-code-markup), and [Live examples](https://angular.io/guide/docs-style-guide#live-examples) pages of the [Authors Style Guide](https://angular.io/guide/docs-style-guide).
We use the [dgeni](https://github.com/angular/dgeni) tool to convert these files into docs that can be viewed in the doc-viewer.
The [Authors Style Guide](https://angular.io/guide/docs-style-guide) prescribes guidelines for
@ -100,8 +105,7 @@ The general setup is as follows:
* Open a terminal, ensure the dependencies are installed; run an initial doc generation; then start the doc-viewer:
```bash
yarn
yarn docs
yarn setup
yarn start
```

BIN
aio/content/examples/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -76,8 +76,8 @@ aot-compiler/**/*.factory.d.ts
# universal
!universal/webpack.server.config.js
# plunkers
*plnkr.no-link.html
# stackblitz
*stackblitz.no-link.html
# ngUpgrade testing
!upgrade-phonecat-*/**/karma.conf.js

View File

@ -1,6 +1,5 @@
{
"description": "AngularJS to Angular Quick Reference",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js",

View File

@ -1,6 +1,5 @@
{
"description": "Angular Animations",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js"

View File

@ -1,9 +1,9 @@
{
"description": "Intro to Angular",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js",
"!app/hero-list.component.1.*"
]
"!**/*.[1].*"
],
"file": "src/app/app.module.ts"
}

View File

@ -1,4 +1,4 @@
// Not used. Keep away from plunker
// Not used. Keep away from stackblitz
// Keeps ATLS from complaining about undeclared directives.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

View File

@ -1,10 +1,9 @@
{
"description": "Attribute Directive",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js",
"!app/*.[0,1,2,3].*"
"!**/*.[1,2,3].*"
],
"tags": ["attribute", "directive"]
}

View File

@ -0,0 +1,8 @@
{
"server": {
"baseDir": "src",
"routes": {
"/node_modules": "node_modules"
}
}
}

View File

@ -0,0 +1,14 @@
import { AppPage } from './app.po';
describe('feature-modules App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display message saying app works', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('app works!');
});
});

View File

@ -0,0 +1,3 @@
<h1>
{{title}}
</h1>

View File

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

View File

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
}

View File

@ -0,0 +1,34 @@
// #docplaster
// #docregion whole-ngmodule
// imports
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
// #docregion directive-import
import { ItemDirective } from './item.directive';
// #enddocregion directive-import
// @NgModule decorator with its metadata
@NgModule({
// #docregion declarations
declarations: [
AppComponent,
ItemDirective
],
// #enddocregion declarations
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
// #enddocregion whole-ngmodule

View File

@ -0,0 +1,8 @@
import { ItemDirective } from './item.directive';
describe('ItemDirective', () => {
it('should create an instance', () => {
const directive = new ItemDirective();
expect(directive).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
// #docplaster
// #docregion directive
import { Directive } from '@angular/core';
@Directive({
selector: '[appItem]'
})
export class ItemDirective {
// code goes here
constructor() { }
}
// #enddocregion directive

View File

@ -0,0 +1,14 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>NgmoduleDemo</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root>Loading...</app-root>
</body>
</html>

View File

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

View File

@ -0,0 +1,10 @@
{
"description": "Bootstrapping",
"files": [
"!**/*.d.ts",
"!**/*.js",
"!**/*.[1,2].*"
],
"file": "src/app/app.component.ts",
"tags": ["ngmodules"]
}

View File

@ -1,6 +1,5 @@
{
"description": "Component Communication Cookbook samples",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js"

View File

@ -1,5 +1,6 @@
/* #docregion import */
@import 'hero-details-box.css';
/* The AOT compiler needs the `./` to show that this is local */
@import './hero-details-box.css';
/* #enddocregion import */
/* #docregion host */

View File

@ -5,7 +5,8 @@ import { Hero } from './hero';
@Component({
selector: 'app-hero-team',
template: `
<link rel="stylesheet" href="assets/hero-team.component.css">
<!-- We must use a relative URL so that the AOT compiler can find the stylesheet -->
<link rel="stylesheet" href="../assets/hero-team.component.css">
<h3>Team</h3>
<ul>
<li *ngFor="let member of hero.team">

View File

@ -1,6 +1,5 @@
{
"description": "Component Styles",
"basePath": "src/",
"files": [
"!**/*.d.ts",
"!**/*.js",

View File

@ -1,6 +1,5 @@
{
"description": "Dependency Injection",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js",

View File

@ -1,6 +1,5 @@
{
"description": "Dependency Injection",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js",

View File

@ -1,6 +1,5 @@
{
"description": "Displaying Data",
"basePath": "src/",
"files": [
"!**/*.d.ts",
"!**/*.js",

View File

@ -1,9 +0,0 @@
{
"description": "Second authors style guide plunker (non-executing)",
"basePath": "src/",
"files": [
"index.2.html"
],
"main": "index.2.html",
"tags": ["author", "style guide"]
}

View File

@ -0,0 +1,8 @@
{
"description": "Second authors style guide stackblitz (non-executing)",
"files": [
"src/index.2.html"
],
"main": "src/index.2.html",
"tags": ["author", "style guide"]
}

View File

@ -1,6 +1,5 @@
{
"description": "Authors style guide",
"basePath": "src/",
"files": [
"!**/*.d.ts",
"!**/*.js",

View File

@ -1,6 +1,5 @@
{
"description": "Dynamic Component Loader",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js"

View File

@ -1,6 +1,5 @@
{
"description": "Dynamic Form",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js",

View File

@ -0,0 +1,17 @@
import { AppPage } from './app.po';
describe('feature-modules App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display message saying app works', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('app works!');
});
});

View File

@ -0,0 +1,4 @@
{
"build": "build:cli",
"run": "serve:cli"
}

View File

@ -0,0 +1,9 @@
<!-- #docplaster -->
<!-- #docregion app-component-template -->
<h1>
{{title}}
</h1>
<!-- add the selector from the CustomerDashboardComponent -->
<app-customer-dashboard></app-customer-dashboard>
<!-- #enddocregion app-component-template -->

View File

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

View File

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
}

View File

@ -0,0 +1,27 @@
// #docplaster
// #docregion app-module
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
// import the feature module here so you can add it to the imports array below
import { CustomerDashboardModule } from './customer-dashboard/customer-dashboard.module';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
CustomerDashboardModule // add the feature module here
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
// #enddocregion app-module

View File

@ -0,0 +1,34 @@
// #docplaster
// #docregion customer-dashboard
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
// #enddocregion customer-dashboard
// #docregion customer-dashboard-component
// import the new component
import { CustomerDashboardComponent } from './customer-dashboard/customer-dashboard.component';
// #enddocregion customer-dashboard-component
// #docregion customer-dashboard-component
@NgModule({
imports: [
CommonModule
],
declarations: [
CustomerDashboardComponent
],
// #enddocregion customer-dashboard-component
// #docregion component-exports
exports: [
CustomerDashboardComponent
]
// #enddocregion component-exports
// #docregion customer-dashboard-component
})
// #enddocregion customer-dashboard-component
// #docregion customer-dashboard
export class CustomerDashboardModule { }
// #enddocregion customer-dashboard

View File

@ -0,0 +1,7 @@
<!-- #docplaster -->
<!-- #docregion feature-template -->
<p>
customer-dashboard works!
</p>
<!-- #enddocregion feature-template -->

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CustomerDashboardComponent } from './customer-dashboard.component';
describe('CustomerDashboardComponent', () => {
let component: CustomerDashboardComponent;
let fixture: ComponentFixture<CustomerDashboardComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CustomerDashboardComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CustomerDashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-customer-dashboard',
templateUrl: './customer-dashboard.component.html',
styleUrls: ['./customer-dashboard.component.css']
})
export class CustomerDashboardComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@ -0,0 +1,14 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Feature Modules</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root>Loading...</app-root>
</body>
</html>

View File

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

View File

@ -0,0 +1,10 @@
{
"description": "Feature Modules",
"files": [
"!**/*.d.ts",
"!**/*.js",
"!**/*.[1,2].*"
],
"file": "src/app/app.component.ts",
"tags": ["feature modules"]
}

View File

@ -15,6 +15,7 @@ describe('Form Validation Tests', function () {
});
tests('Template-Driven Form');
bobTests();
});
describe('Reactive form', () => {

View File

@ -20,7 +20,7 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
// #enddocregion directive-providers
})
export class ForbiddenValidatorDirective implements Validator {
@Input() forbiddenName: string;
@Input('appForbiddenName') forbiddenName: string;
validate(control: AbstractControl): {[key: string]: any} {
return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control)

View File

@ -12,7 +12,7 @@
<!-- #docregion name-with-error-msg -->
<!-- #docregion name-input -->
<input id="name" name="name" class="form-control"
required minlength="4" forbiddenName="bob"
required minlength="4" appForbiddenName="bob"
[(ngModel)]="hero.name" #name="ngModel" >
<!-- #enddocregion name-input -->

View File

@ -1,6 +1,5 @@
{
"description": "Validation",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js"

View File

@ -1,6 +1,5 @@
{
"description": "Forms",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js"

View File

@ -1,6 +1,5 @@
{
"description": "Hierarchical Dependency Injection",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js"

View File

@ -1,138 +0,0 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Server Communication', function () {
beforeAll(function () {
browser.get('');
});
describe('Tour of Heroes (Observable)', function () {
let initialHeroCount = 4;
let newHeroName = 'Mr. IQ';
let heroCountAfterAdd = 5;
let heroListComp = element(by.tagName('hero-list'));
let addButton = heroListComp.element(by.tagName('button'));
let heroTags = heroListComp.all(by.tagName('li'));
let heroNameInput = heroListComp.element(by.tagName('input'));
it('should exist', function() {
expect(heroListComp).toBeDefined('<hero-list> must exist');
});
it('should display ' + initialHeroCount + ' heroes after init', function () {
expect(heroTags.count()).toBe(initialHeroCount);
});
it('should not add hero with empty name', function () {
expect(addButton).toBeDefined('"Add Hero" button must be defined');
addButton.click().then(function() {
expect(heroTags.count()).toBe(initialHeroCount, 'No new hero should be added');
});
});
it('should add a new hero to the list', function () {
expect(heroNameInput).toBeDefined('<input> for hero name must exist');
expect(addButton).toBeDefined('"Add Hero" button must be defined');
heroNameInput.sendKeys(newHeroName);
addButton.click().then(function() {
expect(heroTags.count()).toBe(heroCountAfterAdd, 'A new hero should be added');
let newHeroInList = heroTags.get(heroCountAfterAdd - 1).getText();
expect(newHeroInList).toBe(newHeroName, 'The hero should be added to the end of the list');
});
});
});
describe('Wikipedia Demo', function () {
it('should initialize the demo with empty result list', function () {
let myWikiComp = element(by.tagName('my-wiki'));
expect(myWikiComp).toBeDefined('<my-wiki> must exist');
let resultList = myWikiComp.all(by.tagName('li'));
expect(resultList.count()).toBe(0, 'result list must be empty');
});
describe('Fetches after each keystroke', function () {
it('should fetch results after "B"', function(done: any) {
testForRefreshedResult('B', done);
});
it('should fetch results after "Ba"', function(done: any) {
testForRefreshedResult('a', done);
});
it('should fetch results after "Bas"', function(done: any) {
testForRefreshedResult('s', done);
});
it('should fetch results after "Basic"', function(done: any) {
testForRefreshedResult('ic', done);
});
});
function testForRefreshedResult(keyPressed: string, done: () => void) {
testForResult('my-wiki', keyPressed, false, done);
}
});
describe('Smarter Wikipedia Demo', function () {
it('should initialize the demo with empty result list', function () {
let myWikiSmartComp = element(by.tagName('my-wiki-smart'));
expect(myWikiSmartComp).toBeDefined('<my-wiki-smart> must exist');
let resultList = myWikiSmartComp.all(by.tagName('li'));
expect(resultList.count()).toBe(0, 'result list must be empty');
});
it('should fetch results after "Java"', function(done: any) {
testForNewResult('Java', done);
});
it('should fetch results after "JavaS"', function(done: any) {
testForStaleResult('S', done);
});
it('should fetch results after "JavaSc"', function(done: any) {
testForStaleResult('c', done);
});
it('should fetch results after "JavaScript"', function(done: any) {
testForStaleResult('ript', done);
});
function testForNewResult(keyPressed: string, done: () => void) {
testForResult('my-wiki-smart', keyPressed, false, done);
}
function testForStaleResult(keyPressed: string, done: () => void) {
testForResult('my-wiki-smart', keyPressed, true, done);
}
});
function testForResult(componentTagName: string, keyPressed: string, hasListBeforeSearch: boolean, done: () => void) {
let searchWait = 1000; // Wait for wikipedia but not so long that tests timeout
let wikiComponent = element(by.tagName(componentTagName));
expect(wikiComponent).toBeDefined('<' + componentTagName + '> must exist');
let searchBox = wikiComponent.element(by.tagName('input'));
expect(searchBox).toBeDefined('<input> for search must exist');
searchBox.sendKeys(keyPressed).then(function () {
let resultList = wikiComponent.all(by.tagName('li'));
if (hasListBeforeSearch) {
expect(resultList.count()).toBeGreaterThan(0, 'result list should not be empty before search');
}
setTimeout(function() {
expect(resultList.count()).toBeGreaterThan(0, 'result list should not be empty after search');
done();
}, searchWait);
});
}
});

View File

@ -0,0 +1,139 @@
import { browser, element, by, ElementFinder } from 'protractor';
import { resolve } from 'path';
const page = {
configClearButton: element.all(by.css('app-config > div button')).get(2),
configErrorButton: element.all(by.css('app-config > div button')).get(3),
configErrorMessage: element(by.css('app-config p')),
configGetButton: element.all(by.css('app-config > div button')).get(0),
configGetResponseButton: element.all(by.css('app-config > div button')).get(1),
configSpan: element(by.css('app-config span')),
downloadButton: element.all(by.css('app-downloader button')).get(0),
downloadClearButton: element.all(by.css('app-downloader button')).get(1),
downloadMessage: element(by.css('app-downloader p')),
heroesListAddButton: element.all(by.css('app-heroes > div button')).get(0),
heroesListInput: element(by.css('app-heroes > div input')),
heroesListSearchButton: element.all(by.css('app-heroes > div button')).get(1),
heroesListItems: element.all(by.css('app-heroes ul li')),
logClearButton: element(by.css('app-messages button')),
logList: element(by.css('app-messages ol')),
logListItems: element.all(by.css('app-messages ol li')),
searchInput: element(by.css('app-package-search input#name')),
searchListItems: element.all(by.css('app-package-search li')),
uploadInput: element(by.css('app-uploader input')),
uploadMessage: element(by.css('app-uploader p'))
};
let checkLogForMessage = (message: string) => {
expect(page.logList.getText()).toContain(message);
};
describe('Http Tests', function() {
beforeEach(() => {
browser.get('');
});
describe('Heroes', () => {
it('retrieves the list of heroes at startup', () => {
expect(page.heroesListItems.count()).toBe(4);
expect(page.heroesListItems.get(0).getText()).toContain('Mr. Nice');
checkLogForMessage('GET "api/heroes"');
});
it('makes a POST to add a new hero', () => {
page.heroesListInput.sendKeys('Magneta');
page.heroesListAddButton.click();
expect(page.heroesListItems.count()).toBe(5);
checkLogForMessage('POST "api/heroes"');
});
it('makes a GET to search for a hero', () => {
page.heroesListInput.sendKeys('Celeritas');
page.heroesListSearchButton.click();
checkLogForMessage('GET "api/heroes?name=Celeritas"');
});
});
describe('Messages', () => {
it('can clear the logs', () => {
expect(page.logListItems.count()).toBe(1);
page.logClearButton.click();
expect(page.logListItems.count()).toBe(0);
});
});
describe('Configuration', () => {
it('can fetch the configuration JSON file', () => {
page.configGetButton.click();
checkLogForMessage('GET "assets/config.json"');
expect(page.configSpan.getText()).toContain('Heroes API URL is "api/heroes"');
expect(page.configSpan.getText()).toContain('Textfile URL is "assets/textfile.txt"');
});
it('can fetch the configuration JSON file with headers', () => {
page.configGetResponseButton.click();
checkLogForMessage('GET "assets/config.json"');
expect(page.configSpan.getText()).toContain('Response headers:');
expect(page.configSpan.getText()).toContain('content-type: application/json; charset=UTF-8');
});
it('can clear the configuration log', () => {
page.configGetResponseButton.click();
expect(page.configSpan.getText()).toContain('Response headers:');
page.configClearButton.click();
expect(page.configSpan.isPresent()).toBeFalsy();
});
it('throws an error for a non valid url', () => {
page.configErrorButton.click();
checkLogForMessage('GET "not/a/real/url"');
expect(page.configErrorMessage.getText()).toContain('"Something bad happened; please try again later."');
});
});
describe('Download', () => {
it('can download a txt file and show it', () => {
page.downloadButton.click();
checkLogForMessage('DownloaderService downloaded "assets/textfile.txt"');
checkLogForMessage('GET "assets/textfile.txt"');
expect(page.downloadMessage.getText()).toContain('Contents: "This is the downloaded text file "');
});
it('can clear the log of the download', () => {
page.downloadButton.click();
expect(page.downloadMessage.getText()).toContain('Contents: "This is the downloaded text file "');
page.downloadClearButton.click();
expect(page.downloadMessage.isPresent()).toBeFalsy();
});
});
describe('Upload', () => {
it('can upload a file', () => {
const filename = 'app.po.ts';
const url = resolve(__dirname, filename);
page.uploadInput.sendKeys(url);
checkLogForMessage('POST "/upload/file" succeeded in');
expect(page.uploadMessage.getText()).toContain(
`File "${filename}" was completely uploaded!`);
});
});
describe('PackageSearch', () => {
it('can search for npm package and find in cache', () => {
const packageName = 'angular';
page.searchInput.sendKeys(packageName);
checkLogForMessage(
'Caching response from "https://npmsearch.com/query?q=angular"');
expect(page.searchListItems.count()).toBeGreaterThan(1, 'angular items');
page.searchInput.clear();
page.searchInput.sendKeys(' ');
expect(page.searchListItems.count()).toBe(0, 'search empty');
page.searchInput.clear();
page.searchInput.sendKeys(packageName);
checkLogForMessage(
'Found cached response for "https://npmsearch.com/query?q=angular"');
});
});
});

View File

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

View File

@ -0,0 +1,18 @@
{
"description": "Http Guide Testing",
"files":[
"src/app/heroes/heroes.service.ts",
"src/app/heroes/heroes.service.spec.ts",
"src/app/http-error-handler.service.ts",
"src/app/message.service.ts",
"src/testing/*.ts",
"src/styles.css",
"src/test.css",
"src/main-specs.ts",
"src/index-specs.html"
],
"main": "src/index-specs.html",
"tags": ["http", "testing"]
}

View File

@ -0,0 +1,24 @@
<h1>HTTP Sample</h1>
<div>
<input type="checkbox" id="heroes" [checked]="toggleHeroes" (click)="toggleHeroes()">
<label for="heroes">Heroes</label>
<input type="checkbox" id="config" [checked]="showConfig" (click)="toggleConfig()">
<label for="config">Config</label>
<input type="checkbox" id="downloader" [checked]="showDownloader" (click)="toggleDownloader()">
<label for="downloader">Downloader</label>
<input type="checkbox" id="uploader" [checked]="showUploader" (click)="toggleUploader()">
<label for="uploader">Uploader</label>
<input type="checkbox" id="search" [checked]="showSearch" (click)="toggleSearch()">
<label for="search">Search</label>
</div>
<app-heroes *ngIf="showHeroes"></app-heroes>
<app-messages></app-messages>
<app-config *ngIf="showConfig"></app-config>
<app-downloader *ngIf="showDownloader"></app-downloader>
<app-uploader *ngIf="showUploader"></app-uploader>
<app-package-search *ngIf="showSearch"></app-package-search>

View File

@ -1,13 +1,19 @@
// #docregion
import { Component } from '@angular/core';
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<hero-list></hero-list>
<hero-list-promise></hero-list-promise>
<my-wiki></my-wiki>
<my-wiki-smart></my-wiki-smart>
`
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent { }
export class AppComponent {
showHeroes = true;
showConfig = true;
showDownloader = true;
showUploader = true;
showSearch = true;
toggleHeroes() { this.showHeroes = !this.showHeroes; }
toggleConfig() { this.showConfig = !this.showConfig; }
toggleDownloader() { this.showDownloader = !this.showDownloader; }
toggleUploader() { this.showUploader = !this.showUploader; }
toggleSearch() { this.showSearch = !this.showSearch; }
}

View File

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

View File

@ -1,46 +1,89 @@
// #docplaster
// #docregion
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule, JsonpModule } from '@angular/http';
// #docregion sketch
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
// #enddocregion sketch
import { FormsModule } from '@angular/forms';
// #docregion sketch
import { HttpClientModule } from '@angular/common/http';
// #enddocregion sketch
import { HttpClientXsrfModule } from '@angular/common/http';
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService } from './in-memory-data.service';
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { HeroData } from './hero-data';
import { requestOptionsProvider } from './default-request-options.service';
import { RequestCache, RequestCacheWithMap } from './request-cache.service';
import { AppComponent } from './app.component';
import { AppComponent } from './app.component';
import { AuthService } from './auth.service';
import { ConfigComponent } from './config/config.component';
import { DownloaderComponent } from './downloader/downloader.component';
import { HeroesComponent } from './heroes/heroes.component';
import { HttpErrorHandler } from './http-error-handler.service';
import { MessageService } from './message.service';
import { MessagesComponent } from './messages/messages.component';
import { PackageSearchComponent } from './package-search/package-search.component';
import { UploaderComponent } from './uploader/uploader.component';
import { HeroListComponent } from './toh/hero-list.component';
import { HeroListPromiseComponent } from './toh/hero-list.component.promise';
import { WikiComponent } from './wiki/wiki.component';
import { WikiSmartComponent } from './wiki/wiki-smart.component';
import { httpInterceptorProviders } from './http-interceptors/index';
// #docregion sketch
@NgModule({
// #docregion xsrf
imports: [
// #enddocregion xsrf
BrowserModule,
// #enddocregion sketch
FormsModule,
HttpModule,
JsonpModule,
// #docregion in-mem-web-api
InMemoryWebApiModule.forRoot(HeroData)
// #enddocregion in-mem-web-api
// #docregion sketch
// import HttpClientModule after BrowserModule.
// #docregion xsrf
HttpClientModule,
// #enddocregion sketch
HttpClientXsrfModule.withOptions({
cookieName: 'My-Xsrf-Cookie',
headerName: 'My-Xsrf-Header',
}),
// #enddocregion xsrf
// The HttpClientInMemoryWebApiModule module intercepts HTTP requests
// and returns simulated server responses.
// Remove it when a real server is ready to receive requests.
HttpClientInMemoryWebApiModule.forRoot(
InMemoryDataService, {
dataEncapsulation: false,
passThruUnknownUrl: true,
put204: false // return entity after PUT/update
}
)
// #docregion sketch, xsrf
],
// #enddocregion xsrf
declarations: [
AppComponent,
HeroListComponent,
HeroListPromiseComponent,
WikiComponent,
WikiSmartComponent
// #enddocregion sketch
ConfigComponent,
DownloaderComponent,
HeroesComponent,
MessagesComponent,
UploaderComponent,
PackageSearchComponent,
// #docregion sketch
],
// #docregion provide-default-request-options
providers: [ requestOptionsProvider ],
// #enddocregion provide-default-request-options
// #enddocregion sketch
// #docregion interceptor-providers
providers: [
// #enddocregion interceptor-providers
AuthService,
HttpErrorHandler,
MessageService,
{ provide: RequestCache, useClass: RequestCacheWithMap },
// #docregion interceptor-providers
httpInterceptorProviders
],
// #enddocregion interceptor-providers
// #docregion sketch
bootstrap: [ AppComponent ]
})
export class AppModule {}
// #enddocregion sketch

View File

@ -0,0 +1,9 @@
import { Injectable } from '@angular/core';
/** Mock client-side authentication/authorization service */
@Injectable()
export class AuthService {
getAuthorizationToken() {
return 'some-auth-token';
}
}

View File

@ -0,0 +1,18 @@
<h3>Get configuration from JSON file</h3>
<div>
<button (click)="clear(); showConfig()">get</button>
<button (click)="clear(); showConfigResponse()">getResponse</button>
<button (click)="clear()">clear</button>
<button (click)="clear(); makeError()">error</button>
<span *ngIf="config">
<p>Heroes API URL is "{{config.heroesUrl}}"</p>
<p>Textfile URL is "{{config.textfile}}"</p>
<div *ngIf="headers">
Response headers:
<ul>
<li *ngFor="let header of headers">{{header}}</li>
</ul>
</div>
</span>
</div>
<p *ngIf="error" class="error">{{error | json}}</p>

View File

@ -0,0 +1,78 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
import { Config, ConfigService } from './config.service';
import { MessageService } from '../message.service';
@Component({
selector: 'app-config',
templateUrl: './config.component.html',
providers: [ ConfigService ],
styles: ['.error {color: red;}']
})
export class ConfigComponent {
error: any;
headers: string[];
// #docregion v2
config: Config;
// #enddocregion v2
constructor(private configService: ConfigService) {}
clear() {
this.config = undefined;
this.error = undefined;
this.headers = undefined;
}
// #docregion v1, v2, v3
showConfig() {
this.configService.getConfig()
// #enddocregion v1, v2
.subscribe(
data => this.config = { ...data }, // success path
error => this.error = error // error path
);
}
// #enddocregion v3
showConfig_v1() {
this.configService.getConfig_1()
// #docregion v1, v1_callback
.subscribe(data => this.config = {
heroesUrl: data['heroesUrl'],
textfile: data['textfile']
});
// #enddocregion v1_callback
}
// #enddocregion v1
showConfig_v2() {
this.configService.getConfig()
// #docregion v2, v2_callback
// clone the data object, using its known Config shape
.subscribe(data => this.config = { ...data });
// #enddocregion v2_callback
}
// #enddocregion v2
// #docregion showConfigResponse
showConfigResponse() {
this.configService.getConfigResponse()
// resp is of type `HttpResponse<Config>`
.subscribe(resp => {
// display its headers
const keys = resp.headers.keys();
this.headers = keys.map(key =>
`${key}: ${resp.headers.get(key)}`);
// access the body directly, which is typed as `Config`.
this.config = { ... resp.body };
});
}
// #enddocregion showConfigResponse
makeError() {
this.configService.makeIntentionalError().subscribe(null, error => this.error = error );
}
}
// #enddocregion

View File

@ -0,0 +1,100 @@
// #docplaster
// #docregion , proto
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
// #enddocregion proto
import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
// #docregion rxjs-imports
import { Observable } from 'rxjs/Observable';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
import { catchError, retry } from 'rxjs/operators';
// #enddocregion rxjs-imports
// #docregion config-interface
export interface Config {
heroesUrl: string;
textfile: string;
}
// #enddocregion config-interface
// #docregion proto
@Injectable()
export class ConfigService {
// #enddocregion proto
// #docregion getConfig_1
configUrl = 'assets/config.json';
// #enddocregion getConfig_1
// #docregion proto
constructor(private http: HttpClient) { }
// #enddocregion proto
// #docregion getConfig, getConfig_1, getConfig_2, getConfig_3
getConfig() {
// #enddocregion getConfig_1, getConfig_2, getConfig_3
return this.http.get<Config>(this.configUrl)
.pipe(
retry(3), // retry a failed request up to 3 times
catchError(this.handleError) // then handle the error
);
}
// #enddocregion getConfig
getConfig_1() {
// #docregion getConfig_1
return this.http.get(this.configUrl);
}
// #enddocregion getConfig_1
getConfig_2() {
// #docregion getConfig_2
// now returns an Observable of Config
return this.http.get<Config>(this.configUrl);
}
// #enddocregion getConfig_2
getConfig_3() {
// #docregion getConfig_3
return this.http.get<Config>(this.configUrl)
.pipe(
catchError(this.handleError)
);
}
// #enddocregion getConfig_3
// #docregion getConfigResponse
getConfigResponse(): Observable<HttpResponse<Config>> {
return this.http.get<Config>(
this.configUrl, { observe: 'response' });
}
// #enddocregion getConfigResponse
// #docregion handleError
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
// return an ErrorObservable with a user-facing error message
return new ErrorObservable(
'Something bad happened; please try again later.');
};
// #enddocregion handleError
makeIntentionalError() {
return this.http.get('not/a/real/url')
.pipe(
catchError(this.handleError)
);
}
// #docregion proto
}
// #enddocregion proto

View File

@ -1,16 +0,0 @@
// #docregion
import { Injectable } from '@angular/core';
import { BaseRequestOptions, RequestOptions } from '@angular/http';
@Injectable()
export class DefaultRequestOptions extends BaseRequestOptions {
constructor() {
super();
// Set the default 'Content-Type' header
this.headers.set('Content-Type', 'application/json');
}
}
export const requestOptionsProvider = { provide: RequestOptions, useClass: DefaultRequestOptions };

View File

@ -0,0 +1,4 @@
<h3>Download the textfile</h3>
<button (click)="download()">Download</button>
<button (click)="clear()">clear</button>
<p *ngIf="contents">Contents: "{{contents}}"</p>

View File

@ -0,0 +1,23 @@
import { Component } from '@angular/core';
import { DownloaderService } from './downloader.service';
@Component({
selector: 'app-downloader',
templateUrl: './downloader.component.html',
providers: [ DownloaderService ]
})
export class DownloaderComponent {
contents: string;
constructor(private downloaderService: DownloaderService) {}
clear() {
this.contents = undefined;
}
// #docregion download
download() {
this.downloaderService.getTextFile('assets/textfile.txt')
.subscribe(results => this.contents = results);
}
// #enddocregion download
}

View File

@ -0,0 +1,39 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { tap } from 'rxjs/operators';
import { MessageService } from '../message.service';
@Injectable()
export class DownloaderService {
constructor(
private http: HttpClient,
private messageService: MessageService) { }
// #docregion getTextFile
getTextFile(filename: string) {
// The Observable returned by get() is of type Observable<string>
// because a text response was specified.
// There's no need to pass a <string> type parameter to get().
return this.http.get(filename, {responseType: 'text'})
.pipe(
tap( // Log the result or error
data => this.log(filename, data),
error => this.logError(filename, error)
)
);
}
// #enddocregion getTextFile
private log(filename: string, data: string) {
const message = `DownloaderService downloaded "${filename}" and got "${data}".`;
this.messageService.add(message);
}
private logError(filename: string, error: any) {
const message = `DownloaderService failed to download "${filename}"; got error "${error.message}".`;
console.error(message);
this.messageService.add(message);
}
}

View File

@ -1,13 +0,0 @@
// #docregion
import { InMemoryDbService } from 'angular-in-memory-web-api';
export class HeroData implements InMemoryDbService {
createDb() {
let heroes = [
{ id: 1, name: 'Windstorm' },
{ id: 2, name: 'Bombasto' },
{ id: 3, name: 'Magneta' },
{ id: 4, name: 'Tornado' }
];
return {heroes};
}
}

View File

@ -1,8 +0,0 @@
{
"data": [
{ "id": 1, "name": "Windstorm" },
{ "id": 2, "name": "Bombasto" },
{ "id": 3, "name": "Magneta" },
{ "id": 4, "name": "Tornado" }
]
}

View File

@ -0,0 +1,4 @@
export interface Hero {
id: number;
name: string;
}

View File

@ -0,0 +1,89 @@
/* HeroesComponent's private CSS styles */
.heroes {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.heroes li {
position: relative;
cursor: pointer;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
width: 19em;
}
.heroes li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.heroes a {
color: #888;
text-decoration: none;
position: relative;
display: block;
width: 250px;
}
.heroes a:hover {
color:#607D8B;
}
.heroes .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #607D8B;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
min-width: 16px;
text-align: right;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
.button {
background-color: #eee;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
font-family: Arial;
}
button:hover {
background-color: #cfd8dc;
}
button.delete {
position: relative;
left: 24em;
top: -32px;
background-color: gray !important;
color: white;
display: inherit;
padding: 5px 8px;
width: 2em;
}
input {
font-size: 100%;
margin-bottom: 2px;
width: 11em;
}
.heroes input {
position: relative;
top: -3px;
width: 12em;
}

View File

@ -0,0 +1,32 @@
<h3>Heroes</h3>
<!-- #docregion add -->
<div>
<label>Hero name:
<input #heroName />
</label>
<!-- (click) passes input value to add() and then clears the input -->
<button (click)="add(heroName.value); heroName.value=''">
add
</button>
<button (click)="search(heroName.value)">
search
</button>
</div>
<!-- #enddocregion add -->
<!-- #docregion list -->
<ul class="heroes">
<li *ngFor="let hero of heroes">
<a (click)="edit(hero)">
<span class="badge">{{ hero.id || -1 }}</span>
<span *ngIf="hero!==editHero">{{hero.name}}</span>
<input *ngIf="hero===editHero" [(ngModel)]="hero.name"
(blur)="update()" (keyup.enter)="update()">
</a>
<!-- #docregion delete -->
<button class="delete" title="delete hero"
(click)="delete(hero)">x</button>
<!-- #enddocregion delete -->
</li>
</ul>
<!-- #enddocregion list -->

View File

@ -0,0 +1,76 @@
import { Component, OnInit } from '@angular/core';
import { Hero } from './hero';
import { HeroesService } from './heroes.service';
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
providers: [ HeroesService ],
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
heroes: Hero[];
editHero: Hero; // the hero currently being edited
constructor(private heroesService: HeroesService) { }
ngOnInit() {
this.getHeroes();
}
getHeroes(): void {
this.heroesService.getHeroes()
.subscribe(heroes => this.heroes = heroes);
}
add(name: string): void {
this.editHero = undefined;
name = name.trim();
if (!name) { return; }
// The server will generate the id for this new hero
const newHero: Hero = { name } as Hero;
// #docregion add-hero-subscribe
this.heroesService.addHero(newHero)
.subscribe(hero => this.heroes.push(hero));
// #enddocregion add-hero-subscribe
}
delete(hero: Hero): void {
this.heroes = this.heroes.filter(h => h !== hero);
// #docregion delete-hero-subscribe
this.heroesService.deleteHero(hero.id).subscribe();
// #enddocregion delete-hero-subscribe
/*
// #docregion delete-hero-no-subscribe
// oops ... subscribe() is missing so nothing happens
this.heroesService.deleteHero(hero.id);
// #enddocregion delete-hero-no-subscribe
*/
}
edit(hero) {
this.editHero = hero;
}
search(searchTerm: string) {
this.editHero = undefined;
if (searchTerm) {
this.heroesService.searchHeroes(searchTerm)
.subscribe(heroes => this.heroes = heroes);
}
}
update() {
if (this.editHero) {
this.heroesService.updateHero(this.editHero)
.subscribe(hero => {
// replace the hero in the heroes list with update from server
const ix = hero ? this.heroes.findIndex(h => h.id === hero.id) : -1;
if (ix > -1) { this.heroes[ix] = hero; }
});
this.editHero = undefined;
}
}
}

View File

@ -0,0 +1,156 @@
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
// Other imports
import { TestBed } from '@angular/core/testing';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { Hero } from './hero';
import { HeroesService } from './heroes.service';
import { HttpErrorHandler } from '../http-error-handler.service';
import { MessageService } from '../message.service';
describe('HeroesService', () => {
let httpClient: HttpClient;
let httpTestingController: HttpTestingController;
let heroService: HeroesService;
beforeEach(() => {
TestBed.configureTestingModule({
// Import the HttpClient mocking services
imports: [ HttpClientTestingModule ],
// Provide the service-under-test and its dependencies
providers: [
HeroesService,
HttpErrorHandler,
MessageService
]
});
// Inject the http, test controller, and service-under-test
// as they will be referenced by each test.
httpClient = TestBed.get(HttpClient);
httpTestingController = TestBed.get(HttpTestingController);
heroService = TestBed.get(HeroesService);
});
afterEach(() => {
// After every test, assert that there are no more pending requests.
httpTestingController.verify();
});
/// HeroService method tests begin ///
describe('#getHeroes', () => {
let expectedHeroes: Hero[];
beforeEach(() => {
heroService = TestBed.get(HeroesService);
expectedHeroes = [
{ id: 1, name: 'A' },
{ id: 2, name: 'B' },
] as Hero[];
});
it('should return expected heroes (called once)', () => {
heroService.getHeroes().subscribe(
heroes => expect(heroes).toEqual(expectedHeroes, 'should return expected heroes'),
fail
);
// HeroService should have made one request to GET heroes from expected URL
const req = httpTestingController.expectOne(heroService.heroesUrl);
expect(req.request.method).toEqual('GET');
// Respond with the mock heroes
req.flush(expectedHeroes);
});
it('should be OK returning no heroes', () => {
heroService.getHeroes().subscribe(
heroes => expect(heroes.length).toEqual(0, 'should have empty heroes array'),
fail
);
const req = httpTestingController.expectOne(heroService.heroesUrl);
req.flush([]); // Respond with no heroes
});
// This service reports the error but finds a way to let the app keep going.
it('should turn 404 into an empty heroes result', () => {
heroService.getHeroes().subscribe(
heroes => expect(heroes.length).toEqual(0, 'should return empty heroes array'),
fail
);
const req = httpTestingController.expectOne(heroService.heroesUrl);
// respond with a 404 and the error message in the body
const msg = 'deliberate 404 error';
req.flush(msg, {status: 404, statusText: 'Not Found'});
});
it('should return expected heroes (called multiple times)', () => {
heroService.getHeroes().subscribe();
heroService.getHeroes().subscribe();
heroService.getHeroes().subscribe(
heroes => expect(heroes).toEqual(expectedHeroes, 'should return expected heroes'),
fail
);
const requests = httpTestingController.match(heroService.heroesUrl);
expect(requests.length).toEqual(3, 'calls to getHeroes()');
// Respond to each request with different mock hero results
requests[0].flush([]);
requests[1].flush([{id: 1, name: 'bob'}]);
requests[2].flush(expectedHeroes);
});
});
describe('#updateHero', () => {
// Expecting the query form of URL so should not 404 when id not found
const makeUrl = (id: number) => `${heroService.heroesUrl}/?id=${id}`;
it('should update a hero and return it', () => {
const updateHero: Hero = { id: 1, name: 'A' };
heroService.updateHero(updateHero).subscribe(
data => expect(data).toEqual(updateHero, 'should return the hero'),
fail
);
// HeroService should have made one request to PUT hero
const req = httpTestingController.expectOne(heroService.heroesUrl);
expect(req.request.method).toEqual('PUT');
expect(req.request.body).toEqual(updateHero);
// Expect server to return the hero after PUT
const expectedResponse = new HttpResponse(
{ status: 200, statusText: 'OK', body: updateHero });
req.event(expectedResponse);
});
// This service reports the error but finds a way to let the app keep going.
it('should turn 404 error into return of the update hero', () => {
const updateHero: Hero = { id: 1, name: 'A' };
heroService.updateHero(updateHero).subscribe(
data => expect(data).toEqual(updateHero, 'should return the update hero'),
fail
);
const req = httpTestingController.expectOne(heroService.heroesUrl);
// respond with a 404 and the error message in the body
const msg = 'deliberate 404 error';
req.flush(msg, {status: 404, statusText: 'Not Found'});
});
});
// TODO: test other HeroService methods
});

View File

@ -0,0 +1,99 @@
// #docplaster
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
// #docregion http-options
import { HttpHeaders } from '@angular/common/http';
// #enddocregion http-options
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { catchError } from 'rxjs/operators';
import { Hero } from './hero';
import { HttpErrorHandler, HandleError } from '../http-error-handler.service';
// #docregion http-options
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'my-auth-token'
})
};
// #enddocregion http-options
@Injectable()
export class HeroesService {
heroesUrl = 'api/heroes'; // URL to web api
private handleError: HandleError;
constructor(
private http: HttpClient,
httpErrorHandler: HttpErrorHandler) {
this.handleError = httpErrorHandler.createHandleError('HeroesService');
}
/** GET heroes from the server */
getHeroes (): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
catchError(this.handleError('getHeroes', []))
);
}
// #docregion searchHeroes
/* GET heroes whose name contains search term */
searchHeroes(term: string): Observable<Hero[]> {
term = term.trim();
// Add safe, URL encoded search parameter if there is a search term
const options = term ?
{ params: new HttpParams().set('name', term) } : {};
return this.http.get<Hero[]>(this.heroesUrl, options)
.pipe(
catchError(this.handleError<Hero[]>('searchHeroes', []))
);
}
// #enddocregion searchHeroes
//////// Save methods //////////
// #docregion addHero
/** POST: add a new hero to the database */
addHero (hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions)
.pipe(
catchError(this.handleError('addHero', hero))
);
}
// #enddocregion addHero
// #docregion deleteHero
/** DELETE: delete the hero from the server */
deleteHero (id: number): Observable<{}> {
const url = `${this.heroesUrl}/${id}`; // DELETE api/heroes/42
return this.http.delete(url, httpOptions)
.pipe(
catchError(this.handleError('deleteHero'))
);
}
// #enddocregion deleteHero
// #docregion updateHero
/** PUT: update the hero on the server. Returns the updated hero upon success. */
updateHero (hero: Hero): Observable<Hero> {
// #enddocregion updateHero
// #docregion update-headers
httpOptions.headers =
httpOptions.headers.set('Authorization', 'my-new-auth-token');
// #enddocregion update-headers
// #docregion updateHero
return this.http.put<Hero>(this.heroesUrl, hero, httpOptions)
.pipe(
catchError(this.handleError('updateHero', hero))
);
}
// #enddocregion updateHero
}

View File

@ -0,0 +1,47 @@
import { Injectable } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { MessageService } from './message.service';
/** Type of the handleError function returned by HttpErrorHandler.createHandleError */
export type HandleError =
<T> (operation?: string, result?: T) => (error: HttpErrorResponse) => Observable<T>;
/** Handles HttpClient errors */
@Injectable()
export class HttpErrorHandler {
constructor(private messageService: MessageService) { }
/** Create curried handleError function that already knows the service name */
createHandleError = (serviceName = '') => <T>
(operation = 'operation', result = {} as T) => this.handleError(serviceName, operation, result);
/**
* Returns a function that handles Http operation failures.
* This error handler lets the app continue to run as if no error occurred.
* @param serviceName = name of the data service that attempted the operation
* @param operation - name of the operation that failed
* @param result - optional value to return as the observable result
*/
handleError<T> (serviceName = '', operation = 'operation', result = {} as T) {
return (error: HttpErrorResponse): Observable<T> => {
// TODO: send the error to remote logging infrastructure
console.error(error); // log to console instead
const message = (error.error instanceof ErrorEvent) ?
error.error.message :
`server returned code ${error.status} with body "${error.error}"`;
// TODO: better job of transforming error for user consumption
this.messageService.add(`${serviceName}: ${operation} failed: ${message}`);
// Let the app keep running by returning a safe result.
return of( result );
};
}
}

View File

@ -0,0 +1,42 @@
// #docplaster
import { Injectable } from '@angular/core';
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
// #docregion
import { AuthService } from '../auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private auth: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
// Get the auth token from the service.
const authToken = this.auth.getAuthorizationToken();
// #enddocregion
/*
* The verbose way:
// #docregion
// Clone the request and replace the original headers with
// cloned headers, updated with the authorization.
const authReq = req.clone({
headers: req.headers.set('Authorization', authToken)
});
// #enddocregion
*/
// #docregion set-header-shortcut
// Clone the request and set the new header in one step.
const authReq = req.clone({ setHeaders: { Authorization: authToken } });
// #enddocregion set-header-shortcut
// #docregion
// send cloned request with header to the next handler.
return next.handle(authReq);
}
}
// #enddocregion

View File

@ -0,0 +1,86 @@
// #docplaster
import { Injectable } from '@angular/core';
import {
HttpEvent, HttpHeaders, HttpRequest, HttpResponse,
HttpInterceptor, HttpHandler
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { startWith, tap } from 'rxjs/operators';
import { RequestCache } from '../request-cache.service';
import { searchUrl } from '../package-search/package-search.service';
/**
* If request is cachable (e.g., package search) and
* response is in cache return the cached response as observable.
* If has 'x-refresh' header that is true,
* then also re-run the package search, using response from next(),
* returning an observable that emits the cached response first.
*
* If not in cache or not cachable,
* pass request through to next()
*/
// #docregion v1
@Injectable()
export class CachingInterceptor implements HttpInterceptor {
constructor(private cache: RequestCache) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
// continue if not cachable.
if (!isCachable(req)) { return next.handle(req); }
const cachedResponse = this.cache.get(req);
// #enddocregion v1
// #docregion intercept-refresh
// cache-then-refresh
if (req.headers.get('x-refresh')) {
const results$ = sendRequest(req, next, this.cache);
return cachedResponse ?
results$.pipe( startWith(cachedResponse) ) :
results$;
}
// cache-or-fetch
// #docregion v1
return cachedResponse ?
of(cachedResponse) : sendRequest(req, next, this.cache);
// #enddocregion intercept-refresh
}
}
// #enddocregion v1
/** Is this request cachable? */
function isCachable(req: HttpRequest<any>) {
// Only GET requests are cachable
return req.method === 'GET' &&
// Only npm package search is cachable in this app
-1 < req.url.indexOf(searchUrl);
}
// #docregion send-request
/**
* Get server response observable by sending request to `next()`.
* Will add the response to the cache on the way out.
*/
function sendRequest(
req: HttpRequest<any>,
next: HttpHandler,
cache: RequestCache): Observable<HttpEvent<any>> {
// No headers allowed in npm search request
const noHeaderReq = req.clone({ headers: new HttpHeaders() });
return next.handle(noHeaderReq).pipe(
tap(event => {
// There may be other events besides the response.
if (event instanceof HttpResponse) {
cache.put(req, event); // Update the cache.
}
})
);
}
// #enddocregion send-request

View File

@ -0,0 +1,20 @@
import { Injectable } from '@angular/core';
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class EnsureHttpsInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// #docregion excerpt
// clone request and replace 'http://' with 'https://' at the same time
const secureReq = req.clone({
url: req.url.replace('http://', 'https://')
});
// send the cloned, "secure" request to the next handler.
return next.handle(secureReq);
// #enddocregion excerpt
}
}

View File

@ -0,0 +1,34 @@
// #docplaster
// #docregion interceptor-providers
/* "Barrel" of Http Interceptors */
import { HTTP_INTERCEPTORS } from '@angular/common/http';
// #enddocregion interceptor-providers
import { AuthInterceptor } from './auth-interceptor';
import { CachingInterceptor } from './caching-interceptor';
import { EnsureHttpsInterceptor } from './ensure-https-interceptor';
import { LoggingInterceptor } from './logging-interceptor';
// #docregion interceptor-providers
import { NoopInterceptor } from './noop-interceptor';
// #enddocregion interceptor-providers
import { TrimNameInterceptor } from './trim-name-interceptor';
import { UploadInterceptor } from './upload-interceptor';
// #docregion interceptor-providers
/** Http interceptor providers in outside-in order */
export const httpInterceptorProviders = [
// #docregion noop-provider
{ provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true },
// #enddocregion noop-provider, interceptor-providers
{ provide: HTTP_INTERCEPTORS, useClass: EnsureHttpsInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: TrimNameInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: UploadInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: CachingInterceptor, multi: true },
// #docregion interceptor-providers
];
// #enddocregion interceptor-providers

View File

@ -0,0 +1,39 @@
import { Injectable } from '@angular/core';
import {
HttpEvent, HttpInterceptor, HttpHandler,
HttpRequest, HttpResponse
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
// #docregion excerpt
import { finalize, tap } from 'rxjs/operators';
import { MessageService } from '../message.service';
@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
constructor(private messenger: MessageService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
const started = Date.now();
let ok: string;
// extend server response observable with logging
return next.handle(req)
.pipe(
tap(
// Succeeds when there is a response; ignore other events
event => ok = event instanceof HttpResponse ? 'succeeded' : '',
// Operation failed; error is an HttpErrorResponse
error => ok = 'failed'
),
// Log when response observable either completes or errors
finalize(() => {
const elapsed = Date.now() - started;
const msg = `${req.method} "${req.urlWithParams}"
${ok} in ${elapsed} ms.`;
this.messenger.add(msg);
})
);
}
}
// #enddocregion excerpt

View File

@ -0,0 +1,16 @@
import { Injectable } from '@angular/core';
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
/** Pass untouched request through to the next request handler. */
@Injectable()
export class NoopInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
return next.handle(req);
}
}

View File

@ -0,0 +1,24 @@
import { Injectable } from '@angular/core';
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class TrimNameInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const body = req.body;
if (!body || !body.name ) {
return next.handle(req);
}
// #docregion excerpt
// copy the body and trim whitespace from the name property
const newBody = { ...body, name: body.name.trim() };
// clone request and set its body
const newReq = req.clone({ body: newBody });
// send the cloned request to the next handler.
return next.handle(newReq);
// #enddocregion excerpt
}
}

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