Compare commits

...

101 Commits

Author SHA1 Message Date
24db1ed938 docs: add changelog for 4.3.3 2017-08-02 13:00:35 -07:00
82798e9d04 release: cut the 4.3.3 release 2017-08-02 12:57:34 -07:00
da8bb1b45b docs(aio): fixed list format in FormArray section 2017-07-31 11:40:27 -07:00
f5cbc2ee25 fix(compiler): fix for element needing implicit parent placed in top-level ng-container
fixes #18314
2017-07-31 11:40:20 -07:00
cbc1986c6f fix(aio): fix links to source for paths with symlinks
Fixes #18353
2017-07-31 11:39:47 -07:00
0982f993cb ci: short-circuit npm install for aio builds that use yarn only 2017-07-31 11:39:41 -07:00
a5a29b0591 docs(aio): delay ngUpgrade e2e test to avoid flakes 2017-07-31 11:39:36 -07:00
a8f3197f24 build: short-circuit build for AIO tasks 2017-07-31 11:39:31 -07:00
e6f37120fe docs(aio): fix deprecated protractor API usage
`browser.getLocationAbsUrl()` is deprecated.
We should use `browser.getCurrentUrl()` instead.
2017-07-31 11:39:25 -07:00
6840b7bda9 ci(aio): test the example e2e files using local build of Angular 2017-07-31 11:39:21 -07:00
68f458909a build(aio): ignore generated aot files
Assets such as images and data which are generated
by the aot build were not being ignored.
2017-07-31 11:39:14 -07:00
12acecf756 docs(aio): remove generated styles.css file
This file should have been ignored as it is created
during the build of the example
2017-07-31 11:39:10 -07:00
cfbed40ab6 build(aio): support overriding the Angular packages in examples with locally built ones 2017-07-31 11:39:03 -07:00
fe1a6b8e42 build(aio): refactor and test the example-boilerplate tool 2017-07-31 11:38:59 -07:00
13e29c4e89 ci: shard the aio example e2e tests 2017-07-31 11:38:53 -07:00
fd52b178ed ci(aio): support sharding of example e2e tests 2017-07-31 11:38:48 -07:00
ca1f071b2e build(aio): upgrade @angular/material to 2.0.0-meta.8 2017-07-31 11:38:43 -07:00
296adbbb72 build(aio): upgrade @angular/* to 4.3.1 2017-07-31 11:38:37 -07:00
c795ee1176 docs(aio): fix URLSearchParams interface link to MDN
Fixes #18367
2017-07-31 11:38:33 -07:00
b550618afd refactor(aio): move content-specific images to content/images/
Fixes #17053
2017-07-31 11:38:28 -07:00
d08d6eebff refactor(aio): rename unused directories to _unused 2017-07-31 11:38:23 -07:00
e9789abd05 fix(aio): fix link to logo in example 2017-07-31 11:38:17 -07:00
f2ec2cbb99 refactor(aio): move unused images to unused directories
This prevents the ServiceWorker from prefetching unnecessary files.
2017-07-31 11:38:09 -07:00
8de2ace80a docs: add changelog for 4.3.2 2017-07-27 15:51:46 -05:00
c977994864 release: cut the 4.3.2 release 2017-07-27 15:51:46 -05:00
12b8e1af55 Revert "fix(router): should throw when lazy loaded module doesn't define any routes (#15001)"
This reverts commit be49e0ee93.
2017-07-27 13:05:36 -07:00
9a188485f5 ci: correct bad bazel merge 2017-07-27 12:43:29 -07:00
45a10419bc Revert "build: Bazel builds ngfactories for packages/core (#18289)"
This reverts commit bcea196530.
2017-07-27 10:51:41 -07:00
2245748c14 test(compiler): fix xliff2 integration test for the 4.3 branch (#18363)
Closes #18363
2017-07-27 10:20:34 -07:00
bcea196530 build: Bazel builds ngfactories for packages/core (#18289)
PR Close #18289
2017-07-26 16:31:26 -07:00
b9e32c833a fix(router): child CanActivate guard should wait for parent to complete (#18110)
Closes #15670

PR Close #18110
2017-07-26 17:52:47 -05:00
be49e0ee93 fix(router): should throw when lazy loaded module doesn't define any routes (#15001)
Closes #14596

PR Close #15001
2017-07-26 16:49:50 -05:00
bf95655a1a docs(aio): add my details as a contributor (#18315)
PR Close #18315
2017-07-26 16:49:50 -05:00
6bf5b84fa4 fix(aio): correctly process markdown link in "Browser Support" (#18349)
The markdown processor expects an empty line between an opening tag and the
markdown content. (If there is no empty line, the content is interpreted as
plain HTML.)
Previously, the line between the opening `<td>` and the content contained
whitespace, which caused the content to be interpreted as HTML and not markdown.

Fixes #18312

PR Close #18349
2017-07-26 16:11:11 -05:00
4836565ca7 docs(aio): update examples to 4.3 2017-07-26 12:08:29 -05:00
750e4e8156 docs(aio) - Fixed link to the glossary dash-case term (#18311)
PR Close #18311
2017-07-26 12:08:29 -05:00
a0846194b7 fix(compiler): add equiv & disp attributes to Xliff2 ICU placeholders (#18283)
Fixes #17344

PR Close #18283
2017-07-26 12:08:29 -05:00
bcf6b90c95 docs(platform-server): inline PlatformOptions and add doc strings (#18264)
Fix documentation for the options passed into renderModule and
renderModuleFactory.

PR Close #18264
2017-07-26 12:08:29 -05:00
3ca2a0aa37 refactor(compiler-cli): add support for browser compiler bundle (#17979)
PR Close #17979
2017-07-26 12:08:29 -05:00
b4be96c65d ci: use chrome stable (#18307) 2017-07-26 12:08:29 -05:00
434ff5fecb build: update bazel rules to latest (#18289) 2017-07-26 12:08:24 -05:00
a1bb9c2d42 fix(core): invoke error handler outside of the Angular Zone (#18269)
In Node.JS console.log/error/warn functions actually resuls in a socket
write which in turn is considered by Zone.js as an async task.

This means that if there is any exception during change detection in a platform-server
application the error handler will make the Angular Zone unstable which
in turn will cause change detection to run on next tick and cause an
infinite loop.

It is also better to run the error handler outside of the Angular Zone
in general on all platforms so that an error in the error handler itself doesn't cause an
infinite loop.

Fixes #17073, #7774.

PR Close #18269
2017-07-26 12:04:47 -05:00
7e626bef0a ci: force precise on Travis (#18282)
PR Close #18282
2017-07-26 12:04:47 -05:00
a1e83a8ed2 test: fix bad merge (#18267)
PR Close #18267
2017-07-26 12:04:47 -05:00
cbeb197aa5 fix(animations): export BrowserModule as apart of BrowserAnimationsModule (#18263)
PR Close #18263
2017-07-26 12:04:47 -05:00
0330fa6b82 docs(aio): pngcrush all pngs (#18243)
PR Close #18243
2017-07-26 12:04:47 -05:00
97135e8fd5 fix(platform-server): don't clobber parse5 properties when setting (#18237)
element properties.

Fixes #17050.

We now store all element properties in a separate 'properties' bag.

PR Close #18237
2017-07-26 12:04:47 -05:00
35bd07fc7b refactor(common): CleanUp HttpClient's imports (#18120)
PR Close #18120
2017-07-26 12:04:47 -05:00
a8ac77b645 fix(compiler): allow numbers for ICU message cases in lexer (#18095)
Closes #18095
Fixes #17799
2017-07-26 12:04:47 -05:00
9ecd377a51 ci(aio): fix aio payload script 2017-07-26 12:04:47 -05:00
76171bd8b4 test(upgrade): fail tests when there are AngularJS errors (#18209) 2017-07-26 12:04:47 -05:00
1f106d75bc fix(upgrade): throw error if trying to get injector before setting (#18209)
Previously, `undefined` would be returned.
This change makes it easier to identify incorrect uses/bugs.
(Discussed in https://github.com/angular/angular/pull/18213#issuecomment-316191308.)
2017-07-26 12:04:47 -05:00
a4fae8c405 aio: debounce search and delay index building (#18134)
* feat(aio): debounce search requests

* feat(aio): delay loading search worker and index
2017-07-26 12:04:46 -05:00
33c07b3394 fix(aio): invalid formatting in architecure.md (#18159)
Introduced in e110a80caf (diff-9ac9c6a9277eea9856d75249a7c0a40aL127)
2017-07-26 12:00:01 -05:00
c9d06e676f docs(aio): replace old blog link (#18252)
Fixes #18233
* Docs(aio): Replaced old blog link Now with the Link to the new Angular.io Blog
* Removed double braces
2017-07-26 12:00:01 -05:00
c7c65d9fda docs: fixing invisible tag in README (#18245) 2017-07-26 12:00:01 -05:00
257a9e3e6f ci: test bazel builds on travis (#18240) 2017-07-26 12:00:01 -05:00
c7c0a1688e docs(aio): Fixed typo with closing div 2017-07-26 12:00:00 -05:00
7e95e2b0ba docs(aio): Fix http guide 2017-07-26 12:00:00 -05:00
ddc286f4b5 docs: fix wrong link in CONTRIBUTING.md (#18228)
The link to the CONTRIBUTING.md file was not correct
2017-07-26 12:00:00 -05:00
3d17a3672e docs(router): minor typo (#18226)
Fix a minor typo in the description of a router spec.
2017-07-26 12:00:00 -05:00
61d253f5fd docs(aio): fix typo in NgModule FAQs (#18211)
Fix #18133. Fix typo 'added' to 'add' in the 'Why does lazy loading create a child injector' section.
2017-07-26 12:00:00 -05:00
54be25a7a1 docs: add changelog for 4.3.1 2017-07-19 12:54:45 -07:00
b1757037fb release: cut the 4.3.1 release 2017-07-19 12:53:17 -07:00
f0476fcff0 fix(compiler-cli): don't generate empty <target/> when extracting xliff
Fixes #15754
2017-07-19 09:50:09 -07:00
a5c4bb5b96 fix(animations): make sure @.disabled works in non-animation components
Note 4.3 only!

Prior to this fix when [@.disabled] was used in a component that
contained zero animation code it wouldn't register properly because the
renderer associated with that component was not an animation renderer.
This patch ensures that it gets registered even when there are no
animations set.
2017-07-19 09:50:03 -07:00
4c1f32b0db fix(animations): do not crash animations if a nested component fires CD during CD
Closes #18193
2017-07-19 09:49:57 -07:00
383d8969ab fix(animations): always camelcase style property names that contain auto styles
Closes #17938
2017-07-19 09:49:47 -07:00
333ffd8d32 fix(animations): capture cancelled animation styles within grouped animations
Closes #17170
2017-07-19 09:49:42 -07:00
d4679a0bc2 docs(aio): fix typo in Router documentation
Fix title and link to RouteConfigLoadEnd documentation
2017-07-19 15:02:14 +01:00
4ce29f3a5b fix(platform-server): provide XhrFactory for HttpClient 2017-07-18 14:19:18 -07:00
17b7bc3e06 fix(common): send flushed body as error instead of null
fix #18181
2017-07-18 10:58:27 -07:00
f19bd5f4f3 docs(http): Make name of injected HttpTestingController consistent 2017-07-18 10:54:42 -07:00
d503d25f29 docs(platform-server): add doc string for PlatformOptions 2017-07-18 10:54:37 -07:00
5d275e994a fix(router): terminal route in custom matcher 2017-07-18 10:54:32 -07:00
d8c8b13bb8 docs: fix typo 2017-07-18 10:54:26 -07:00
4671168635 fix(compiler): ensure jit external id arguments names are unique
Fixes: #17558, #17378, #8676
2017-07-18 10:54:21 -07:00
1ac78bfd5d fix(router): canDeactivate guards should run from bottom to top
Closes #15657.
2017-07-18 10:54:15 -07:00
4340beacea fix(router): should navigate to the same url when config changes
Closes #15535
2017-07-18 10:54:09 -07:00
ec89f378fc fix(router): should run resolvers for the same route concurrently
Fixes #14279
2017-07-18 10:54:04 -07:00
4dd6863bc2 docs(aio): fix HttpClient setting new header sample 2017-07-18 10:53:58 -07:00
37c626e673 fix(aio): remove title attribute from CodeExampleComponent
This was causing browser to add an unwanted tooltip that appeared
when the user hovers over the code.

See #17524
2017-07-18 17:55:54 +01:00
f0a110928b fix(aio): add quote to module 2017-07-18 17:54:51 +01:00
c39e7d1eb2 fix(aio): do not wrap <code-tabs> tab labels
Fixes #17751
2017-07-18 17:45:20 +01:00
799bffb431 docs(aio): fix cheatsheet layout for narrow screens
* Tell the app that this will have no Table of Contents, since we have no
h2 headings anyway.
* Remove all the `nbsp;` from the code since that doesn't help with layout
* Remove side padding from sidenav-content when screen is narrow
* Restyle the cheatsheet table when the screen is narrow
2017-07-18 17:33:36 +01:00
fda607cc2f build(aio): fail doc-gen if referenced images are missing 2017-07-18 11:46:15 +01:00
cc3aa68123 docs(aio): fix broken image sources 2017-07-18 11:46:15 +01:00
306621d2d6 docs(aio): fix up broken links 2017-07-18 11:46:15 +01:00
d204f7aa2a build(aio): abort doc-gen on dangling links 2017-07-18 11:46:14 +01:00
a94f5e8cbb build(aio): abort doc-gen if an example is missing
Closes #16936
2017-07-18 11:46:14 +01:00
1390afef23 docs: fix HttpClient sample 2017-07-17 14:18:41 -07:00
b0346a6e45 docs(aio): fix HttpClient's interceptor sample 2017-07-17 14:18:41 -07:00
e5da059994 docs(http): fix "Expecting and answering requests" example mistake
Possibly overlooked testing documentation mistake fixed:
- `http.get('/data')` probably ought to be paired with `httpMock.expectOne('/data')` instead of `httpMock.expectOne('/data')`.
2017-07-17 14:18:41 -07:00
ac92c3bb26 docs: fix HttpClient logging's sample 2017-07-17 14:18:41 -07:00
87157d7089 docs(http): fixed syntax error in AuthInterceptor example 2017-07-17 14:18:41 -07:00
611dd12f0f ci: add GK and PBD to aio content and marketing groups 2017-07-17 14:18:41 -07:00
969ce9dc2b fix(aio): remove title from callout 2017-07-15 15:39:40 +01:00
34834a9e79 fix(aio): remove unused news.html file
Although outdated and not used, the file would be picked up and showed in search
results.
2017-07-15 15:29:59 +01:00
6e2ddccc2c build(aio): render type parameters of API function exports
Fixes #18123
2017-07-15 08:54:34 +01:00
55742e4737 Revert "revert: revert: ci(aio): exclude changes in aio/content folder"
This reverts commit 3d85f72652.

Still causing repeated flakes on master.
2017-07-14 14:55:56 -07:00
0091b1e8db refactor(upgrade): clean up some types 2017-07-14 14:55:46 -07:00
265 changed files with 2814 additions and 1478 deletions

View File

@ -41,7 +41,7 @@ jobs:
- restore_cache:
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
- run: bazel run @io_bazel_rules_typescript_node//:bin/npm install
- run: bazel run @build_bazel_rules_typescript_node//:bin/npm install
- run: bazel build ...
- save_cache:
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}

View File

@ -256,6 +256,8 @@ groups:
files:
include:
- "aio/*"
exclude:
- "aio/content/*"
users:
- petebacondarwin #primary
- IgorMinar
@ -276,6 +278,8 @@ groups:
- Foxandxss
- stephenfluin
- wardbell
- petebacondarwin
- gkalpak
- IgorMinar #fallback
- mhevery #fallback
@ -289,5 +293,7 @@ groups:
users:
- juleskremer #primary
- stephenfluin
- petebacondarwin
- gkalpak
- IgorMinar #fallback
- mhevery #fallback

View File

@ -1,9 +1,12 @@
language: node_js
sudo: false
# force trusty as Google Chrome addon is not supported on Precise
dist: trusty
node_js:
- '6.9.5'
addons:
chrome: stable
# firefox: "38.0"
apt:
sources:
@ -52,14 +55,15 @@ env:
- CI_MODE=browserstack_optional
- CI_MODE=docs_test
- CI_MODE=aio
- CI_MODE=aio_e2e
- CI_MODE=aio_e2e AIO_SHARD=0
- CI_MODE=aio_e2e AIO_SHARD=1
- CI_MODE=bazel
matrix:
fast_finish: true
allow_failures:
- env: "CI_MODE=saucelabs_optional"
- env: "CI_MODE=browserstack_optional"
- env: "CI_MODE=aio_e2e"
before_install:
# source the env.sh script so that the exported variables are available to other scripts later on

View File

@ -11,8 +11,15 @@ filegroup(
# This won't scale in the general case.
# TODO(alexeagle): figure out what to do
"node_modules/typescript/**",
"node_modules/zone.js/**/*.d.ts",
"node_modules/zone.js/**",
"node_modules/rxjs/**/*.d.ts",
"node_modules/rxjs/**/*.js",
"node_modules/@types/**/*.d.ts",
"node_modules/tsickle/**",
"node_modules/hammerjs/**/*.d.ts",
"node_modules/protobufjs/**",
"node_modules/bytebuffer/**",
"node_modules/reflect-metadata/**",
"node_modules/minimist/**/*.js",
]),
)

View File

@ -1,3 +1,51 @@
<a name="4.3.3"></a>
## [4.3.3](https://github.com/angular/angular/compare/4.3.2...4.3.3) (2017-08-02)
### Bug Fixes
* **compiler:** fix for element needing implicit parent placed in top-level ng-container ([f5cbc2e](https://github.com/angular/angular/commit/f5cbc2e)), closes [#18314](https://github.com/angular/angular/issues/18314)
<a name="4.3.2"></a>
## [4.3.2](https://github.com/angular/angular/compare/4.3.1...4.3.2) (2017-07-26)
### Bug Fixes
* **animations:** export BrowserModule as apart of BrowserAnimationsModule ([#18263](https://github.com/angular/angular/issues/18263)) ([cbeb197](https://github.com/angular/angular/commit/cbeb197))
* **compiler:** add equiv & disp attributes to Xliff2 ICU placeholders ([#18283](https://github.com/angular/angular/issues/18283)) ([a084619](https://github.com/angular/angular/commit/a084619)), closes [#17344](https://github.com/angular/angular/issues/17344)
* **compiler:** allow numbers for ICU message cases in lexer ([#18095](https://github.com/angular/angular/issues/18095)) ([a8ac77b](https://github.com/angular/angular/commit/a8ac77b)), closes [#17799](https://github.com/angular/angular/issues/17799)
* **core:** invoke error handler outside of the Angular Zone ([#18269](https://github.com/angular/angular/issues/18269)) ([a1bb9c2](https://github.com/angular/angular/commit/a1bb9c2)), closes [#17073](https://github.com/angular/angular/issues/17073) [#7774](https://github.com/angular/angular/issues/7774)
* **platform-server:** don't clobber parse5 properties when setting ([#18237](https://github.com/angular/angular/issues/18237)) ([97135e8](https://github.com/angular/angular/commit/97135e8)), closes [#17050](https://github.com/angular/angular/issues/17050)
* **router:** child CanActivate guard should wait for parent to complete ([#18110](https://github.com/angular/angular/issues/18110)) ([b9e32c8](https://github.com/angular/angular/commit/b9e32c8)), closes [#15670](https://github.com/angular/angular/issues/15670)
* **router:** should throw when lazy loaded module doesn't define any routes ([#15001](https://github.com/angular/angular/issues/15001)) ([be49e0e](https://github.com/angular/angular/commit/be49e0e)), closes [#14596](https://github.com/angular/angular/issues/14596)
* **upgrade:** throw error if trying to get injector before setting ([#18209](https://github.com/angular/angular/issues/18209)) ([1f106d7](https://github.com/angular/angular/commit/1f106d7))
<a name="4.3.1"></a>
## [4.3.1](https://github.com/angular/angular/compare/4.3.0...4.3.1) (2017-07-19)
### Bug Fixes
* **animations:** always camelcase style property names that contain auto styles ([383d896](https://github.com/angular/angular/commit/383d896)), closes [#17938](https://github.com/angular/angular/issues/17938)
* **animations:** capture cancelled animation styles within grouped animations ([333ffd8](https://github.com/angular/angular/commit/333ffd8)), closes [#17170](https://github.com/angular/angular/issues/17170)
* **animations:** do not crash animations if a nested component fires CD during CD ([4c1f32b](https://github.com/angular/angular/commit/4c1f32b)), closes [#18193](https://github.com/angular/angular/issues/18193)
* **animations:** make sure @.disabled works in non-animation components ([a5c4bb5](https://github.com/angular/angular/commit/a5c4bb5))
* **common:** send flushed body as error instead of null ([17b7bc3](https://github.com/angular/angular/commit/17b7bc3)), closes [#18181](https://github.com/angular/angular/issues/18181)
* **compiler:** ensure jit external id arguments names are unique ([4671168](https://github.com/angular/angular/commit/4671168))
* **compiler-cli:** don't generate empty `<target/>` when extracting xliff ([f0476fc](https://github.com/angular/angular/commit/f0476fc)), closes [#15754](https://github.com/angular/angular/issues/15754)
* **platform-server:** provide XhrFactory for HttpClient ([4ce29f3](https://github.com/angular/angular/commit/4ce29f3))
* **router:** canDeactivate guards should run from bottom to top ([1ac78bf](https://github.com/angular/angular/commit/1ac78bf)), closes [#15657](https://github.com/angular/angular/issues/15657)
* **router:** should navigate to the same url when config changes ([4340bea](https://github.com/angular/angular/commit/4340bea)), closes [#15535](https://github.com/angular/angular/issues/15535)
* **router:** should run resolvers for the same route concurrently ([ec89f37](https://github.com/angular/angular/commit/ec89f37)), closes [#14279](https://github.com/angular/angular/issues/14279)
* **router:** terminal route in custom matcher ([5d275e9](https://github.com/angular/angular/commit/5d275e9))
<a name="4.3.0"></a>
# [4.3.0](https://github.com/angular/angular/compare/4.3.0-rc.0...4.3.0) (2017-07-14)

View File

@ -1,11 +1,17 @@
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "io_bazel_rules_typescript",
name = "build_bazel_rules_typescript",
remote = "https://github.com/bazelbuild/rules_typescript.git",
commit = "3a8404d",
tag = "0.0.5",
)
load("@io_bazel_rules_typescript//:defs.bzl", "node_repositories")
load("@build_bazel_rules_typescript//:defs.bzl", "node_repositories")
node_repositories(package_json = "//:package.json")
git_repository(
name = "build_bazel_rules_angular",
remote = "https://github.com/bazelbuild/rules_angular.git",
tag = "0.0.1",
)

View File

@ -43,13 +43,9 @@ dist/
**/app/**/*.ajs.js
# aot
**/*.ngfactory.ts
**/*.ngsummary.json
**/*.ngsummary.ts
**/*.shim.ngstyle.ts
**/*.metadata.json
!aot/bs-config.json
!aot/index.html
*/aot/**/*
!*/aot/bs-config.json
!*/aot/index.html
!rollup-config.js
# i18n

View File

@ -9,7 +9,7 @@ export class AppComponent {
wolves = 0;
gender = 'f';
fly = true;
logo = 'https://angular.io/resources/images/logos/angular/angular.png';
logo = 'https://angular.io/assets/images/logos/angular/angular.png';
count = 3;
heroes: string[] = ['Magneta', 'Celeritas', 'Dynama'];
inc(i: number) {

View File

@ -1,116 +0,0 @@
/* #docregion , quickstart, toh */
/* Master Styles */
h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
h2, h3 {
color: #444;
font-family: Arial, Helvetica, sans-serif;
font-weight: lighter;
}
body {
margin: 2em;
}
/* #enddocregion quickstart */
body, input[text], button {
color: #888;
font-family: Cambria, Georgia;
}
/* #enddocregion toh */
a {
cursor: pointer;
cursor: hand;
}
button {
font-family: Arial;
background-color: #eee;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
background-color: #cfd8dc;
}
button:disabled {
background-color: #eee;
color: #aaa;
cursor: auto;
}
/* Navigation link styles */
nav a {
padding: 5px 10px;
text-decoration: none;
margin-right: 10px;
margin-top: 10px;
display: inline-block;
background-color: #eee;
border-radius: 4px;
}
nav a:visited, a:link {
color: #607D8B;
}
nav a:hover {
color: #039be5;
background-color: #CFD8DC;
}
nav a.active {
color: #039be5;
}
/* items class */
.items {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 24em;
}
.items li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.items li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.items li.selected {
background-color: #CFD8DC;
color: white;
}
.items li.selected:hover {
background-color: #BBD8DC;
}
.items .text {
position: relative;
top: -3px;
}
.items .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;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
/* #docregion toh */
/* everywhere else */
* {
font-family: Arial, Helvetica, sans-serif;
}

View File

@ -9,7 +9,8 @@ describe('PhoneCat Application', function() {
it('should redirect `index.html` to `index.html#!/phones', function() {
browser.get('index.html');
expect(browser.getLocationAbsUrl()).toBe('/phones');
browser.sleep(1000); // Not sure why this is needed but it is. The route change works fine.
expect(browser.getCurrentUrl()).toMatch(/\/phones$/);
});
describe('View: Phone list', function() {
@ -65,7 +66,7 @@ describe('PhoneCat Application', function() {
element.all(by.css('.phones li a')).first().click();
browser.sleep(1000); // Not sure why this is needed but it is. The route change works fine.
expect(browser.getLocationAbsUrl()).toBe('/phones/nexus-s');
expect(browser.getCurrentUrl()).toMatch(/\/phones\/nexus-s$/);
});
});

View File

@ -124,7 +124,7 @@ For example, import Angular's `Component` decorator from the `@angular/core` lib
<code-example path="architecture/src/app/app.component.ts" region="import" linenums="false"></code-example>
You also import NgModules_ from Angular _libraries_ using JavaScript import statements:
You also import NgModules from Angular _libraries_ using JavaScript import statements:
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false"></code-example>

View File

@ -2,7 +2,7 @@
An NgModule class describes how the application parts fit together.
Every application has at least one NgModule, the _root_ module
that you [bootstrap](guide/appmodule#main) to launch the application.
that you [bootstrap](#main) to launch the application.
You can call it anything you want. The conventional name is `AppModule`.
The [setup](guide/setup) instructions produce a new project with the following minimal `AppModule`.
@ -110,7 +110,7 @@ Do not put any other kind of class in `declarations`; _not_ `NgModule` classes,
### The _bootstrap_ array
You launch the application by [_bootstrapping_](guide/bootstrapping#main) the root `AppModule`.
You launch the application by [_bootstrapping_](#main) the root `AppModule`.
Among other things, the _bootstrapping_ process creates the component(s) listed in the `bootstrap` array
and inserts each one into the browser DOM.
@ -127,13 +127,6 @@ Which brings us to the _bootstrapping_ process itself.
{@a main}
<l-main-section>
</l-main-section>
## Bootstrap in _main.ts_
There are many ways to bootstrap an application.

View File

@ -390,8 +390,9 @@ Here are the features which may require additional polyfills:
<td>
[Typed&nbsp;Array](guide/browser-support#typedarray) <br>[Blob](guide/browser-support#blob)<br>[FormData](guide/browser-support#formdata)
[Typed&nbsp;Array](guide/browser-support#typedarray)<br>
[Blob](guide/browser-support#blob)<br>
[FormData](guide/browser-support#formdata)
</td>
<td>

View File

@ -79,7 +79,7 @@ including sections named outlets, wildcard routes, and preload strategies.
## HTTP: how to set default request headers (and other request options) (2016-12-14)
Added section on how to set default request headers (and other request options) to
[HTTP](guide/http#override-default-request-options) guide.
HTTP guide.
## Testing: added component test plunkers (2016-12-02)

View File

@ -1,4 +1,4 @@
# Cheat Sheet
<h1 class="no-toc">Cheat Sheet</h1>
<div id="cheatsheet">
<table class="is-full-width is-fixed-layout">
@ -23,28 +23,28 @@
</th>
</tr>
<tr>
<td><code>@<b>NgModule</b>({&nbsp;declarations:&nbsp;...,&nbsp;imports:&nbsp;...,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exports:&nbsp;...,&nbsp;providers:&nbsp;...,&nbsp;bootstrap:&nbsp;...})<br>class&nbsp;MyModule&nbsp;{}</code></td>
<td><code>@<b>NgModule</b>({ declarations: ..., imports: ...,<br> exports: ..., providers: ..., bootstrap: ...})<br>class MyModule {}</code></td>
<td><p>Defines a module that contains components, directives, pipes, and providers.</p>
</td>
</tr><tr>
<td><code><b>declarations:</b>&nbsp;[MyRedComponent,&nbsp;MyBlueComponent,&nbsp;MyDatePipe]</code></td>
<td><code><b>declarations:</b> [MyRedComponent, MyBlueComponent, MyDatePipe]</code></td>
<td><p>List of components, directives, and pipes that belong to this module.</p>
</td>
</tr><tr>
<td><code><b>imports:</b>&nbsp;[BrowserModule,&nbsp;SomeOtherModule]</code></td>
<td><code><b>imports:</b> [BrowserModule, SomeOtherModule]</code></td>
<td><p>List of modules to import into this module. Everything from the imported modules
is available to <code>declarations</code> of this module.</p>
</td>
</tr><tr>
<td><code><b>exports:</b>&nbsp;[MyRedComponent,&nbsp;MyDatePipe]</code></td>
<td><code><b>exports:</b> [MyRedComponent, MyDatePipe]</code></td>
<td><p>List of components, directives, and pipes visible to modules that import this module.</p>
</td>
</tr><tr>
<td><code><b>providers:</b>&nbsp;[MyService,&nbsp;{&nbsp;provide:&nbsp;...&nbsp;}]</code></td>
<td><code><b>providers:</b> [MyService, { provide: ... }]</code></td>
<td><p>List of dependency injection providers visible both to the contents of this module and to importers of this module.</p>
</td>
</tr><tr>
<td><code><b>bootstrap:</b>&nbsp;[MyAppComponent]</code></td>
<td><code><b>bootstrap:</b> [MyAppComponent]</code></td>
<td><p>List of components to bootstrap when this module is bootstrapped.</p>
</td>
</tr>
@ -56,61 +56,61 @@ is available to <code>declarations</code> of this module.</p>
<th></th>
</tr>
<tr>
<td><code>&lt;input&nbsp;<b>[value]</b>="firstName"&gt;</code></td>
<td><code>&lt;input <b>[value]</b>="firstName"&gt;</code></td>
<td><p>Binds property <code>value</code> to the result of expression <code>firstName</code>.</p>
</td>
</tr><tr>
<td><code>&lt;div&nbsp;<b>[attr.role]</b>="myAriaRole"&gt;</code></td>
<td><code>&lt;div <b>[attr.role]</b>="myAriaRole"&gt;</code></td>
<td><p>Binds attribute <code>role</code> to the result of expression <code>myAriaRole</code>.</p>
</td>
</tr><tr>
<td><code>&lt;div&nbsp;<b>[class.extra-sparkle]</b>="isDelightful"&gt;</code></td>
<td><code>&lt;div <b>[class.extra-sparkle]</b>="isDelightful"&gt;</code></td>
<td><p>Binds the presence of the CSS class <code>extra-sparkle</code> on the element to the truthiness of the expression <code>isDelightful</code>.</p>
</td>
</tr><tr>
<td><code>&lt;div&nbsp;<b>[style.width.px]</b>="mySize"&gt;</code></td>
<td><code>&lt;div <b>[style.width.px]</b>="mySize"&gt;</code></td>
<td><p>Binds style property <code>width</code> to the result of expression <code>mySize</code> in pixels. Units are optional.</p>
</td>
</tr><tr>
<td><code>&lt;button&nbsp;<b>(click)</b>="readRainbow($event)"&gt;</code></td>
<td><code>&lt;button <b>(click)</b>="readRainbow($event)"&gt;</code></td>
<td><p>Calls method <code>readRainbow</code> when a click event is triggered on this button element (or its children) and passes in the event object.</p>
</td>
</tr><tr>
<td><code>&lt;div&nbsp;title="Hello&nbsp;<b>{{ponyName}}</b>"&gt;</code></td>
<td><code>&lt;div title="Hello <b>{{ponyName}}</b>"&gt;</code></td>
<td><p>Binds a property to an interpolated string, for example, "Hello Seabiscuit". Equivalent to:
<code>&lt;div [title]="'Hello ' + ponyName"&gt;</code></p>
</td>
</tr><tr>
<td><code>&lt;p&gt;Hello&nbsp;<b>{{ponyName}}</b>&lt;/p&gt;</code></td>
<td><code>&lt;p&gt;Hello <b>{{ponyName}}</b>&lt;/p&gt;</code></td>
<td><p>Binds text content to an interpolated string, for example, "Hello Seabiscuit".</p>
</td>
</tr><tr>
<td><code>&lt;my-cmp&nbsp;<b>[(title)]</b>="name"&gt;</code></td>
<td><code>&lt;my-cmp <b>[(title)]</b>="name"&gt;</code></td>
<td><p>Sets up two-way data binding. Equivalent to: <code>&lt;my-cmp [title]="name" (titleChange)="name=$event"&gt;</code></p>
</td>
</tr><tr>
<td><code>&lt;video&nbsp;<b>#movieplayer</b>&nbsp;...&gt;<br>&nbsp;&nbsp;&lt;button&nbsp;<b>(click)</b>="movieplayer.play()"&gt;<br>&lt;/video&gt;</code></td>
<td><code>&lt;video <b>#movieplayer</b> ...&gt;<br> &lt;button <b>(click)</b>="movieplayer.play()"&gt;<br>&lt;/video&gt;</code></td>
<td><p>Creates a local variable <code>movieplayer</code> that provides access to the <code>video</code> element instance in data-binding and event-binding expressions in the current template.</p>
</td>
</tr><tr>
<td><code>&lt;p&nbsp;<b>*myUnless</b>="myExpression"&gt;...&lt;/p&gt;</code></td>
<td><code>&lt;p <b>*myUnless</b>="myExpression"&gt;...&lt;/p&gt;</code></td>
<td><p>The <code>*</code> symbol turns the current element into an embedded template. Equivalent to:
<code>&lt;ng-template [myUnless]="myExpression"&gt;&lt;p&gt;...&lt;/p&gt;&lt;/ng-template&gt;</code></p>
</td>
</tr><tr>
<td><code>&lt;p&gt;Card&nbsp;No.:&nbsp;<b>{{cardNumber&nbsp;|&nbsp;myCardNumberFormatter}}</b>&lt;/p&gt;</code></td>
<td><code>&lt;p&gt;Card No.: <b>{{cardNumber | myCardNumberFormatter}}</b>&lt;/p&gt;</code></td>
<td><p>Transforms the current value of expression <code>cardNumber</code> via the pipe called <code>myCardNumberFormatter</code>.</p>
</td>
</tr><tr>
<td><code>&lt;p&gt;Employer:&nbsp;<b>{{employer?.companyName}}</b>&lt;/p&gt;</code></td>
<td><code>&lt;p&gt;Employer: <b>{{employer?.companyName}}</b>&lt;/p&gt;</code></td>
<td><p>The safe navigation operator (<code>?</code>) means that the <code>employer</code> field is optional and if <code>undefined</code>, the rest of the expression should be ignored.</p>
</td>
</tr><tr>
<td><code>&lt;<b>svg:</b>rect&nbsp;x="0"&nbsp;y="0"&nbsp;width="100"&nbsp;height="100"/&gt;</code></td>
<td><code>&lt;<b>svg:</b>rect x="0" y="0" width="100" height="100"/&gt;</code></td>
<td><p>An SVG snippet template needs an <code>svg:</code> prefix on its root element to disambiguate the SVG element from an HTML component.</p>
</td>
</tr><tr>
<td><code>&lt;<b>svg</b>&gt;<br>&nbsp;&nbsp;&lt;rect&nbsp;x="0"&nbsp;y="0"&nbsp;width="100"&nbsp;height="100"/&gt;<br>&lt;/<b>svg</b>&gt;</code></td>
<td><code>&lt;<b>svg</b>&gt;<br> &lt;rect x="0" y="0" width="100" height="100"/&gt;<br>&lt;/<b>svg</b>&gt;</code></td>
<td><p>An <code>&lt;svg&gt;</code> root element is detected as an SVG element automatically, without the prefix.</p>
</td>
</tr>
@ -124,19 +124,19 @@ is available to <code>declarations</code> of this module.</p>
</th>
</tr>
<tr>
<td><code>&lt;section&nbsp;<b>*ngIf</b>="showSection"&gt;</code></td>
<td><code>&lt;section <b>*ngIf</b>="showSection"&gt;</code></td>
<td><p>Removes or recreates a portion of the DOM tree based on the <code>showSection</code> expression.</p>
</td>
</tr><tr>
<td><code>&lt;li&nbsp;<b>*ngFor</b>="let&nbsp;item&nbsp;of&nbsp;list"&gt;</code></td>
<td><code>&lt;li <b>*ngFor</b>="let item of list"&gt;</code></td>
<td><p>Turns the li element and its contents into a template, and uses that to instantiate a view for each item in list.</p>
</td>
</tr><tr>
<td><code>&lt;div&nbsp;<b>[ngSwitch]</b>="conditionExpression"&gt;<br>&nbsp;&nbsp;&lt;ng-template&nbsp;<b>[<b>ngSwitchCase</b>]</b>="case1Exp"&gt;...&lt;/ng-template&gt;<br>&nbsp;&nbsp;&lt;ng-template&nbsp;<b>ngSwitchCase</b>="case2LiteralString"&gt;...&lt;/ng-template&gt;<br>&nbsp;&nbsp;&lt;ng-template&nbsp;<b>ngSwitchDefault</b>&gt;...&lt;/ng-template&gt;<br>&lt;/div&gt;</code></td>
<td><code>&lt;div <b>[ngSwitch]</b>="conditionExpression"&gt;<br> &lt;ng-template <b>[<b>ngSwitchCase</b>]</b>="case1Exp"&gt;...&lt;/ng-template&gt;<br> &lt;ng-template <b>ngSwitchCase</b>="case2LiteralString"&gt;...&lt;/ng-template&gt;<br> &lt;ng-template <b>ngSwitchDefault</b>&gt;...&lt;/ng-template&gt;<br>&lt;/div&gt;</code></td>
<td><p>Conditionally swaps the contents of the div by selecting one of the embedded templates based on the current value of <code>conditionExpression</code>.</p>
</td>
</tr><tr>
<td><code>&lt;div&nbsp;<b>[ngClass]</b>="{'active':&nbsp;isActive,&nbsp;'disabled':&nbsp;isDisabled}"&gt;</code></td>
<td><code>&lt;div <b>[ngClass]</b>="{'active': isActive, 'disabled': isDisabled}"&gt;</code></td>
<td><p>Binds the presence of CSS classes on the element to the truthiness of the associated map values. The right-hand expression should return {class-name: true/false} map.</p>
</td>
</tr>
@ -150,7 +150,7 @@ is available to <code>declarations</code> of this module.</p>
</th>
</tr>
<tr>
<td><code>&lt;input&nbsp;<b>[(ngModel)]</b>="userName"&gt;</code></td>
<td><code>&lt;input <b>[(ngModel)]</b>="userName"&gt;</code></td>
<td><p>Provides two-way data-binding, parsing, and validation for form controls.</p>
</td>
</tr>
@ -164,19 +164,19 @@ is available to <code>declarations</code> of this module.</p>
</th>
</tr>
<tr>
<td><code><b>@Component({...})</b><br>class&nbsp;MyComponent()&nbsp;{}</code></td>
<td><code><b>@Component({...})</b><br>class MyComponent() {}</code></td>
<td><p>Declares that a class is a component and provides metadata about the component.</p>
</td>
</tr><tr>
<td><code><b>@Directive({...})</b><br>class&nbsp;MyDirective()&nbsp;{}</code></td>
<td><code><b>@Directive({...})</b><br>class MyDirective() {}</code></td>
<td><p>Declares that a class is a directive and provides metadata about the directive.</p>
</td>
</tr><tr>
<td><code><b>@Pipe({...})</b><br>class&nbsp;MyPipe()&nbsp;{}</code></td>
<td><code><b>@Pipe({...})</b><br>class MyPipe() {}</code></td>
<td><p>Declares that a class is a pipe and provides metadata about the pipe.</p>
</td>
</tr><tr>
<td><code><b>@Injectable()</b><br>class&nbsp;MyService()&nbsp;{}</code></td>
<td><code><b>@Injectable()</b><br>class MyService() {}</code></td>
<td><p>Declares that a class has dependencies that should be injected into the constructor when the dependency injector is creating an instance of this class.
</p>
</td>
@ -191,13 +191,13 @@ is available to <code>declarations</code> of this module.</p>
</th>
</tr>
<tr>
<td><code><b>selector:</b>&nbsp;'.cool-button:not(a)'</code></td>
<td><code><b>selector:</b> '.cool-button:not(a)'</code></td>
<td><p>Specifies a CSS selector that identifies this directive within a template. Supported selectors include <code>element</code>,
<code>[attribute]</code>, <code>.class</code>, and <code>:not()</code>.</p>
<p>Does not support parent-child relationship selectors.</p>
</td>
</tr><tr>
<td><code><b>providers:</b>&nbsp;[MyService,&nbsp;{&nbsp;provide:&nbsp;...&nbsp;}]</code></td>
<td><code><b>providers:</b> [MyService, { provide: ... }]</code></td>
<td><p>List of dependency injection providers for this directive and its children.</p>
</td>
</tr>
@ -212,19 +212,19 @@ so the <code>@Directive</code> configuration applies to components as well</p>
</th>
</tr>
<tr>
<td><code><b>moduleId:</b>&nbsp;module.id</code></td>
<td><code><b>moduleId:</b> module.id</code></td>
<td><p>If set, the <code>templateUrl</code> and <code>styleUrl</code> are resolved relative to the component.</p>
</td>
</tr><tr>
<td><code><b>viewProviders:</b>&nbsp;[MyService,&nbsp;{&nbsp;provide:&nbsp;...&nbsp;}]</code></td>
<td><code><b>viewProviders:</b> [MyService, { provide: ... }]</code></td>
<td><p>List of dependency injection providers scoped to this component's view.</p>
</td>
</tr><tr>
<td><code><b>template:</b>&nbsp;'Hello&nbsp;{{name}}'<br><b>templateUrl:</b>&nbsp;'my-component.html'</code></td>
<td><code><b>template:</b> 'Hello {{name}}'<br><b>templateUrl:</b> 'my-component.html'</code></td>
<td><p>Inline template or external template URL of the component's view.</p>
</td>
</tr><tr>
<td><code><b>styles:</b>&nbsp;['.primary&nbsp;{color:&nbsp;red}']<br><b>styleUrls:</b>&nbsp;['my-component.css']</code></td>
<td><code><b>styles:</b> ['.primary {color: red}']<br><b>styleUrls:</b> ['my-component.css']</code></td>
<td><p>List of inline CSS styles or external stylesheet URLs for styling the components view.</p>
</td>
</tr>
@ -238,36 +238,36 @@ so the <code>@Directive</code> configuration applies to components as well</p>
</th>
</tr>
<tr>
<td><code><b>@Input()</b>&nbsp;myProperty;</code></td>
<td><code><b>@Input()</b> myProperty;</code></td>
<td><p>Declares an input property that you can update via property binding (example:
<code>&lt;my-cmp [myProperty]="someExpression"&gt;</code>).</p>
</td>
</tr><tr>
<td><code><b>@Output()</b>&nbsp;myEvent&nbsp;=&nbsp;new&nbsp;EventEmitter();</code></td>
<td><code><b>@Output()</b> myEvent = new EventEmitter();</code></td>
<td><p>Declares an output property that fires events that you can subscribe to with an event binding (example: <code>&lt;my-cmp (myEvent)="doSomething()"&gt;</code>).</p>
</td>
</tr><tr>
<td><code><b>@HostBinding('class.valid')</b>&nbsp;isValid;</code></td>
<td><code><b>@HostBinding('class.valid')</b> isValid;</code></td>
<td><p>Binds a host element property (here, the CSS class <code>valid</code>) to a directive/component property (<code>isValid</code>).</p>
</td>
</tr><tr>
<td><code><b>@HostListener('click',&nbsp;['$event'])</b>&nbsp;onClick(e)&nbsp;{...}</code></td>
<td><code><b>@HostListener('click', ['$event'])</b> onClick(e) {...}</code></td>
<td><p>Subscribes to a host element event (<code>click</code>) with a directive/component method (<code>onClick</code>), optionally passing an argument (<code>$event</code>).</p>
</td>
</tr><tr>
<td><code><b>@ContentChild(myPredicate)</b>&nbsp;myChildComponent;</code></td>
<td><code><b>@ContentChild(myPredicate)</b> myChildComponent;</code></td>
<td><p>Binds the first result of the component content query (<code>myPredicate</code>) to a property (<code>myChildComponent</code>) of the class.</p>
</td>
</tr><tr>
<td><code><b>@ContentChildren(myPredicate)</b>&nbsp;myChildComponents;</code></td>
<td><code><b>@ContentChildren(myPredicate)</b> myChildComponents;</code></td>
<td><p>Binds the results of the component content query (<code>myPredicate</code>) to a property (<code>myChildComponents</code>) of the class.</p>
</td>
</tr><tr>
<td><code><b>@ViewChild(myPredicate)</b>&nbsp;myChildComponent;</code></td>
<td><code><b>@ViewChild(myPredicate)</b> myChildComponent;</code></td>
<td><p>Binds the first result of the component view query (<code>myPredicate</code>) to a property (<code>myChildComponent</code>) of the class. Not available for directives.</p>
</td>
</tr><tr>
<td><code><b>@ViewChildren(myPredicate)</b>&nbsp;myChildComponents;</code></td>
<td><code><b>@ViewChildren(myPredicate)</b> myChildComponents;</code></td>
<td><p>Binds the results of the component view query (<code>myPredicate</code>) to a property (<code>myChildComponents</code>) of the class. Not available for directives.</p>
</td>
</tr>
@ -281,39 +281,39 @@ so the <code>@Directive</code> configuration applies to components as well</p>
</th>
</tr>
<tr>
<td><code><b>constructor(myService:&nbsp;MyService,&nbsp;...)</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><code><b>constructor(myService: MyService, ...)</b> { ... }</code></td>
<td><p>Called before any other lifecycle hook. Use it to inject dependencies, but avoid any serious work here.</p>
</td>
</tr><tr>
<td><code><b>ngOnChanges(changeRecord)</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><code><b>ngOnChanges(changeRecord)</b> { ... }</code></td>
<td><p>Called after every change to input properties and before processing content or child views.</p>
</td>
</tr><tr>
<td><code><b>ngOnInit()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><code><b>ngOnInit()</b> { ... }</code></td>
<td><p>Called after the constructor, initializing input properties, and the first call to <code>ngOnChanges</code>.</p>
</td>
</tr><tr>
<td><code><b>ngDoCheck()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><code><b>ngDoCheck()</b> { ... }</code></td>
<td><p>Called every time that the input properties of a component or a directive are checked. Use it to extend change detection by performing a custom check.</p>
</td>
</tr><tr>
<td><code><b>ngAfterContentInit()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><code><b>ngAfterContentInit()</b> { ... }</code></td>
<td><p>Called after <code>ngOnInit</code> when the component's or directive's content has been initialized.</p>
</td>
</tr><tr>
<td><code><b>ngAfterContentChecked()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><code><b>ngAfterContentChecked()</b> { ... }</code></td>
<td><p>Called after every check of the component's or directive's content.</p>
</td>
</tr><tr>
<td><code><b>ngAfterViewInit()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><code><b>ngAfterViewInit()</b> { ... }</code></td>
<td><p>Called after <code>ngAfterContentInit</code> when the component's view has been initialized. Applies to components only.</p>
</td>
</tr><tr>
<td><code><b>ngAfterViewChecked()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><code><b>ngAfterViewChecked()</b> { ... }</code></td>
<td><p>Called after every check of the component's view. Applies to components only.</p>
</td>
</tr><tr>
<td><code><b>ngOnDestroy()</b>&nbsp;{&nbsp;...&nbsp;}</code></td>
<td><code><b>ngOnDestroy()</b> { ... }</code></td>
<td><p>Called once, before the instance is destroyed.</p>
</td>
</tr>
@ -325,15 +325,15 @@ so the <code>@Directive</code> configuration applies to components as well</p>
<th></th>
</tr>
<tr>
<td><code>{&nbsp;<b>provide</b>:&nbsp;MyService,&nbsp;<b>useClass</b>:&nbsp;MyMockService&nbsp;}</code></td>
<td><code>{ <b>provide</b>: MyService, <b>useClass</b>: MyMockService }</code></td>
<td><p>Sets or overrides the provider for <code>MyService</code> to the <code>MyMockService</code> class.</p>
</td>
</tr><tr>
<td><code>{&nbsp;<b>provide</b>:&nbsp;MyService,&nbsp;<b>useFactory</b>:&nbsp;myFactory&nbsp;}</code></td>
<td><code>{ <b>provide</b>: MyService, <b>useFactory</b>: myFactory }</code></td>
<td><p>Sets or overrides the provider for <code>MyService</code> to the <code>myFactory</code> factory function.</p>
</td>
</tr><tr>
<td><code>{&nbsp;<b>provide</b>:&nbsp;MyValue,&nbsp;<b>useValue</b>:&nbsp;41&nbsp;}</code></td>
<td><code>{ <b>provide</b>: MyValue, <b>useValue</b>: 41 }</code></td>
<td><p>Sets or overrides the provider for <code>MyValue</code> to the value <code>41</code>.</p>
</td>
</tr>
@ -347,39 +347,39 @@ so the <code>@Directive</code> configuration applies to components as well</p>
</th>
</tr>
<tr>
<td><code>const&nbsp;routes:&nbsp;<b>Routes</b>&nbsp;=&nbsp;[<br>&nbsp;&nbsp;{&nbsp;path:&nbsp;'',&nbsp;component:&nbsp;HomeComponent&nbsp;},<br>&nbsp;&nbsp;{&nbsp;path:&nbsp;'path/:routeParam',&nbsp;component:&nbsp;MyComponent&nbsp;},<br>&nbsp;&nbsp;{&nbsp;path:&nbsp;'staticPath',&nbsp;component:&nbsp;...&nbsp;},<br>&nbsp;&nbsp;{&nbsp;path:&nbsp;'**',&nbsp;component:&nbsp;...&nbsp;},<br>&nbsp;&nbsp;{&nbsp;path:&nbsp;'oldPath',&nbsp;redirectTo:&nbsp;'/staticPath'&nbsp;},<br>&nbsp;&nbsp;{&nbsp;path:&nbsp;...,&nbsp;component:&nbsp;...,&nbsp;data:&nbsp;{&nbsp;message:&nbsp;'Custom'&nbsp;}&nbsp;}<br>]);<br><br>const&nbsp;routing&nbsp;=&nbsp;RouterModule.forRoot(routes);</code></td>
<td><code>const routes: <b>Routes</b> = [<br> { path: '', component: HomeComponent },<br> { path: 'path/:routeParam', component: MyComponent },<br> { path: 'staticPath', component: ... },<br> { path: '**', component: ... },<br> { path: 'oldPath', redirectTo: '/staticPath' },<br> { path: ..., component: ..., data: { message: 'Custom' } }<br>]);<br><br>const routing = RouterModule.forRoot(routes);</code></td>
<td><p>Configures routes for the application. Supports static, parameterized, redirect, and wildcard routes. Also supports custom route data and resolve.</p>
</td>
</tr><tr>
<td><code><br>&lt;<b>router-outlet</b>&gt;&lt;/<b>router-outlet</b>&gt;<br>&lt;<b>router-outlet</b>&nbsp;name="aux"&gt;&lt;/<b>router-outlet</b>&gt;<br></code></td>
<td><code><br>&lt;<b>router-outlet</b>&gt;&lt;/<b>router-outlet</b>&gt;<br>&lt;<b>router-outlet</b> name="aux"&gt;&lt;/<b>router-outlet</b>&gt;<br></code></td>
<td><p>Marks the location to load the component of the active route.</p>
</td>
</tr><tr>
<td><code><br>&lt;a&nbsp;routerLink="/path"&gt;<br>&lt;a&nbsp;<b>[routerLink]</b>="[&nbsp;'/path',&nbsp;routeParam&nbsp;]"&gt;<br>&lt;a&nbsp;<b>[routerLink]</b>="[&nbsp;'/path',&nbsp;{&nbsp;matrixParam:&nbsp;'value'&nbsp;}&nbsp;]"&gt;<br>&lt;a&nbsp;<b>[routerLink]</b>="[&nbsp;'/path'&nbsp;]"&nbsp;[queryParams]="{&nbsp;page:&nbsp;1&nbsp;}"&gt;<br>&lt;a&nbsp;<b>[routerLink]</b>="[&nbsp;'/path'&nbsp;]"&nbsp;fragment="anchor"&gt;<br></code></td>
<td><code><br>&lt;a routerLink="/path"&gt;<br>&lt;a <b>[routerLink]</b>="[ '/path', routeParam ]"&gt;<br>&lt;a <b>[routerLink]</b>="[ '/path', { matrixParam: 'value' } ]"&gt;<br>&lt;a <b>[routerLink]</b>="[ '/path' ]" [queryParams]="{ page: 1 }"&gt;<br>&lt;a <b>[routerLink]</b>="[ '/path' ]" fragment="anchor"&gt;<br></code></td>
<td><p>Creates a link to a different view based on a route instruction consisting of a route path, required and optional parameters, query parameters, and a fragment. To navigate to a root route, use the <code>/</code> prefix; for a child route, use the <code>./</code>prefix; for a sibling or parent, use the <code>../</code> prefix.</p>
</td>
</tr><tr>
<td><code>&lt;a&nbsp;[routerLink]="[&nbsp;'/path'&nbsp;]"&nbsp;routerLinkActive="active"&gt;</code></td>
<td><code>&lt;a [routerLink]="[ '/path' ]" routerLinkActive="active"&gt;</code></td>
<td><p>The provided classes are added to the element when the <code>routerLink</code> becomes the current active route.</p>
</td>
</tr><tr>
<td><code>class&nbsp;<b>CanActivate</b>Guard&nbsp;implements&nbsp;<b>CanActivate</b>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;canActivate(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;route:&nbsp;ActivatedRouteSnapshot,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state:&nbsp;RouterStateSnapshot<br>&nbsp;&nbsp;&nbsp;&nbsp;):&nbsp;Observable&lt;boolean&gt;|Promise&lt;boolean&gt;|boolean&nbsp;{&nbsp;...&nbsp;}<br>}<br><br>{&nbsp;path:&nbsp;...,&nbsp;canActivate:&nbsp;[<b>CanActivate</b>Guard]&nbsp;}</code></td>
<td><code>class <b>CanActivate</b>Guard implements <b>CanActivate</b> {<br> canActivate(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable&lt;boolean&gt;|Promise&lt;boolean&gt;|boolean { ... }<br>}<br><br>{ path: ..., canActivate: [<b>CanActivate</b>Guard] }</code></td>
<td><p>An interface for defining a class that the router should call first to determine if it should activate this component. Should return a boolean or an Observable/Promise that resolves to a boolean.</p>
</td>
</tr><tr>
<td><code>class&nbsp;<b>CanDeactivate</b>Guard&nbsp;implements&nbsp;<b>CanDeactivate</b>&lt;T&gt;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;canDeactivate(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;component:&nbsp;T,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;route:&nbsp;ActivatedRouteSnapshot,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state:&nbsp;RouterStateSnapshot<br>&nbsp;&nbsp;&nbsp;&nbsp;):&nbsp;Observable&lt;boolean&gt;|Promise&lt;boolean&gt;|boolean&nbsp;{&nbsp;...&nbsp;}<br>}<br><br>{&nbsp;path:&nbsp;...,&nbsp;canDeactivate:&nbsp;[<b>CanDeactivate</b>Guard]&nbsp;}</code></td>
<td><code>class <b>CanDeactivate</b>Guard implements <b>CanDeactivate</b>&lt;T&gt; {<br> canDeactivate(<br> component: T,<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable&lt;boolean&gt;|Promise&lt;boolean&gt;|boolean { ... }<br>}<br><br>{ path: ..., canDeactivate: [<b>CanDeactivate</b>Guard] }</code></td>
<td><p>An interface for defining a class that the router should call first to determine if it should deactivate this component after a navigation. Should return a boolean or an Observable/Promise that resolves to a boolean.</p>
</td>
</tr><tr>
<td><code>class&nbsp;<b>CanActivateChild</b>Guard&nbsp;implements&nbsp;<b>CanActivateChild</b>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;canActivateChild(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;route:&nbsp;ActivatedRouteSnapshot,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state:&nbsp;RouterStateSnapshot<br>&nbsp;&nbsp;&nbsp;&nbsp;):&nbsp;Observable&lt;boolean&gt;|Promise&lt;boolean&gt;|boolean&nbsp;{&nbsp;...&nbsp;}<br>}<br><br>{&nbsp;path:&nbsp;...,&nbsp;canActivateChild:&nbsp;[CanActivateGuard],<br>&nbsp;&nbsp;&nbsp;&nbsp;children:&nbsp;...&nbsp;}</code></td>
<td><code>class <b>CanActivateChild</b>Guard implements <b>CanActivateChild</b> {<br> canActivateChild(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable&lt;boolean&gt;|Promise&lt;boolean&gt;|boolean { ... }<br>}<br><br>{ path: ..., canActivateChild: [CanActivateGuard],<br> children: ... }</code></td>
<td><p>An interface for defining a class that the router should call first to determine if it should activate the child route. Should return a boolean or an Observable/Promise that resolves to a boolean.</p>
</td>
</tr><tr>
<td><code>class&nbsp;<b>Resolve</b>Guard&nbsp;implements&nbsp;<b>Resolve</b>&lt;T&gt;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;resolve(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;route:&nbsp;ActivatedRouteSnapshot,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state:&nbsp;RouterStateSnapshot<br>&nbsp;&nbsp;&nbsp;&nbsp;):&nbsp;Observable&lt;any&gt;|Promise&lt;any&gt;|any&nbsp;{&nbsp;...&nbsp;}<br>}<br><br>{&nbsp;path:&nbsp;...,&nbsp;resolve:&nbsp;[<b>Resolve</b>Guard]&nbsp;}</code></td>
<td><code>class <b>Resolve</b>Guard implements <b>Resolve</b>&lt;T&gt; {<br> resolve(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable&lt;any&gt;|Promise&lt;any&gt;|any { ... }<br>}<br><br>{ path: ..., resolve: [<b>Resolve</b>Guard] }</code></td>
<td><p>An interface for defining a class that the router should call first to resolve route data before rendering the route. Should return a value or an Observable/Promise that resolves to a value.</p>
</td>
</tr><tr>
<td><code>class&nbsp;<b>CanLoad</b>Guard&nbsp;implements&nbsp;<b>CanLoad</b>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;canLoad(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;route:&nbsp;Route<br>&nbsp;&nbsp;&nbsp;&nbsp;):&nbsp;Observable&lt;boolean&gt;|Promise&lt;boolean&gt;|boolean&nbsp;{&nbsp;...&nbsp;}<br>}<br><br>{&nbsp;path:&nbsp;...,&nbsp;canLoad:&nbsp;[<b>CanLoad</b>Guard],&nbsp;loadChildren:&nbsp;...&nbsp;}</code></td>
<td><code>class <b>CanLoad</b>Guard implements <b>CanLoad</b> {<br> canLoad(<br> route: Route<br> ): Observable&lt;boolean&gt;|Promise&lt;boolean&gt;|boolean { ... }<br>}<br><br>{ path: ..., canLoad: [<b>CanLoad</b>Guard], loadChildren: ... }</code></td>
<td><p>An interface for defining a class that the router should call first to check if the lazy loaded module should be loaded. Should return a boolean or an Observable/Promise that resolves to a boolean.</p>
</td>
</tr>

View File

@ -50,7 +50,10 @@ The `get()` method on `HttpClient` makes accessing this data straightforward.
```javascript
@Component(...)
export class MyComponent implements NgOnInit {
export class MyComponent implements OnInit {
results: string[];
// Inject HttpClient into your component or service.
constructor(private http: HttpClient) {}
@ -268,12 +271,12 @@ has a single `intercept()` method. Here is a simple interceptor which does nothi
```javascript
import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest) from '@angular/common/http';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';
@Injectable()
export class NoopInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(Req);
return next.handle(req);
}
}
```
@ -332,7 +335,7 @@ If you have a need to mutate the request body, you need to copy the request body
Since requests are immutable, they cannot be modified directly. To mutate them, use `clone()`:
```javascript
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpError<any>> {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// This is a duplicate. It is exactly the same as the original.
const dupReq = req.clone();
@ -349,7 +352,7 @@ A common use of interceptors is to set default headers on outgoing responses. Fo
```javascript
import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest) from '@angular/common/http';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
@ -357,7 +360,7 @@ export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Get the auth header from the service.
const authHeader: this.auth.getAuthorizationHeader();
const authHeader = this.auth.getAuthorizationHeader();
// Clone the request to add the new header.
const authReq = req.clone({headers: req.headers.set('Authorization', authHeader)});
// Pass on the cloned request instead of the original request.
@ -389,12 +392,12 @@ export class TimingInterceptor implements HttpInterceptor {
constructor(private auth: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const elapsed = Date.now();
const started = Date.now();
return next
.handle(req)
.do(event => {
if (event instanceof HttpResponse) {
const time = Date.now() - started;
const elapsed = Date.now() - started;
console.log(`Request for ${req.urlWithParams} took ${elapsed} ms.`);
}
});
@ -598,7 +601,7 @@ it('expects a GET request', inject([HttpClient, HttpTestingController], (http: H
// At this point, the request is pending, and no response has been
// sent. The next step is to expect that the request happened.
const req = httpMock.expectOne('/test');
const req = httpMock.expectOne('/data');
// If no request with that URL was made, or if multiple requests match,
// expectOne() would throw. However this test makes only one request to
@ -611,7 +614,7 @@ it('expects a GET request', inject([HttpClient, HttpTestingController], (http: H
req.flush({name: 'Test Data'});
// Finally, assert that there are no outstanding requests.
mockHttp.verify();
httpMock.verify();
}));
```
@ -619,7 +622,7 @@ The last step, verifying that no requests remain outstanding, is common enough f
```javascript
afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => {
mockHttp.verify();
httpMock.verify();
}));
```
@ -628,7 +631,7 @@ afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => {
If matching by URL isn't sufficient, it's possible to implement your own matching function. For example, you could look for an outgoing request that has an Authorization header:
```javascript
const req = mockHttp.expectOne((req) => req.headers.has('Authorization'));
const req = httpMock.expectOne((req) => req.headers.has('Authorization'));
```
Just as with the `expectOne()` by URL in the test above, if 0 or 2+ requests match this expectation, it will throw.
@ -639,7 +642,7 @@ If you need to respond to duplicate requests in your test, use the `match()` API
```javascript
// Expect that 5 pings have been made and flush them.
const reqs = mockHttp.match('/ping');
const reqs = httpMock.match('/ping');
expect(reqs.length).toBe(5);
reqs.forEach(req => req.flush());
```

View File

@ -397,8 +397,7 @@ created under test or before you decide to display it.
Constructors should do no more than set the initial local variables to simple values.
An `ngOnInit()` is a good place for a component to fetch its initial data. The
[Tour of Heroes Tutorial](tutorial/toh-pt4#oninit) and [HTTP Client](guide/http#oninit)
guides show how.
[Tour of Heroes Tutorial](tutorial/toh-pt4#oninit) guide shows how.
Remember also that a directive's data-bound input properties are not set until _after construction_.

View File

@ -615,7 +615,7 @@ Once the application begins, the app root injector is closed to new providers.
Time passes and application logic triggers lazy loading of a module.
Angular must add the lazy-loaded module's providers to an injector somewhere.
It can't added them to the app root injector because that injector is closed to new providers.
It can't add them to the app root injector because that injector is closed to new providers.
So Angular creates a new child injector for the lazy-loaded module context.

View File

@ -163,7 +163,6 @@ without waiting for Angular updates.
***angular-in-memory-web-api***: An Angular-supported library that simulates a remote server's web api
without requiring an actual server or real HTTP calls.
Good for demos, samples, and early stage development (before you even have a server).
Read about it in the [HTTP Client](guide/http#in-mem-web-api) page.
***bootstrap***: [Bootstrap](http://getbootstrap.com/) is a popular HTML and CSS framework for designing responsive web apps.
Some of the samples improve their appearance with *bootstrap*.

View File

@ -1080,8 +1080,11 @@ To get access to the `FormArray` class, import it into `hero-detail.component.ts
To _work_ with a `FormArray` you do the following:
1. Define the items (`FormControls` or `FormGroups`) in the array.
1. Initialize the array with items created from data in the _data model_.
1. Add and remove items as the user requires.
In this guide, you define a `FormArray` for `Hero.addresses` and

View File

@ -255,11 +255,11 @@ During each navigation, the `Router` emits navigation events through the `Router
<tr>
<td>
<code>RouteConfigLoadStart</code>
<code>RouteConfigLoadEnd</code>
</td>
<td>
An [event](api/router/RouteConfigLoadStart) triggered after a route has been lazy loaded.
An [event](api/router/RouteConfigLoadEnd) triggered after a route has been lazy loaded.
</td>
</tr>
@ -1830,7 +1830,7 @@ Finally, you activate the observable with `subscribe` method and (re)set the com
#### _ParamMap_ API
The `ParamMap` API is inspired by the [URLSearchParams interface](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParamsOPut). It provides methods
The `ParamMap` API is inspired by the [URLSearchParams interface](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams). It provides methods
to handle parameter access for both route parameters (`paramMap`) and query parameters (`queryParamMap`).
<table>

View File

@ -178,7 +178,7 @@ For a discussion of the unit testing setup files, [see below](guide/testing#setu
{@a isolated-v-testing-utilities}
### Isolated unit tests vs. the Angular testing utilites
### Isolated unit tests vs. the Angular testing utilities
[Isolated unit tests](guide/testing#isolated-unit-tests "Unit testing without the Angular testing utilities")
examine an instance of a class all by itself without any dependence on Angular or any injected values.

View File

@ -247,11 +247,9 @@ next to the original _ES5_ version for comparison:
</code-pane>
</code-tabs>
<div class="callout is-helpful">
{@a name-constructor}
### Name the constructor
<div class="callout is-helpful">
A **named** constructor displays clearly in the console log
if the component throws a runtime error.

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 KiB

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 257 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -573,5 +573,14 @@
"website": "http://www.methotic.com",
"bio": "Thierry is a senior consultant and trainer, specialized on Angular, and a Google Developer Expert.",
"group": "GDE"
},
"gerardsans": {
"name": "Gerard Sans",
"picture": "gerardsans.jpg",
"twitter": "gerardsans",
"website": "https://medium.com/@gerard.sans",
"bio": "Gerard is very excited about the future of the Web and JavaScript. Always happy Computer Science Engineer and humble Google Developer Expert. He loves to share his learnings by giving talks, trainings and writing about cool technologies. He loves running AngularZone and GraphQL London, mentoring students and giving back to the community.",
"group": "GDE"
}
}

View File

@ -8,7 +8,7 @@
<div class="feature-section">
<div class="feature-header">
<div class="text-headline">Cross Platform</div>
<img src="../assets/images/icons/feature-icon.svg" height="70px">
<img src="generated/images/marketing/features/feature-icon.svg" height="70px">
</div>
<div class="feature-row">
@ -34,7 +34,7 @@
<div class="feature-section">
<div class="feature-header">
<div class="text-headline">Speed and Performance</div>
<img src="../assets/images/icons/feature-icon.svg" height="70px">
<img src="generated/images/marketing/features/feature-icon.svg" height="70px">
</div>
<div class="feature-row">
@ -59,7 +59,7 @@
<div class="feature-section">
<div class="feature-header">
<div class="text-headline">Productivity</div>
<img src="../assets/images/icons/feature-icon.svg" height="70px">
<img src="generated/images/marketing/features/feature-icon.svg" height="70px">
</div>
<div class="feature-row">
@ -84,7 +84,7 @@
<div class="feature-section">
<div class="feature-header">
<div class="text-headline">Full Development Story</div>
<img src="../assets/images/icons/feature-icon.svg" height="70px">
<img src="generated/images/marketing/features/feature-icon.svg" height="70px">
</div>
<div class="feature-row">

View File

@ -30,7 +30,7 @@
<!--Announcement Bar-->
<div class="homepage-container">
<div class="announcement-bar">
<img src="generated/images/marketing/angular-mix.png" height="40" width="151">
<img src="generated/images/marketing/home/angular-mix.png" height="40" width="151">
<p>Join us at our newest event, October 2017</p>
<a class="button" href="https://angularmix.com/">Learn More</a>
</div>
@ -40,7 +40,7 @@
<div layout="row" layout-xs="column" class="home-row homepage-container">
<div class="promo-img-container promo-1">
<div>
<img height="222" width="340" src="assets/images/home/responsive-framework.svg" alt="responsive framework">
<img height="222" width="340" src="generated/images/marketing/home/responsive-framework.svg" alt="responsive framework">
</div>
</div>
@ -65,7 +65,7 @@
<div class="promo-img-container promo-2">
<div>
<img height="222" width="323" src="assets/images/home/speed-performance.svg" alt="speed and performance">
<img height="222" width="323" src="generated/images/marketing/home/speed-performance.svg" alt="speed and performance">
</div>
</div>
</div>
@ -74,7 +74,7 @@
<!-- Group 3-->
<div layout="row" layout-xs="column" class="home-row">
<div class="promo-img-container promo-3">
<div><img src="assets/images/home/joyful-development.png" alt="IDE example"></div>
<div><img src="generated/images/marketing/home/joyful-development.svg" alt="IDE example"></div>
</div>
<div class="text-container">
@ -100,7 +100,7 @@
<div class="promo-img-container promo-4">
<div>
<img src="assets/images/home/loved-by-millions.png" alt="angular on the map" width="455" height="228">
<img src="generated/images/marketing/home/loved-by-millions.svg" alt="angular on the map" width="455" height="228">
</div>
</div>
</div>
@ -110,7 +110,7 @@
<a href="guide/quickstart">
<div class="card">
<img src="../assets/images/icons/code-icon.svg" height="70px">
<img src="generated/images/marketing/home/code-icon.svg" height="70px">
<div class="card-text-container">
<div class="text-headline">Get Started</div>
<p>Start building your Angular application.</p>

View File

@ -1,116 +0,0 @@
<header class="hero background-sky">
<h1 class="hero-title no-toc">News</h1>
<div class="clear"></div>
</header>
<artice>
<div class="grid-fluid l-space-bottom-2">
<div class="c12 text-center"><h3 class="text-headline text-uppercase"> Core Team</h3></div>
<div class="clear"></div>
</div>
<div class="grid-fluid">
<div class="c6">
<div class="article-card">
<div class="date">Oct 12, 2016</div>
<div class="title"><a target="_blank"
href="http://angularjs.blogspot.com/2016/10/angular-210-now-available.html">Angular
2.1.0 Now Available</a></div>
<p>Angular version 2.1.0 - incremental-metamorphosis - is a minor release following our
announced adoption of Semantic Versioning...</p>
<div class="author"><img src="generated/images/bios/stephenfluin.jpg">
<div class="posted">Posted by <b>Stephen Fluin</b></div>
</div>
</div>
</div>
<div class="c6">
<div class="article-card">
<div class="date">Oct 7, 2016</div>
<div class="title"><a target="_blank"
href="http://angularjs.blogspot.com/2016/10/versioning-and-releasing-angular.html">Versioning
and Releasing Angular</a></div>
<p>In order for the ecosystem around Angular to thrive, developers need stability from the
Angular framework so that reusable components and libraries, tools and learned practices
dont go obsolete unexpectedly...</p>
<div class="author"><img src="generated/images/bios/igor-minar.jpg">
<div class="posted">Posted by <b>Igor Minar</b></div>
</div>
</div>
</div>
</div>
<div class="grid-fluid l-space-bottom-2 l-space-top-4">
<div class="c12 text-center"><h3 class="text-headline text-uppercase"> Developer Community</h3>
</div>
<div class="clear"></div>
</div>
<div class="grid-fluid">
<div class="c6">
<div class="article-card">
<div class="date">Oct 30, 2016</div>
<div class="title"><a target="_blank"
href="https://www.thepolyglotdeveloper.com/2016/10/use-pre-populated-sqlite-database-nativescript-angular-2/">Use
A Pre-Populated SQLite Database With NativeScript And Angular 2</a></div>
<p>I figured it would be a good idea to demonstrate how to ship a NativeScript Angular 2
application with a pre-filled SQLite database rather than populating it on-the-fly....</p>
<div class="author"><img src="generated/images/bios/shield-bio-placeholder.png">
<div class="posted">Posted by <b>Nic Raboy</b></div>
</div>
</div>
</div>
<div class="c6">
<div class="article-card">
<div class="date">Oct 13, 2016</div>
<div class="title"><a target="_blank"
href="http://blog.thoughtram.io/angular/2016/10/13/two-way-data-binding-in-angular-2.html">Two-way
Data Binding in Angular 2</a></div>
<p>If there was one feature in Angular that made us go “Wow”, then it was probably its
two-way data binding system. Changes in the application state have been automagically
reflected into the view...</p>
<div class="author"><img src="generated/images/bios/angular-gde-bio-placeholder.png">
<div class="posted">Posted by <b>Pascal Precht</b></div>
</div>
</div>
</div>
</div>
<div class="grid-fluid">
<div class="c6">
<div class="article-card">
<div class="date">Oct 10, 2016</div>
<div class="title"><a target="_blank"
href="http://www.creativebloq.com/how-to/build-a-material-design-app-with-angular-2">Build
a Material Design app with Angular 2</a></div>
<p>This walkthrough reveals how to create a DialogComponent and to-do app with Angular
Material and the Angular CLI...</p>
<div class="author"><img src="generated/images/bios/shield-bio-placeholder.png">
<div class="posted">Posted by <b>Daniel Zen</b></div>
</div>
</div>
</div>
<div class="c6">
<div class="article-card">
<div class="date">Sept 30, 2016</div>
<div class="title"><a target="_blank"
href="http://www.simb.co/angular-cli-using-docker/?platform=hootsuite">Using
Angular CLI to create Angular 2 applications in Docker</a></div>
<p>Angular CLI is a great tool for developing Angular 2 applications. I thought it would be
fun to do a quick demo...</p>
<div class="author"><img src="generated/images/bios/shield-bio-placeholder.png">
<div class="posted">Posted by <b>Simeon Bateman</b></div>
</div>
</div>
</div>
</div>
<div class="grid-fluid l-space-bottom-2 l-space-top-4">
<div class="c12 text-center"><h3 class="text-headline text-uppercase">Twitter</h3></div>
<div class="clear"></div>
<div class="grid-fluid">
<div class="c3"><p></p></div>
<div class="c6">
<div class="article-card">
<div class="title"><a href="http://twitter.com/angularjs" data-show-count="false"
class="twitter-follow-button">Follow @angularjs</a></div>
<p><a class="twitter-timeline" data-chrome="nofooter noborders noheader"
href="http://twitter.com/angularjs" data-widget-id="700150278465523713"></a></p>
</div>
</div>
</div>
</div>
</article>

View File

@ -17,7 +17,7 @@
"title": "Events"
},
{
"url": "https://blog.angularjs.org/",
"url": "https://blog.angular.io/",
"title": "Blog"
}
],
@ -39,7 +39,7 @@
"title": "Events"
},
{
"url": "https://blog.angularjs.org/",
"url": "https://blog.angular.io/",
"title": "Blog"
}
]

View File

@ -137,7 +137,7 @@ Create a file in the `app` folder called `hero.service.ts`.
The naming convention for service files is the service name in lowercase followed by `.service`.
For a multi-word service name, use lower [dash-case](guide/glossary).
For a multi-word service name, use lower [dash-case](guide/glossary#dash-case).
For example, the filename for `SpecialSuperHeroService` is `special-super-hero.service.ts`.
</div>

View File

@ -83,10 +83,6 @@ Added hero "Zero" to confirm that the data service can handle a hero with `id==0
Don't worry about the details of this backend substitution; you can
skip it when you have a real web API server.
Read more about the in-memory web API in the
[Appendix: Tour of Heroes in-memory web api](guide/http#in-mem-web-api)
section of the [HTTP Client](guide/http#in-mem-web-api) page.
</div>
## Heroes and HTTP

View File

@ -10,8 +10,8 @@
},
"static.ignore": [
"\\.js\\.map$",
"^/assets/images/.*/unused/",
"^/generated/(?:docs/(?!api/api-list\\.json).*|images|live-examples|zips)/"
"^(?:/|\\\\)assets(?:/|\\\\)images(?:/|\\\\).*(?:/|\\\\)_unused(?:/|\\\\)",
"^(?:/|\\\\)generated(?:/|\\\\)(?:docs(?:/|\\\\)(?!api(?:/|\\\\)api-list\\.json).*|images(?:/|\\\\)(?!marketing(?:/|\\\\)).*|live-examples|zips)(?:/|\\\\)"
],
"static.versioned": [
"\\.[0-9a-z]{20}\\."

View File

@ -33,8 +33,9 @@
"docs-test": "node tools/transforms/test.js",
"serve-and-sync": "concurrently --kill-others \"yarn docs-watch\" \"yarn start\"",
"~~update-webdriver": "webdriver-manager update --standalone false --gecko false",
"boilerplate:add": "node ./tools/examples/add-example-boilerplate add",
"boilerplate:remove": "node ./tools/examples/add-example-boilerplate remove",
"boilerplate:add": "node ./tools/examples/example-boilerplate add",
"boilerplate:remove": "node ./tools/examples/example-boilerplate remove",
"boilerplate:test": "node tools/examples/test.js",
"generate-plunkers": "node ./tools/plunker-builder/generatePlunkers",
"generate-zips": "node ./tools/example-zipper/generateZips",
"sw-manifest": "ngu-sw-manifest --dist dist --in ngsw-manifest.json --out dist/ngsw-manifest.json",
@ -48,17 +49,18 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^4.2.4",
"@angular/common": "^4.2.4",
"@angular/compiler": "^4.2.4",
"@angular/core": "^4.2.4",
"@angular/forms": "^4.2.4",
"@angular/http": "^4.2.4",
"@angular/material": "^2.0.0-beta.7",
"@angular/platform-browser": "^4.2.4",
"@angular/platform-browser-dynamic": "^4.2.4",
"@angular/platform-server": "^4.2.4",
"@angular/router": "^4.2.4",
"@angular/animations": "^4.3.1",
"@angular/cdk": "^2.0.0-beta.8",
"@angular/common": "^4.3.1",
"@angular/compiler": "^4.3.1",
"@angular/core": "^4.3.1",
"@angular/forms": "^4.3.1",
"@angular/http": "^4.3.1",
"@angular/material": "^2.0.0-beta.8",
"@angular/platform-browser": "^4.3.1",
"@angular/platform-browser-dynamic": "^4.3.1",
"@angular/platform-server": "^4.3.1",
"@angular/router": "^4.3.1",
"@angular/service-worker": "^1.0.0-beta.16",
"classlist.js": "^1.1.20150312",
"core-js": "^2.4.1",
@ -72,7 +74,7 @@
},
"devDependencies": {
"@angular/cli": "angular/cli-builds#webpack-next",
"@angular/compiler-cli": "^4.2.4",
"@angular/compiler-cli": "^4.3.1",
"@types/jasmine": "^2.5.52",
"@types/node": "~6.0.60",
"archiver": "^1.3.0",
@ -81,7 +83,7 @@
"concurrently": "^3.4.0",
"cross-spawn": "^5.1.0",
"dgeni": "^0.4.7",
"dgeni-packages": "^0.20.0-rc.5",
"dgeni-packages": "^0.20.0",
"entities": "^1.1.1",
"eslint": "^3.19.0",
"eslint-plugin-jasmine": "^2.2.0",

View File

@ -4,12 +4,8 @@ set -eu -o pipefail
readonly thisDir=$(cd $(dirname $0); pwd)
readonly parentDir=$(dirname $thisDir)
readonly TOKEN=${ANGULAR_PAYLOAD_FIREBASE_TOKEN:-}
readonly PROJECT_NAME="angular-payload-size"
# temporarily turn on debugging - we disable it later in the script to prevent token leak
set -x
source ${thisDir}/_payload-limits.sh
failed=false
@ -45,15 +41,21 @@ timestamp=$(date +%s)
payloadData="$payloadData\"timestamp\": $timestamp, "
# Add change source: application, dependencies, or 'application+dependencies'
applicationChanges=$(git diff --name-only $TRAVIS_COMMIT_RANGE $parentDir | grep -v ${parentDir}/content | grep -v ${parentDir}/yarn.lock | wc -l)
dependencyChanges=$(git diff --name-only $TRAVIS_COMMIT_RANGE ${parentDir}/yarn.lock | wc -l)
applicationChanged=false
dependenciesChanged=false
if [[ $(git diff --name-only $TRAVIS_COMMIT_RANGE $parentDir | grep -v aio/yarn.lock | grep -v content) ]]; then
applicationChanged=true
fi
if [[ $(git diff --name-only $TRAVIS_COMMIT_RANGE $parentDir/yarn.lock) ]]; then
dependenciesChanged=true
fi
if [[ $dependencyChanges -eq 1 ]] && [[ $applicationChanges -eq 0 ]]; then
if $dependenciesChanged && $applicationChanged; then
change='application+dependencies'
elif $dependenciesChanged; then
# only yarn.lock changed
change='dependencies'
elif [[ $dependencyChanges -eq 1 ]] && [[ $applicationChanges -gt 0 ]]; then
change='application+dependencies'
elif [[ $applicationChanges -gt 0 ]]; then
elif $applicationChanged; then
change='application'
else
# Nothing changed in aio/
@ -71,7 +73,7 @@ if [[ "$TRAVIS_PULL_REQUEST" == "false" ]]; then
# WARNING: FIREBASE_TOKEN should NOT be printed.
set +x
firebase database:update --data "$payloadData" --project $PROJECT_NAME --confirm --token "$TOKEN" $dbPath
firebase database:update --data "$payloadData" --project $PROJECT_NAME --confirm --token "$ANGULAR_PAYLOAD_FIREBASE_TOKEN" $dbPath
fi
if [[ $failed = true ]]; then

View File

@ -19,11 +19,6 @@ const config = require('lighthouse/lighthouse-core/config/default.js');
// Constants
const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer/';
// Specify the path to Chrome on Travis
if (process.env.TRAVIS) {
process.env.LIGHTHOUSE_CHROMIUM_PATH = process.env.CHROME_BIN;
}
// Run
_main(process.argv.slice(2));

View File

@ -632,7 +632,6 @@ describe('AppComponent', () => {
it('should initialize the search worker', inject([SearchService], (searchService: SearchService) => {
fixture.detectChanges(); // triggers ngOnInit
expect(searchService.initWorker).toHaveBeenCalled();
expect(searchService.loadIndex).toHaveBeenCalled();
}));
});

View File

@ -48,7 +48,7 @@ export class AppComponent implements OnInit {
* the styling of individual pages.
* You will get three classes:
*
* * `page-...`: computed from the current document id (e.g. news, guide-security, tutorial-toh-pt2)
* * `page-...`: computed from the current document id (e.g. events, guide-security, tutorial-toh-pt2)
* * `folder-...`: computed from the top level folder for an id (e.g. guide, tutorial, etc)
* * `view-...`: computef from the navigation view (e.g. SideNav, TopBar, etc)
*/
@ -76,8 +76,8 @@ export class AppComponent implements OnInit {
get homeImageUrl() {
return this.isSideBySide ?
'assets/images/logos/standard/logo-nav@2x.png' :
'assets/images/logos/standard/shield-large.svg';
'assets/images/logos/angular/logo-nav@2x.png' :
'assets/images/logos/angular/shield-large.svg';
}
get isOpened() { return this.isSideBySide && this.isSideNavDoc; }
get mode() { return this.isSideBySide ? 'side' : 'over'; }
@ -111,8 +111,8 @@ export class AppComponent implements OnInit {
ngOnInit() {
// Do not initialize the search on browsers that lack web worker support
if ('Worker' in window) {
this.searchService.initWorker('app/search/search-worker.js');
this.searchService.loadIndex();
// Delay initialization by up to 2 seconds
this.searchService.initWorker('app/search/search-worker.js', 2000);
}
this.onResize(window.innerWidth);

View File

@ -65,6 +65,13 @@ describe('CodeExampleComponent', () => {
expect(actual).toBe('Great Example');
});
it('should remove the `title` attribute after initialisation', () => {
TestBed.overrideComponent(HostComponent, {
set: {template: '<code-example title="Great Example"></code-example>'}});
createComponent(oneLineCode);
expect(codeExampleDe.nativeElement.getAttribute('title')).toEqual(null);
});
it('should pass hideCopy to CodeComonent', () => {
TestBed.overrideComponent(HostComponent, {
set: {template: '<code-example hideCopy="true"></code-example>'}});

View File

@ -45,6 +45,8 @@ export class CodeExampleComponent implements OnInit {
this.path = element.getAttribute('path') || '';
this.region = element.getAttribute('region') || '';
this.title = element.getAttribute('title') || '';
// Now remove the title attribute to prevent unwanted tooltip popups when hovering over the code.
element.removeAttribute('title');
this.isAvoid = this.path.indexOf('.avoid.') !== -1;
this.hideCopy = this.isAvoid || getBoolFromAttribute(element, ['hidecopy', 'hide-copy']);

View File

@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
import { ComponentFixture, fakeAsync, inject, TestBed, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { SearchBoxComponent } from './search-box.component';
import { MockSearchService } from 'testing/search.service';
@ -36,30 +36,67 @@ describe('SearchBoxComponent', () => {
});
describe('initialisation', () => {
it('should get the current search query from the location service', inject([LocationService], (location: MockLocationService) => {
it('should get the current search query from the location service',
inject([LocationService], (location: MockLocationService) => fakeAsync(() => {
location.search.and.returnValue({ search: 'initial search' });
component.ngOnInit();
expect(location.search).toHaveBeenCalled();
tick(300);
expect(host.searchHandler).toHaveBeenCalledWith('initial search');
expect(component.searchBox.nativeElement.value).toEqual('initial search');
})));
});
describe('onSearch', () => {
it('should debounce by 300ms', fakeAsync(() => {
component.doSearch();
expect(host.searchHandler).not.toHaveBeenCalled();
tick(300);
expect(host.searchHandler).toHaveBeenCalled();
}));
it('should pass through the value of the input box', fakeAsync(() => {
const input = fixture.debugElement.query(By.css('input'));
input.nativeElement.value = 'some query (input)';
component.doSearch();
tick(300);
expect(host.searchHandler).toHaveBeenCalledWith('some query (input)');
}));
it('should only send events if the search value has changed', fakeAsync(() => {
const input = fixture.debugElement.query(By.css('input'));
input.nativeElement.value = 'some query';
component.doSearch();
tick(300);
expect(host.searchHandler).toHaveBeenCalledTimes(1);
component.doSearch();
tick(300);
expect(host.searchHandler).toHaveBeenCalledTimes(1);
input.nativeElement.value = 'some other query';
component.doSearch();
tick(300);
expect(host.searchHandler).toHaveBeenCalledTimes(2);
}));
});
describe('on input', () => {
it('should trigger the onSearch event', () => {
it('should trigger a search', () => {
const input = fixture.debugElement.query(By.css('input'));
input.nativeElement.value = 'some query (input)';
spyOn(component, 'doSearch');
input.triggerEventHandler('input', { });
expect(host.searchHandler).toHaveBeenCalledWith('some query (input)');
expect(component.doSearch).toHaveBeenCalled();
});
});
describe('on keyup', () => {
it('should trigger the onSearch event', () => {
it('should trigger a search', () => {
const input = fixture.debugElement.query(By.css('input'));
input.nativeElement.value = 'some query (keyup)';
spyOn(component, 'doSearch');
input.triggerEventHandler('keyup', { });
expect(host.searchHandler).toHaveBeenCalledWith('some query (keyup)');
expect(component.doSearch).toHaveBeenCalled();
});
});
@ -73,28 +110,11 @@ describe('SearchBoxComponent', () => {
});
describe('on click', () => {
it('should trigger the search event', () => {
it('should trigger a search', () => {
const input = fixture.debugElement.query(By.css('input'));
input.nativeElement.value = 'some query (click)';
spyOn(component, 'doSearch');
input.triggerEventHandler('click', { });
expect(host.searchHandler).toHaveBeenCalledWith('some query (click)');
});
});
describe('event filtering', () => {
it('should only send events if the search value has changed', () => {
const input = fixture.debugElement.query(By.css('input'));
input.nativeElement.value = 'some query';
input.triggerEventHandler('input', { });
expect(host.searchHandler).toHaveBeenCalledTimes(1);
input.triggerEventHandler('input', { });
expect(host.searchHandler).toHaveBeenCalledTimes(1);
input.nativeElement.value = 'some other query';
input.triggerEventHandler('input', { });
expect(host.searchHandler).toHaveBeenCalledTimes(2);
expect(component.doSearch).toHaveBeenCalled();
});
});

View File

@ -26,10 +26,11 @@ import 'rxjs/add/operator/distinctUntilChanged';
})
export class SearchBoxComponent implements OnInit {
private searchDebounce = 300;
private searchSubject = new Subject<string>();
@ViewChild('searchBox') searchBox: ElementRef;
@Output() onSearch = this.searchSubject.distinctUntilChanged();
@Output() onSearch = this.searchSubject.distinctUntilChanged().debounceTime(this.searchDebounce);
@Output() onFocus = new EventEmitter<string>();
constructor(private locationService: LocationService) { }

View File

@ -1,24 +1,62 @@
import { ReflectiveInjector, NgZone } from '@angular/core';
import { fakeAsync, tick } from '@angular/core/testing';
import { Observable } from 'rxjs/Observable';
import { SearchService } from './search.service';
import { WebWorkerClient } from 'app/shared/web-worker';
describe('SearchService', () => {
let injector: ReflectiveInjector;
let service: SearchService;
let sendMessageSpy: jasmine.Spy;
let mockWorker: WebWorkerClient;
beforeEach(() => {
sendMessageSpy = jasmine.createSpy('sendMessage').and.returnValue(Observable.of({}));
mockWorker = { sendMessage: sendMessageSpy } as any;
spyOn(WebWorkerClient, 'create').and.returnValue(mockWorker);
injector = ReflectiveInjector.resolveAndCreate([
SearchService,
{ provide: NgZone, useFactory: () => new NgZone({ enableLongStackTrace: false }) }
]);
service = injector.get(SearchService);
});
describe('loadIndex', () => {
it('should send a "load-index" message to the worker');
it('should connect the `ready` property to the response to the "load-index" message');
describe('initWorker', () => {
it('should create the worker and load the index after the specified delay', fakeAsync(() => {
service.initWorker('some/url', 100);
expect(WebWorkerClient.create).not.toHaveBeenCalled();
expect(mockWorker.sendMessage).not.toHaveBeenCalled();
tick(100);
expect(WebWorkerClient.create).toHaveBeenCalledWith('some/url', jasmine.any(NgZone));
expect(mockWorker.sendMessage).toHaveBeenCalledWith('load-index');
}));
});
describe('search', () => {
it('should send a "query-index" message to the worker');
it('should push the response to the `searchResults` observable');
beforeEach(() => {
// We must initialize the service before calling search
service.initWorker('some/url', 100);
});
it('should trigger a `loadIndex` synchronously', () => {
service.search('some query');
expect(mockWorker.sendMessage).toHaveBeenCalledWith('load-index');
});
it('should send a "query-index" message to the worker', () => {
service.search('some query');
expect(mockWorker.sendMessage).toHaveBeenCalledWith('query-index', 'some query');
});
it('should push the response to the `searchResults` observable', () => {
const mockSearchResults = { results: ['a', 'b'] };
(mockWorker.sendMessage as jasmine.Spy).and.returnValue(Observable.of(mockSearchResults));
let searchResults: any;
service.searchResults.subscribe(results => searchResults = results);
service.search('some query');
expect(searchResults).toEqual(mockSearchResults);
});
});
});

View File

@ -7,8 +7,10 @@ can be found in the LICENSE file at http://angular.io/license
import { NgZone, Injectable, Type } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { ReplaySubject } from 'rxjs/ReplaySubject';
import 'rxjs/add/operator/publishLast';
import 'rxjs/add/observable/race';
import 'rxjs/add/observable/timer';
import 'rxjs/add/operator/concatMap';
import 'rxjs/add/operator/publish';
import { WebWorkerClient } from 'app/shared/web-worker';
export interface SearchResults {
@ -27,26 +29,50 @@ export interface SearchResult {
@Injectable()
export class SearchService {
private worker: WebWorkerClient;
private ready: Observable<boolean>;
private resultsSubject = new ReplaySubject<SearchResults>(1);
readonly searchResults = this.resultsSubject.asObservable();
private searchesSubject = new ReplaySubject<string>(1);
searchResults: Observable<SearchResults>;
constructor(private zone: NgZone) {}
initWorker(workerUrl) {
this.worker = new WebWorkerClient(new Worker(workerUrl), this.zone);
}
loadIndex() {
const ready = this.ready = this.worker.sendMessage<boolean>('load-index').publishLast();
// trigger the index to be loaded immediately
ready.connect();
/**
* Initialize the search engine. We offer an `initDelay` to prevent the search initialisation from delaying the
* initial rendering of the web page. Triggering a search will override this delay and cause the index to be
* loaded immediately.
*
* @param workerUrl the url of the WebWorker script that runs the searches
* @param initDelay the number of milliseconds to wait before we load the WebWorker and generate the search index
*/
initWorker(workerUrl: string, initDelay: number) {
const searchResults = Observable
// Wait for the initDelay or the first search
.race(
Observable.timer(initDelay),
this.searchesSubject.first()
)
.concatMap(() => {
// Create the worker and load the index
const worker = WebWorkerClient.create(workerUrl, this.zone);
return worker.sendMessage('load-index').concatMap(() =>
// Once the index has loaded, switch to listening to the searches coming in
this.searchesSubject.switchMap((query) =>
// Each search gets switched to a web worker message, whose results are returned via an observable
worker.sendMessage<SearchResults>('query-index', query)
)
);
}).publish();
// Connect to the observable to kick off the timer
searchResults.connect();
// Expose the connected observable to the rest of the world
this.searchResults = searchResults;
}
/**
* Send a search query to the index.
* The results will appear on the `searchResults` observable.
*/
search(query: string) {
this.ready.concatMap(ready => {
return this.worker.sendMessage('query-index', query) as Observable<SearchResults>;
}).subscribe(results => this.resultsSubject.next(results));
this.searchesSubject.next(query);
}
}

View File

@ -16,7 +16,11 @@ export interface WebWorkerMessage {
export class WebWorkerClient {
private nextId = 0;
constructor(private worker: Worker, private zone: NgZone) {
static create(workerUrl: string, zone: NgZone) {
return new WebWorkerClient(new Worker(workerUrl), zone);
}
private constructor(private worker: Worker, private zone: NgZone) {
}
sendMessage<T>(type: string, payload?: any): Observable<T> {

View File

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

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