Compare commits
78 Commits
Author | SHA1 | Date | |
---|---|---|---|
8de2ace80a | |||
c977994864 | |||
12b8e1af55 | |||
9a188485f5 | |||
45a10419bc | |||
2245748c14 | |||
bcea196530 | |||
b9e32c833a | |||
be49e0ee93 | |||
bf95655a1a | |||
6bf5b84fa4 | |||
4836565ca7 | |||
750e4e8156 | |||
a0846194b7 | |||
bcf6b90c95 | |||
3ca2a0aa37 | |||
b4be96c65d | |||
434ff5fecb | |||
a1bb9c2d42 | |||
7e626bef0a | |||
a1e83a8ed2 | |||
cbeb197aa5 | |||
0330fa6b82 | |||
97135e8fd5 | |||
35bd07fc7b | |||
a8ac77b645 | |||
9ecd377a51 | |||
76171bd8b4 | |||
1f106d75bc | |||
a4fae8c405 | |||
33c07b3394 | |||
c9d06e676f | |||
c7c65d9fda | |||
257a9e3e6f | |||
c7c0a1688e | |||
7e95e2b0ba | |||
ddc286f4b5 | |||
3d17a3672e | |||
61d253f5fd | |||
54be25a7a1 | |||
b1757037fb | |||
f0476fcff0 | |||
a5c4bb5b96 | |||
4c1f32b0db | |||
383d8969ab | |||
333ffd8d32 | |||
d4679a0bc2 | |||
4ce29f3a5b | |||
17b7bc3e06 | |||
f19bd5f4f3 | |||
d503d25f29 | |||
5d275e994a | |||
d8c8b13bb8 | |||
4671168635 | |||
1ac78bfd5d | |||
4340beacea | |||
ec89f378fc | |||
4dd6863bc2 | |||
37c626e673 | |||
f0a110928b | |||
c39e7d1eb2 | |||
799bffb431 | |||
fda607cc2f | |||
cc3aa68123 | |||
306621d2d6 | |||
d204f7aa2a | |||
a94f5e8cbb | |||
1390afef23 | |||
b0346a6e45 | |||
e5da059994 | |||
ac92c3bb26 | |||
87157d7089 | |||
611dd12f0f | |||
969ce9dc2b | |||
34834a9e79 | |||
6e2ddccc2c | |||
55742e4737 | |||
0091b1e8db |
@ -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" }}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
@ -53,6 +56,7 @@ env:
|
||||
- CI_MODE=docs_test
|
||||
- CI_MODE=aio
|
||||
- CI_MODE=aio_e2e
|
||||
- CI_MODE=bazel
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
@ -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",
|
||||
]),
|
||||
)
|
38
CHANGELOG.md
@ -1,3 +1,41 @@
|
||||
<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)
|
||||
|
||||
|
12
WORKSPACE
@ -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",
|
||||
)
|
@ -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>
|
||||
|
||||
|
@ -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.
|
||||
|
@ -390,8 +390,9 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
<td>
|
||||
|
||||
|
||||
[Typed Array](guide/browser-support#typedarray) <br>[Blob](guide/browser-support#blob)<br>[FormData](guide/browser-support#formdata)
|
||||
[Typed Array](guide/browser-support#typedarray)<br>
|
||||
[Blob](guide/browser-support#blob)<br>
|
||||
[FormData](guide/browser-support#formdata)
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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>({ declarations: ..., imports: ...,<br> exports: ..., providers: ..., bootstrap: ...})<br>class MyModule {}</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> [MyRedComponent, MyBlueComponent, 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> [BrowserModule, 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> [MyRedComponent, 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> [MyService, { provide: ... }]</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> [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><input <b>[value]</b>="firstName"></code></td>
|
||||
<td><code><input <b>[value]</b>="firstName"></code></td>
|
||||
<td><p>Binds property <code>value</code> to the result of expression <code>firstName</code>.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><div <b>[attr.role]</b>="myAriaRole"></code></td>
|
||||
<td><code><div <b>[attr.role]</b>="myAriaRole"></code></td>
|
||||
<td><p>Binds attribute <code>role</code> to the result of expression <code>myAriaRole</code>.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><div <b>[class.extra-sparkle]</b>="isDelightful"></code></td>
|
||||
<td><code><div <b>[class.extra-sparkle]</b>="isDelightful"></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><div <b>[style.width.px]</b>="mySize"></code></td>
|
||||
<td><code><div <b>[style.width.px]</b>="mySize"></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><button <b>(click)</b>="readRainbow($event)"></code></td>
|
||||
<td><code><button <b>(click)</b>="readRainbow($event)"></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><div title="Hello <b>{{ponyName}}</b>"></code></td>
|
||||
<td><code><div title="Hello <b>{{ponyName}}</b>"></code></td>
|
||||
<td><p>Binds a property to an interpolated string, for example, "Hello Seabiscuit". Equivalent to:
|
||||
<code><div [title]="'Hello ' + ponyName"></code></p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><p>Hello <b>{{ponyName}}</b></p></code></td>
|
||||
<td><code><p>Hello <b>{{ponyName}}</b></p></code></td>
|
||||
<td><p>Binds text content to an interpolated string, for example, "Hello Seabiscuit".</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><my-cmp <b>[(title)]</b>="name"></code></td>
|
||||
<td><code><my-cmp <b>[(title)]</b>="name"></code></td>
|
||||
<td><p>Sets up two-way data binding. Equivalent to: <code><my-cmp [title]="name" (titleChange)="name=$event"></code></p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><video <b>#movieplayer</b> ...><br> <button <b>(click)</b>="movieplayer.play()"><br></video></code></td>
|
||||
<td><code><video <b>#movieplayer</b> ...><br> <button <b>(click)</b>="movieplayer.play()"><br></video></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><p <b>*myUnless</b>="myExpression">...</p></code></td>
|
||||
<td><code><p <b>*myUnless</b>="myExpression">...</p></code></td>
|
||||
<td><p>The <code>*</code> symbol turns the current element into an embedded template. Equivalent to:
|
||||
<code><ng-template [myUnless]="myExpression"><p>...</p></ng-template></code></p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><p>Card No.: <b>{{cardNumber | myCardNumberFormatter}}</b></p></code></td>
|
||||
<td><code><p>Card No.: <b>{{cardNumber | myCardNumberFormatter}}</b></p></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><p>Employer: <b>{{employer?.companyName}}</b></p></code></td>
|
||||
<td><code><p>Employer: <b>{{employer?.companyName}}</b></p></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><<b>svg:</b>rect x="0" y="0" width="100" height="100"/></code></td>
|
||||
<td><code><<b>svg:</b>rect x="0" y="0" width="100" height="100"/></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><<b>svg</b>><br> <rect x="0" y="0" width="100" height="100"/><br></<b>svg</b>></code></td>
|
||||
<td><code><<b>svg</b>><br> <rect x="0" y="0" width="100" height="100"/><br></<b>svg</b>></code></td>
|
||||
<td><p>An <code><svg></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><section <b>*ngIf</b>="showSection"></code></td>
|
||||
<td><code><section <b>*ngIf</b>="showSection"></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><li <b>*ngFor</b>="let item of list"></code></td>
|
||||
<td><code><li <b>*ngFor</b>="let item of list"></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><div <b>[ngSwitch]</b>="conditionExpression"><br> <ng-template <b>[<b>ngSwitchCase</b>]</b>="case1Exp">...</ng-template><br> <ng-template <b>ngSwitchCase</b>="case2LiteralString">...</ng-template><br> <ng-template <b>ngSwitchDefault</b>>...</ng-template><br></div></code></td>
|
||||
<td><code><div <b>[ngSwitch]</b>="conditionExpression"><br> <ng-template <b>[<b>ngSwitchCase</b>]</b>="case1Exp">...</ng-template><br> <ng-template <b>ngSwitchCase</b>="case2LiteralString">...</ng-template><br> <ng-template <b>ngSwitchDefault</b>>...</ng-template><br></div></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><div <b>[ngClass]</b>="{'active': isActive, 'disabled': isDisabled}"></code></td>
|
||||
<td><code><div <b>[ngClass]</b>="{'active': isActive, 'disabled': isDisabled}"></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><input <b>[(ngModel)]</b>="userName"></code></td>
|
||||
<td><code><input <b>[(ngModel)]</b>="userName"></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 MyComponent() {}</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 MyDirective() {}</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 MyPipe() {}</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 MyService() {}</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> '.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> [MyService, { provide: ... }]</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> 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> [MyService, { provide: ... }]</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> 'Hello {{name}}'<br><b>templateUrl:</b> '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> ['.primary {color: red}']<br><b>styleUrls:</b> ['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 component’s 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> 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><my-cmp [myProperty]="someExpression"></code>).</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@Output()</b> myEvent = new 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><my-cmp (myEvent)="doSomething()"></code>).</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@HostBinding('class.valid')</b> 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', ['$event'])</b> onClick(e) {...}</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> 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> 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> 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> 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: MyService, ...)</b> { ... }</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> { ... }</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> { ... }</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> { ... }</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> { ... }</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> { ... }</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> { ... }</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> { ... }</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> { ... }</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>{ <b>provide</b>: MyService, <b>useClass</b>: MyMockService }</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>{ <b>provide</b>: MyService, <b>useFactory</b>: myFactory }</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>{ <b>provide</b>: MyValue, <b>useValue</b>: 41 }</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 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><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><<b>router-outlet</b>></<b>router-outlet</b>><br><<b>router-outlet</b> name="aux"></<b>router-outlet</b>><br></code></td>
|
||||
<td><code><br><<b>router-outlet</b>></<b>router-outlet</b>><br><<b>router-outlet</b> name="aux"></<b>router-outlet</b>><br></code></td>
|
||||
<td><p>Marks the location to load the component of the active route.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><br><a routerLink="/path"><br><a <b>[routerLink]</b>="[ '/path', routeParam ]"><br><a <b>[routerLink]</b>="[ '/path', { matrixParam: 'value' } ]"><br><a <b>[routerLink]</b>="[ '/path' ]" [queryParams]="{ page: 1 }"><br><a <b>[routerLink]</b>="[ '/path' ]" fragment="anchor"><br></code></td>
|
||||
<td><code><br><a routerLink="/path"><br><a <b>[routerLink]</b>="[ '/path', routeParam ]"><br><a <b>[routerLink]</b>="[ '/path', { matrixParam: 'value' } ]"><br><a <b>[routerLink]</b>="[ '/path' ]" [queryParams]="{ page: 1 }"><br><a <b>[routerLink]</b>="[ '/path' ]" fragment="anchor"><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><a [routerLink]="[ '/path' ]" routerLinkActive="active"></code></td>
|
||||
<td><code><a [routerLink]="[ '/path' ]" routerLinkActive="active"></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 <b>CanActivate</b>Guard implements <b>CanActivate</b> {<br> canActivate(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<boolean>|Promise<boolean>|boolean { ... }<br>}<br><br>{ path: ..., canActivate: [<b>CanActivate</b>Guard] }</code></td>
|
||||
<td><code>class <b>CanActivate</b>Guard implements <b>CanActivate</b> {<br> canActivate(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<boolean>|Promise<boolean>|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 <b>CanDeactivate</b>Guard implements <b>CanDeactivate</b><T> {<br> canDeactivate(<br> component: T,<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<boolean>|Promise<boolean>|boolean { ... }<br>}<br><br>{ path: ..., canDeactivate: [<b>CanDeactivate</b>Guard] }</code></td>
|
||||
<td><code>class <b>CanDeactivate</b>Guard implements <b>CanDeactivate</b><T> {<br> canDeactivate(<br> component: T,<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<boolean>|Promise<boolean>|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 <b>CanActivateChild</b>Guard implements <b>CanActivateChild</b> {<br> canActivateChild(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<boolean>|Promise<boolean>|boolean { ... }<br>}<br><br>{ path: ..., canActivateChild: [CanActivateGuard],<br> children: ... }</code></td>
|
||||
<td><code>class <b>CanActivateChild</b>Guard implements <b>CanActivateChild</b> {<br> canActivateChild(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<boolean>|Promise<boolean>|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 <b>Resolve</b>Guard implements <b>Resolve</b><T> {<br> resolve(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<any>|Promise<any>|any { ... }<br>}<br><br>{ path: ..., resolve: [<b>Resolve</b>Guard] }</code></td>
|
||||
<td><code>class <b>Resolve</b>Guard implements <b>Resolve</b><T> {<br> resolve(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<any>|Promise<any>|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 <b>CanLoad</b>Guard implements <b>CanLoad</b> {<br> canLoad(<br> route: Route<br> ): Observable<boolean>|Promise<boolean>|boolean { ... }<br>}<br><br>{ path: ..., canLoad: [<b>CanLoad</b>Guard], loadChildren: ... }</code></td>
|
||||
<td><code>class <b>CanLoad</b>Guard implements <b>CanLoad</b> {<br> canLoad(<br> route: Route<br> ): Observable<boolean>|Promise<boolean>|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>
|
||||
|
@ -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());
|
||||
```
|
||||
|
@ -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_.
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
@ -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*.
|
||||
|
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
BIN
aio/content/images/bios/gerardsans.jpg
Normal file
After Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 320 KiB After Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 4.1 KiB |
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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="assets/images/icons/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="assets/images/icons/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="assets/images/icons/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="assets/images/icons/feature-icon.svg" height="70px">
|
||||
</div>
|
||||
<div class="feature-row">
|
||||
|
||||
|
@ -110,7 +110,7 @@
|
||||
|
||||
<a href="guide/quickstart">
|
||||
<div class="card">
|
||||
<img src="../assets/images/icons/code-icon.svg" height="70px">
|
||||
<img src="assets/images/icons/code-icon.svg" height="70px">
|
||||
<div class="card-text-container">
|
||||
<div class="text-headline">Get Started</div>
|
||||
<p>Start building your Angular application.</p>
|
||||
|
@ -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
|
||||
don’t 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>
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -81,7 +81,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-rc.6",
|
||||
"entities": "^1.1.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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();
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -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)
|
||||
*/
|
||||
@ -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);
|
||||
|
@ -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>'}});
|
||||
|
@ -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']);
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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) { }
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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> {
|
||||
|
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 178 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.5 KiB |
@ -17,6 +17,7 @@ aio-shell.page-docs {
|
||||
|
||||
.sidenav-content {
|
||||
min-height: 450px;
|
||||
padding: 80px 1rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,11 @@ aio-code pre {
|
||||
}
|
||||
}
|
||||
|
||||
.code-tab-group div.mat-tab-body-content {
|
||||
.code-tab-group .mat-tab-label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.code-tab-group .mat-tab-body-content {
|
||||
height: auto;
|
||||
transform: none;
|
||||
}
|
||||
|
@ -80,6 +80,35 @@ table {
|
||||
}
|
||||
}
|
||||
|
||||
#cheatsheet table tbody td {
|
||||
#cheatsheet {
|
||||
|
||||
table tbody td {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 990px) {
|
||||
|
||||
/* Force table to not be like tables anymore */
|
||||
table, thead, tbody, tfoot, tr, th, td {
|
||||
display: block;
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
|
||||
code {
|
||||
padding: 0;
|
||||
background-color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
th, td {
|
||||
&:not(:last-child) {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
19
aio/tools/RELEASE.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Docs releases
|
||||
|
||||
This document explains how to update the documentation examples after an Angular release. This is only needed for major and minor versions.
|
||||
|
||||
All the packages for the docs' examples are specified in `/aio/tools/examples/shared/package.json`
|
||||
|
||||
**1)** So within the `shared` folder, you need to issue the following command:
|
||||
|
||||
```
|
||||
$ yarn upgrade-interactive --tilde
|
||||
```
|
||||
|
||||
There, select all the packages that are updated on the new Angular release.
|
||||
|
||||
**2)** Changes to the tsconfig.json? There is one to update at `/aio/tools/examples/shared/boilerplate/src/tsconfig.json`
|
||||
|
||||
**3)** The file `/aio/tools/examples/shared/boilerplate/src/systemjs.config.web.js` contains the configuration for plunkers. It has some hardcoded versions that could be updated.
|
||||
|
||||
**4)** As in step 3, more hardcoded versions at `/aio/tools/plunker-builder/translator/rules/indexHtml.js`
|
@ -12,19 +12,19 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/animations": "~4.2.0",
|
||||
"@angular/common": "~4.2.0",
|
||||
"@angular/compiler": "~4.2.0",
|
||||
"@angular/compiler-cli": "~4.2.0",
|
||||
"@angular/core": "~4.2.0",
|
||||
"@angular/forms": "~4.2.0",
|
||||
"@angular/http": "~4.2.0",
|
||||
"@angular/platform-browser": "~4.2.0",
|
||||
"@angular/platform-browser-dynamic": "~4.2.0",
|
||||
"@angular/platform-server": "~4.2.0",
|
||||
"@angular/router": "~4.2.0",
|
||||
"@angular/tsc-wrapped": "~4.2.0",
|
||||
"@angular/upgrade": "~4.2.0",
|
||||
"@angular/animations": "~4.3.1",
|
||||
"@angular/common": "~4.3.1",
|
||||
"@angular/compiler": "~4.3.1",
|
||||
"@angular/compiler-cli": "~4.3.1",
|
||||
"@angular/core": "~4.3.1",
|
||||
"@angular/forms": "~4.3.1",
|
||||
"@angular/http": "~4.3.1",
|
||||
"@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/tsc-wrapped": "~4.3.1",
|
||||
"@angular/upgrade": "~4.3.1",
|
||||
"angular-in-memory-web-api": "~0.3.2",
|
||||
"core-js": "^2.4.1",
|
||||
"rxjs": "^5.1.0",
|
||||
|
@ -20,11 +20,7 @@ exports.config = {
|
||||
|
||||
// Capabilities to be passed to the webdriver instance.
|
||||
capabilities: {
|
||||
'browserName': 'chrome',
|
||||
// For Travis
|
||||
chromeOptions: {
|
||||
binary: process.env.CHROME_BIN
|
||||
}
|
||||
'browserName': 'chrome'
|
||||
},
|
||||
|
||||
// Framework to use. Jasmine is recommended.
|
||||
|
@ -1,10 +1,12 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
# yarn v0.25.3
|
||||
# node v7.8.0
|
||||
|
||||
|
||||
"@angular/animations@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-4.2.0.tgz#e964fc56c9621f28679f24d5e69026e2d1571425"
|
||||
"@angular/animations@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-4.3.1.tgz#1f7e0bb803efc21c608246e6765a1c647f3d1a5f"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
@ -74,79 +76,79 @@
|
||||
optionalDependencies:
|
||||
node-sass "^4.3.0"
|
||||
|
||||
"@angular/common@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.2.0.tgz#5df34718bcefc49918bfcb2683f6c19720b66a61"
|
||||
"@angular/common@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/common/-/common-4.3.1.tgz#260f487a7cdca326c436bd3ea9515c797de2ff72"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/compiler-cli@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-4.2.0.tgz#bd6f6b71f003df48a8f86184a8c16533afdf23ec"
|
||||
"@angular/compiler-cli@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-4.3.1.tgz#00b41afb6faeb4aef561b8427804ac8880aff63c"
|
||||
dependencies:
|
||||
"@angular/tsc-wrapped" "4.2.0"
|
||||
"@angular/tsc-wrapped" "4.3.1"
|
||||
minimist "^1.2.0"
|
||||
reflect-metadata "^0.1.2"
|
||||
|
||||
"@angular/compiler@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-4.2.0.tgz#a21df81995b210f822ffd70b57247d474876fbed"
|
||||
"@angular/compiler@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-4.3.1.tgz#3a24d49ecf01ac2b6e07f63e378b8ff8e257fe09"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/core@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.2.0.tgz#8bf57d01379c2a9e29476ad569dec9e20d5b17dc"
|
||||
"@angular/core@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/core/-/core-4.3.1.tgz#a9d0a7d644b96260674269b689a04feea632a8d3"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/forms@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.2.0.tgz#cb3ae69172e254452fa77578605ebc1bb72138c9"
|
||||
"@angular/forms@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-4.3.1.tgz#33914da2cb146430ff901471e682c76654622dfe"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/http@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/http/-/http-4.2.0.tgz#484af53639e04a68834c5167a1955d2d0cde8e1c"
|
||||
"@angular/http@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/http/-/http-4.3.1.tgz#e4f661f746711e88ecbea76a3c905babf97d315a"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/platform-browser-dynamic@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.2.0.tgz#b84c05616bd824e15b52b2b85c47b58b25d0158a"
|
||||
"@angular/platform-browser-dynamic@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.3.1.tgz#84034da60a82ef36e7effda7b3ade6e645b330b3"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/platform-browser@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.2.0.tgz#dfd782e7ebacba1bbe2ae0556d5d7fb012f2a6cd"
|
||||
"@angular/platform-browser@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-4.3.1.tgz#db727b06eed64bda5defec71815db26a4da2f690"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/platform-server@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-4.2.0.tgz#90add7fcd9c4f568a31058c9d93a9aca09eb4c58"
|
||||
"@angular/platform-server@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-4.3.1.tgz#3b915fc4013c6a947a8c147b4db0279b025936eb"
|
||||
dependencies:
|
||||
parse5 "^3.0.1"
|
||||
tslib "^1.7.1"
|
||||
xhr2 "^0.1.4"
|
||||
|
||||
"@angular/router@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.2.0.tgz#7cda9a23621ee41b466eced8bb4cbb62237ba6b9"
|
||||
"@angular/router@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/router/-/router-4.3.1.tgz#5219d44526156d816065841127610165a015b450"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/tsc-wrapped@4.2.0", "@angular/tsc-wrapped@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-4.2.0.tgz#e62ce9953c27ba96e4c6daf117f10e31169ccea2"
|
||||
"@angular/tsc-wrapped@4.3.1", "@angular/tsc-wrapped@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-4.3.1.tgz#f6616a4d2a3bbec1cded664fd1f526edce99ef41"
|
||||
dependencies:
|
||||
tsickle "^0.21.0"
|
||||
|
||||
"@angular/upgrade@~4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@angular/upgrade/-/upgrade-4.2.0.tgz#2766c8a8bb507146b6d38bed2c379f77bbc70335"
|
||||
"@angular/upgrade@~4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@angular/upgrade/-/upgrade-4.3.1.tgz#db80f9d428e4801b0a41618df71bf899882f39c8"
|
||||
dependencies:
|
||||
tslib "^1.7.1"
|
||||
|
||||
|
@ -20,14 +20,16 @@ module.exports = function addImageDimensions(getImageDimensions) {
|
||||
const src = props.src;
|
||||
if (!src) {
|
||||
file.message('Missing src in image tag `' + source(node, file) + '`');
|
||||
} else if (props.width === undefined && props.height === undefined) {
|
||||
} else {
|
||||
try {
|
||||
const dimensions = getImageDimensions(addImageDimensionsImpl.basePath, src);
|
||||
if (props.width === undefined && props.height === undefined) {
|
||||
props.width = '' + dimensions.width;
|
||||
props.height = '' + dimensions.height;
|
||||
}
|
||||
} catch(e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
file.message('Unable to load src in image tag `' + source(node, file) + '`');
|
||||
file.fail('Unable to load src in image tag `' + source(node, file) + '`');
|
||||
} else {
|
||||
file.fail(e.message);
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ describe('addImageDimensions post-processor', () => {
|
||||
expect(log.warn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should log a warning for images whose source cannot be loaded', () => {
|
||||
it('should fail for images whose source cannot be loaded', () => {
|
||||
getImageDimensionsSpy.and.callFake(() => {
|
||||
const error = new Error('no such file or directory');
|
||||
error.code = 'ENOENT';
|
||||
@ -68,13 +68,8 @@ describe('addImageDimensions post-processor', () => {
|
||||
docType: 'a',
|
||||
renderedContent: '<img src="missing">'
|
||||
}];
|
||||
processor.$process(docs);
|
||||
expect(() => processor.$process(docs)).toThrowError('Unable to load src in image tag `<img src="missing">` - doc (a) ');
|
||||
expect(getImageDimensionsSpy).toHaveBeenCalled();
|
||||
expect(docs).toEqual([jasmine.objectContaining({
|
||||
docType: 'a',
|
||||
renderedContent: '<img src="missing">'
|
||||
})]);
|
||||
expect(log.warn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should ignore images with width or height attributes', () => {
|
||||
@ -87,7 +82,6 @@ describe('addImageDimensions post-processor', () => {
|
||||
`
|
||||
}];
|
||||
processor.$process(docs);
|
||||
expect(getImageDimensionsSpy).not.toHaveBeenCalled();
|
||||
expect(docs).toEqual([jasmine.objectContaining({
|
||||
docType: 'a',
|
||||
renderedContent: `
|
||||
|