Compare commits

...

39 Commits

Author SHA1 Message Date
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
61 changed files with 928 additions and 431 deletions

View File

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

View File

@ -1,3 +1,24 @@
<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> <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) # [4.3.0](https://github.com/angular/angular/compare/4.3.0-rc.0...4.3.0) (2017-07-14)

View File

@ -2,7 +2,7 @@
An NgModule class describes how the application parts fit together. An NgModule class describes how the application parts fit together.
Every application has at least one NgModule, the _root_ module 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`. 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`. 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 ### 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 Among other things, the _bootstrapping_ process creates the component(s) listed in the `bootstrap` array
and inserts each one into the browser DOM. and inserts each one into the browser DOM.
@ -127,13 +127,6 @@ Which brings us to the _bootstrapping_ process itself.
{@a main} {@a main}
<l-main-section>
</l-main-section>
## Bootstrap in _main.ts_ ## Bootstrap in _main.ts_
There are many ways to bootstrap an application. There are many ways to bootstrap an application.

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) ## 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 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) ## 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"> <div id="cheatsheet">
<table class="is-full-width is-fixed-layout"> <table class="is-full-width is-fixed-layout">
@ -23,28 +23,28 @@
</th> </th>
</tr> </tr>
<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><p>Defines a module that contains components, directives, pipes, and providers.</p>
</td> </td>
</tr><tr> </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><p>List of components, directives, and pipes that belong to this module.</p>
</td> </td>
</tr><tr> </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 <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> is available to <code>declarations</code> of this module.</p>
</td> </td>
</tr><tr> </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><p>List of components, directives, and pipes visible to modules that import this module.</p>
</td> </td>
</tr><tr> </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><p>List of dependency injection providers visible both to the contents of this module and to importers of this module.</p>
</td> </td>
</tr><tr> </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><p>List of components to bootstrap when this module is bootstrapped.</p>
</td> </td>
</tr> </tr>
@ -56,61 +56,61 @@ is available to <code>declarations</code> of this module.</p>
<th></th> <th></th>
</tr> </tr>
<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><p>Binds property <code>value</code> to the result of expression <code>firstName</code>.</p>
</td> </td>
</tr><tr> </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><p>Binds attribute <code>role</code> to the result of expression <code>myAriaRole</code>.</p>
</td> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><p>Binds style property <code>width</code> to the result of expression <code>mySize</code> in pixels. Units are optional.</p>
</td> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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: <td><p>Binds a property to an interpolated string, for example, "Hello Seabiscuit". Equivalent to:
<code>&lt;div [title]="'Hello ' + ponyName"&gt;</code></p> <code>&lt;div [title]="'Hello ' + ponyName"&gt;</code></p>
</td> </td>
</tr><tr> </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><p>Binds text content to an interpolated string, for example, "Hello Seabiscuit".</p>
</td> </td>
</tr><tr> </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><p>Sets up two-way data binding. Equivalent to: <code>&lt;my-cmp [title]="name" (titleChange)="name=$event"&gt;</code></p>
</td> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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: <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> <code>&lt;ng-template [myUnless]="myExpression"&gt;&lt;p&gt;...&lt;/p&gt;&lt;/ng-template&gt;</code></p>
</td> </td>
</tr><tr> </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><p>Transforms the current value of expression <code>cardNumber</code> via the pipe called <code>myCardNumberFormatter</code>.</p>
</td> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><p>An <code>&lt;svg&gt;</code> root element is detected as an SVG element automatically, without the prefix.</p>
</td> </td>
</tr> </tr>
@ -124,19 +124,19 @@ is available to <code>declarations</code> of this module.</p>
</th> </th>
</tr> </tr>
<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><p>Removes or recreates a portion of the DOM tree based on the <code>showSection</code> expression.</p>
</td> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><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> </td>
</tr> </tr>
@ -150,7 +150,7 @@ is available to <code>declarations</code> of this module.</p>
</th> </th>
</tr> </tr>
<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><p>Provides two-way data-binding, parsing, and validation for form controls.</p>
</td> </td>
</tr> </tr>
@ -164,19 +164,19 @@ is available to <code>declarations</code> of this module.</p>
</th> </th>
</tr> </tr>
<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><p>Declares that a class is a component and provides metadata about the component.</p>
</td> </td>
</tr><tr> </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><p>Declares that a class is a directive and provides metadata about the directive.</p>
</td> </td>
</tr><tr> </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><p>Declares that a class is a pipe and provides metadata about the pipe.</p>
</td> </td>
</tr><tr> </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. <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> </p>
</td> </td>
@ -191,13 +191,13 @@ is available to <code>declarations</code> of this module.</p>
</th> </th>
</tr> </tr>
<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>, <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> <code>[attribute]</code>, <code>.class</code>, and <code>:not()</code>.</p>
<p>Does not support parent-child relationship selectors.</p> <p>Does not support parent-child relationship selectors.</p>
</td> </td>
</tr><tr> </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><p>List of dependency injection providers for this directive and its children.</p>
</td> </td>
</tr> </tr>
@ -212,19 +212,19 @@ so the <code>@Directive</code> configuration applies to components as well</p>
</th> </th>
</tr> </tr>
<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><p>If set, the <code>templateUrl</code> and <code>styleUrl</code> are resolved relative to the component.</p>
</td> </td>
</tr><tr> </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><p>List of dependency injection providers scoped to this component's view.</p>
</td> </td>
</tr><tr> </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><p>Inline template or external template URL of the component's view.</p>
</td> </td>
</tr><tr> </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><p>List of inline CSS styles or external stylesheet URLs for styling the components view.</p>
</td> </td>
</tr> </tr>
@ -238,36 +238,36 @@ so the <code>@Directive</code> configuration applies to components as well</p>
</th> </th>
</tr> </tr>
<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: <td><p>Declares an input property that you can update via property binding (example:
<code>&lt;my-cmp [myProperty]="someExpression"&gt;</code>).</p> <code>&lt;my-cmp [myProperty]="someExpression"&gt;</code>).</p>
</td> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><p>Binds a host element property (here, the CSS class <code>valid</code>) to a directive/component property (<code>isValid</code>).</p>
</td> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><p>Binds the first result of the component content query (<code>myPredicate</code>) to a property (<code>myChildComponent</code>) of the class.</p>
</td> </td>
</tr><tr> </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><p>Binds the results of the component content query (<code>myPredicate</code>) to a property (<code>myChildComponents</code>) of the class.</p>
</td> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><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> </td>
</tr> </tr>
@ -281,39 +281,39 @@ so the <code>@Directive</code> configuration applies to components as well</p>
</th> </th>
</tr> </tr>
<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><p>Called before any other lifecycle hook. Use it to inject dependencies, but avoid any serious work here.</p>
</td> </td>
</tr><tr> </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><p>Called after every change to input properties and before processing content or child views.</p>
</td> </td>
</tr><tr> </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><p>Called after the constructor, initializing input properties, and the first call to <code>ngOnChanges</code>.</p>
</td> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><p>Called after <code>ngOnInit</code> when the component's or directive's content has been initialized.</p>
</td> </td>
</tr><tr> </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><p>Called after every check of the component's or directive's content.</p>
</td> </td>
</tr><tr> </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><p>Called after <code>ngAfterContentInit</code> when the component's view has been initialized. Applies to components only.</p>
</td> </td>
</tr><tr> </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><p>Called after every check of the component's view. Applies to components only.</p>
</td> </td>
</tr><tr> </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><p>Called once, before the instance is destroyed.</p>
</td> </td>
</tr> </tr>
@ -325,15 +325,15 @@ so the <code>@Directive</code> configuration applies to components as well</p>
<th></th> <th></th>
</tr> </tr>
<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><p>Sets or overrides the provider for <code>MyService</code> to the <code>MyMockService</code> class.</p>
</td> </td>
</tr><tr> </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><p>Sets or overrides the provider for <code>MyService</code> to the <code>myFactory</code> factory function.</p>
</td> </td>
</tr><tr> </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><p>Sets or overrides the provider for <code>MyValue</code> to the value <code>41</code>.</p>
</td> </td>
</tr> </tr>
@ -347,39 +347,39 @@ so the <code>@Directive</code> configuration applies to components as well</p>
</th> </th>
</tr> </tr>
<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><p>Configures routes for the application. Supports static, parameterized, redirect, and wildcard routes. Also supports custom route data and resolve.</p>
</td> </td>
</tr><tr> </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><p>Marks the location to load the component of the active route.</p>
</td> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><p>The provided classes are added to the element when the <code>routerLink</code> becomes the current active route.</p>
</td> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><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> </td>
</tr><tr> </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><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> </td>
</tr> </tr>

View File

@ -50,7 +50,10 @@ The `get()` method on `HttpClient` makes accessing this data straightforward.
```javascript ```javascript
@Component(...) @Component(...)
export class MyComponent implements NgOnInit { export class MyComponent implements OnInit {
results: string[];
// Inject HttpClient into your component or service. // Inject HttpClient into your component or service.
constructor(private http: HttpClient) {} constructor(private http: HttpClient) {}
@ -268,12 +271,12 @@ has a single `intercept()` method. Here is a simple interceptor which does nothi
```javascript ```javascript
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest) from '@angular/common/http'; import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';
@Injectable() @Injectable()
export class NoopInterceptor implements HttpInterceptor { export class NoopInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(Req); return next.handle(req);
} }
} }
``` ```
@ -349,7 +352,7 @@ A common use of interceptors is to set default headers on outgoing responses. Fo
```javascript ```javascript
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest) from '@angular/common/http'; import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';
@Injectable() @Injectable()
export class AuthInterceptor implements HttpInterceptor { export class AuthInterceptor implements HttpInterceptor {
@ -357,7 +360,7 @@ export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Get the auth header from the service. // 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. // Clone the request to add the new header.
const authReq = req.clone({headers: req.headers.set('Authorization', authHeader)}); const authReq = req.clone({headers: req.headers.set('Authorization', authHeader)});
// Pass on the cloned request instead of the original request. // Pass on the cloned request instead of the original request.
@ -389,12 +392,12 @@ export class TimingInterceptor implements HttpInterceptor {
constructor(private auth: AuthService) {} constructor(private auth: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const elapsed = Date.now(); const started = Date.now();
return next return next
.handle(req) .handle(req)
.do(event => { .do(event => {
if (event instanceof HttpResponse) { if (event instanceof HttpResponse) {
const time = Date.now() - started; const elapsed = Date.now() - started;
console.log(`Request for ${req.urlWithParams} took ${elapsed} ms.`); 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 // At this point, the request is pending, and no response has been
// sent. The next step is to expect that the request happened. // 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, // If no request with that URL was made, or if multiple requests match,
// expectOne() would throw. However this test makes only one request to // 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'}); req.flush({name: 'Test Data'});
// Finally, assert that there are no outstanding requests. // 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 ```javascript
afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => { 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: 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 ```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. 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 ```javascript
// Expect that 5 pings have been made and flush them. // 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); expect(reqs.length).toBe(5);
reqs.forEach(req => req.flush()); 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. 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 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) [Tour of Heroes Tutorial](tutorial/toh-pt4#oninit) guide shows how.
guides show how.
Remember also that a directive's data-bound input properties are not set until _after construction_. Remember also that a directive's data-bound input properties are not set until _after construction_.

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 ***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. without requiring an actual server or real HTTP calls.
Good for demos, samples, and early stage development (before you even have a server). 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. ***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*. Some of the samples improve their appearance with *bootstrap*.

View File

@ -255,11 +255,11 @@ During each navigation, the `Router` emits navigation events through the `Router
<tr> <tr>
<td> <td>
<code>RouteConfigLoadStart</code> <code>RouteConfigLoadEnd</code>
</td> </td>
<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> </td>
</tr> </tr>

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} {@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") [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. examine an instance of a class all by itself without any dependence on Angular or any injected values.

View File

@ -247,12 +247,10 @@ next to the original _ES5_ version for comparison:
</code-pane> </code-pane>
</code-tabs> </code-tabs>
{@a name-constructor}
<div class="callout is-helpful"> <div class="callout is-helpful">
{@a name-constructor}
### Name the constructor
A **named** constructor displays clearly in the console log A **named** constructor displays clearly in the console log
if the component throws a runtime error. if the component throws a runtime error.
An **unnamed** constructor displays as an anonymous function, for example, `class0`, An **unnamed** constructor displays as an anonymous function, for example, `class0`,

View File

@ -8,7 +8,7 @@
<div class="feature-section"> <div class="feature-section">
<div class="feature-header"> <div class="feature-header">
<div class="text-headline">Cross Platform</div> <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>
<div class="feature-row"> <div class="feature-row">
@ -34,7 +34,7 @@
<div class="feature-section"> <div class="feature-section">
<div class="feature-header"> <div class="feature-header">
<div class="text-headline">Speed and Performance</div> <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>
<div class="feature-row"> <div class="feature-row">
@ -59,7 +59,7 @@
<div class="feature-section"> <div class="feature-section">
<div class="feature-header"> <div class="feature-header">
<div class="text-headline">Productivity</div> <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>
<div class="feature-row"> <div class="feature-row">
@ -84,7 +84,7 @@
<div class="feature-section"> <div class="feature-section">
<div class="feature-header"> <div class="feature-header">
<div class="text-headline">Full Development Story</div> <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>
<div class="feature-row"> <div class="feature-row">

View File

@ -110,7 +110,7 @@
<a href="guide/quickstart"> <a href="guide/quickstart">
<div class="card"> <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="card-text-container">
<div class="text-headline">Get Started</div> <div class="text-headline">Get Started</div>
<p>Start building your Angular application.</p> <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

@ -83,11 +83,7 @@ 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 Don't worry about the details of this backend substitution; you can
skip it when you have a real web API server. skip it when you have a real web API server.
Read more about the in-memory web API in the div>
[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 ## Heroes and HTTP

View File

@ -81,7 +81,7 @@
"concurrently": "^3.4.0", "concurrently": "^3.4.0",
"cross-spawn": "^5.1.0", "cross-spawn": "^5.1.0",
"dgeni": "^0.4.7", "dgeni": "^0.4.7",
"dgeni-packages": "^0.20.0-rc.5", "dgeni-packages": "^0.20.0-rc.6",
"entities": "^1.1.1", "entities": "^1.1.1",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-plugin-jasmine": "^2.2.0", "eslint-plugin-jasmine": "^2.2.0",

View File

@ -45,15 +45,20 @@ timestamp=$(date +%s)
payloadData="$payloadData\"timestamp\": $timestamp, " payloadData="$payloadData\"timestamp\": $timestamp, "
# Add change source: application, dependencies, or 'application+dependencies' # 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) yarnChanged=false
dependencyChanges=$(git diff --name-only $TRAVIS_COMMIT_RANGE ${parentDir}/yarn.lock | wc -l) allChangedFiles=$(git diff --name-only $TRAVIS_COMMIT_RANGE $parentDir | wc -l)
allChangedFileNames=$(git diff --name-only $TRAVIS_COMMIT_RANGE $parentDir)
if [[ $dependencyChanges -eq 1 ]] && [[ $applicationChanges -eq 0 ]]; then if [[ $allChangedFileNames == *"yarn.lock"* ]]; then
yarnChanged=true
fi
if [[ $allChangedFiles -eq 1 ]] && [[ "$yarnChanged" = true ]]; then
# only yarn.lock changed # only yarn.lock changed
change='dependencies' change='dependencies'
elif [[ $dependencyChanges -eq 1 ]] && [[ $applicationChanges -gt 0 ]]; then elif [[ $allChangedFiles -gt 1 ]] && [[ "$yarnChanged" = true ]]; then
change='application+dependencies' change='application+dependencies'
elif [[ $applicationChanges -gt 0 ]]; then elif [[ $allChangedFiles -gt 0 ]]; then
change='application' change='application'
else else
# Nothing changed in aio/ # Nothing changed in aio/

View File

@ -48,7 +48,7 @@ export class AppComponent implements OnInit {
* the styling of individual pages. * the styling of individual pages.
* You will get three classes: * 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) * * `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) * * `view-...`: computef from the navigation view (e.g. SideNav, TopBar, etc)
*/ */

View File

@ -65,6 +65,13 @@ describe('CodeExampleComponent', () => {
expect(actual).toBe('Great Example'); 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', () => { it('should pass hideCopy to CodeComonent', () => {
TestBed.overrideComponent(HostComponent, { TestBed.overrideComponent(HostComponent, {
set: {template: '<code-example hideCopy="true"></code-example>'}}); 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.path = element.getAttribute('path') || '';
this.region = element.getAttribute('region') || ''; this.region = element.getAttribute('region') || '';
this.title = element.getAttribute('title') || ''; 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.isAvoid = this.path.indexOf('.avoid.') !== -1;
this.hideCopy = this.isAvoid || getBoolFromAttribute(element, ['hidecopy', 'hide-copy']); this.hideCopy = this.isAvoid || getBoolFromAttribute(element, ['hidecopy', 'hide-copy']);

View File

@ -17,6 +17,7 @@ aio-shell.page-docs {
.sidenav-content { .sidenav-content {
min-height: 450px; min-height: 450px;
padding: 80px 1rem 1rem;
} }
} }

View File

@ -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; height: auto;
transform: none; transform: none;
} }

View File

@ -80,6 +80,35 @@ table {
} }
} }
#cheatsheet table tbody td { #cheatsheet {
overflow: auto;
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;
}
}
}
} }

View File

@ -20,14 +20,16 @@ module.exports = function addImageDimensions(getImageDimensions) {
const src = props.src; const src = props.src;
if (!src) { if (!src) {
file.message('Missing src in image tag `' + source(node, file) + '`'); file.message('Missing src in image tag `' + source(node, file) + '`');
} else if (props.width === undefined && props.height === undefined) { } else {
try { try {
const dimensions = getImageDimensions(addImageDimensionsImpl.basePath, src); const dimensions = getImageDimensions(addImageDimensionsImpl.basePath, src);
props.width = '' + dimensions.width; if (props.width === undefined && props.height === undefined) {
props.height = '' + dimensions.height; props.width = '' + dimensions.width;
props.height = '' + dimensions.height;
}
} catch(e) { } catch(e) {
if (e.code === 'ENOENT') { 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 { } else {
file.fail(e.message); file.fail(e.message);
} }

View File

@ -58,7 +58,7 @@ describe('addImageDimensions post-processor', () => {
expect(log.warn).toHaveBeenCalled(); 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(() => { getImageDimensionsSpy.and.callFake(() => {
const error = new Error('no such file or directory'); const error = new Error('no such file or directory');
error.code = 'ENOENT'; error.code = 'ENOENT';
@ -68,13 +68,8 @@ describe('addImageDimensions post-processor', () => {
docType: 'a', docType: 'a',
renderedContent: '<img src="missing">' 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(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', () => { it('should ignore images with width or height attributes', () => {
@ -87,7 +82,6 @@ describe('addImageDimensions post-processor', () => {
` `
}]; }];
processor.$process(docs); processor.$process(docs);
expect(getImageDimensionsSpy).not.toHaveBeenCalled();
expect(docs).toEqual([jasmine.objectContaining({ expect(docs).toEqual([jasmine.objectContaining({
docType: 'a', docType: 'a',
renderedContent: ` renderedContent: `

View File

@ -47,4 +47,5 @@ module.exports = new Package('angular.io', [gitPackage, apiPackage, contentPacka
} }
}); });
checkAnchorLinksProcessor.pathVariants = ['', '/', '.html', '/index.html', '#top-of-page']; checkAnchorLinksProcessor.pathVariants = ['', '/', '.html', '/index.html', '#top-of-page'];
checkAnchorLinksProcessor.errorOnUnmatchedLinks = true;
}); });

View File

@ -31,8 +31,14 @@ describe('example inline-tag-def', function() {
}; };
}); });
it('should return a <code-example> tag', () => { it('should throw an error if there is no matching example', () => {
expect(handler({}, 'example', 'some/uri')).toEqual('<code-example>\n\n</code-example>'); expect(function() {
handler({}, 'example', 'missing/uri');
}).toThrowError();
expect(function() {
handler({}, 'example', 'test/url missing-region');
}).toThrowError();
}); });
it('should contain the whole contents from the example file if no region is specified', () => { it('should contain the whole contents from the example file if no region is specified', () => {

View File

@ -1,4 +1,4 @@
module.exports = function getExampleRegion(exampleMap, createDocMessage, log, collectExamples) { module.exports = function getExampleRegion(exampleMap, createDocMessage, collectExamples) {
return function getExampleRegionImpl(doc, relativePath, regionName) { return function getExampleRegionImpl(doc, relativePath, regionName) {
const EXAMPLES_FOLDERS = collectExamples.exampleFolders; const EXAMPLES_FOLDERS = collectExamples.exampleFolders;
@ -14,16 +14,16 @@ module.exports = function getExampleRegion(exampleMap, createDocMessage, log, co
// If still no file then we error // If still no file then we error
if (!exampleFile) { if (!exampleFile) {
log.error(createDocMessage('Missing example file... relativePath: "' + relativePath + '".', doc)); const message = createDocMessage('Missing example file... relativePath: "' + relativePath + '".', doc) + '\n' +
log.error('Example files can be found in: ' + EXAMPLES_FOLDERS.join(', ')); 'Example files can be found in: ' + EXAMPLES_FOLDERS.join(', ');
return ''; throw new Error(message);
} }
var sourceCodeDoc = exampleFile.regions[regionName || '']; var sourceCodeDoc = exampleFile.regions[regionName || ''];
if (!sourceCodeDoc) { if (!sourceCodeDoc) {
log.error(createDocMessage('Missing example region... relativePath: "' + relativePath + '", region: "' + regionName + '".', doc)); const message = createDocMessage('Missing example region... relativePath: "' + relativePath + '", region: "' + regionName + '".', doc) + '\n' +
log.error('Regions available are:', Object.keys[exampleFile.regions]); 'Regions available are:' + Object.keys[exampleFile.regions];
return ''; throw new Error(message);
} }
return sourceCodeDoc.renderedContent; return sourceCodeDoc.renderedContent;

View File

@ -26,4 +26,13 @@ describe('getExampleRegion', () => {
it('should contain the region contents from the example file if a region is specified', () => { it('should contain the region contents from the example file if a region is specified', () => {
expect(getExampleRegion({}, 'test/url', 'region-1')).toEqual('region 1 contents'); expect(getExampleRegion({}, 'test/url', 'region-1')).toEqual('region 1 contents');
}); });
it('should throw an error if an example doesn\'t exist', function() {
expect(function() {
getExampleRegion({}, 'missing/file', 'region-1');
}).toThrowError();
expect(function() {
getExampleRegion({}, 'test/url', 'missing-region');
}).toThrowError();
});
}); });

View File

@ -3,7 +3,7 @@
{% block overview %} {% block overview %}
<code-example language="ts" hideCopy="true" class="no-box api-heading"> <code-example language="ts" hideCopy="true" class="no-box api-heading">
function {$ doc.name $}{$ params.paramList(doc.parameters) $} function {$ doc.name $}{$ doc.typeParameters | escape $}{$ params.paramList(doc.parameters) $}
{%- if doc.type %}: {$ doc.type | escape $}{% endif %}; {%- if doc.type %}: {$ doc.type | escape $}{% endif %};
</code-example> </code-example>
{% endblock %} {% endblock %}
@ -13,7 +13,7 @@ function {$ doc.name $}{$ params.paramList(doc.parameters) $}
{% if doc.overloads.length %} {% if doc.overloads.length %}
<h2>Overloads</h2>{% for overload in doc.overloads %} <h2>Overloads</h2>{% for overload in doc.overloads %}
<code-example language="ts" hideCopy="true" class="no-box api-heading"> <code-example language="ts" hideCopy="true" class="no-box api-heading">
function {$ overload.name $}{$ params.paramList(overload.parameters) $} function {$ overload.name $}{$ doc.typeParameters | escape $}{$ params.paramList(overload.parameters) $}
{%- if overload.type %}: {$ overload.type | escape $}{% endif %}; {%- if overload.type %}: {$ overload.type | escape $}{% endif %};
</code-example> </code-example>
<section class="description"> <section class="description">

View File

@ -11,7 +11,7 @@
</tr> </tr>
<tr> <tr>
<th>Module</th> <th>Module</th>
<td><code>import { {$ doc.name $} } from <a href="{$ doc.moduleDoc.path $}">@angular/{$ doc.moduleDoc.id $}</a>;</code></td> <td><code>import { {$ doc.name $} } from <a href="{$ doc.moduleDoc.path $}">'@angular/{$ doc.moduleDoc.id $}'</a>;</code></td>
</tr> </tr>
<tr> <tr>
<th>Source</th> <th>Source</th>

View File

@ -1904,9 +1904,9 @@ devtools-timeline-model@1.1.6:
chrome-devtools-frontend "1.0.401423" chrome-devtools-frontend "1.0.401423"
resolve "1.1.7" resolve "1.1.7"
dgeni-packages@^0.20.0-rc.5: dgeni-packages@^0.20.0-rc.6:
version "0.20.0-rc.5" version "0.20.0-rc.6"
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.20.0-rc.5.tgz#09dd8134a3d79595578d6c0192ec0d82c78354be" resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.20.0-rc.6.tgz#d615e0631305dcf091386c802d0e424ef86206d2"
dependencies: dependencies:
canonical-path "0.0.2" canonical-path "0.0.2"
catharsis "^0.8.1" catharsis "^0.8.1"

View File

@ -1,6 +1,6 @@
{ {
"name": "angular-srcs", "name": "angular-srcs",
"version": "4.3.0", "version": "4.3.1",
"private": true, "private": true,
"branchPattern": "2.0.*", "branchPattern": "2.0.*",
"description": "Angular - a web framework for modern web apps", "description": "Angular - a web framework for modern web apps",

View File

@ -67,20 +67,17 @@ export class AnimationEngine {
this._transitionEngine.removeNode(namespaceId, element, context); this._transitionEngine.removeNode(namespaceId, element, context);
} }
process(namespaceId: string, element: any, property: string, value: any): boolean { disableAnimations(element: any, disable: boolean) {
switch (property.charAt(0)) { this._transitionEngine.markElementAsDisabled(element, disable);
case '.': }
if (property == '.disabled') {
this._transitionEngine.markElementAsDisabled(element, !!value); process(namespaceId: string, element: any, property: string, value: any) {
} if (property.charAt(0) == '@') {
return false; const [id, action] = parseTimelineCommand(property);
case '@': const args = value as any[];
const [id, action] = parseTimelineCommand(property); this._timelineEngine.command(id, element, action, args);
const args = value as any[]; } else {
this._timelineEngine.command(id, element, action, args); this._transitionEngine.trigger(namespaceId, element, property, value);
return false;
default:
return this._transitionEngine.trigger(namespaceId, element, property, value);
} }
} }

View File

@ -36,13 +36,22 @@ export function normalizeKeyframes(
Object.keys(kf).forEach(prop => { Object.keys(kf).forEach(prop => {
let normalizedProp = prop; let normalizedProp = prop;
let normalizedValue = kf[prop]; let normalizedValue = kf[prop];
if (normalizedValue == PRE_STYLE) { if (prop !== 'offset') {
normalizedValue = preStyles[prop]; normalizedProp = normalizer.normalizePropertyName(normalizedProp, errors);
} else if (normalizedValue == AUTO_STYLE) { switch (normalizedValue) {
normalizedValue = postStyles[prop]; case PRE_STYLE:
} else if (prop != 'offset') { normalizedValue = preStyles[prop];
normalizedProp = normalizer.normalizePropertyName(prop, errors); break;
normalizedValue = normalizer.normalizeStyleValue(prop, normalizedProp, kf[prop], errors);
case AUTO_STYLE:
normalizedValue = postStyles[prop];
break;
default:
normalizedValue =
normalizer.normalizeStyleValue(prop, normalizedProp, normalizedValue, errors);
break;
}
} }
normalizedKeyframe[normalizedProp] = normalizedValue; normalizedKeyframe[normalizedProp] = normalizedValue;
}); });

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be * Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AUTO_STYLE, AnimationOptions, AnimationPlayer, NoopAnimationPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations'; import {AUTO_STYLE, AnimationOptions, AnimationPlayer, NoopAnimationPlayer, ɵAnimationGroupPlayer as AnimationGroupPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations';
import {AnimationTimelineInstruction} from '../dsl/animation_timeline_instruction'; import {AnimationTimelineInstruction} from '../dsl/animation_timeline_instruction';
import {AnimationTransitionFactory} from '../dsl/animation_transition_factory'; import {AnimationTransitionFactory} from '../dsl/animation_transition_factory';
@ -1168,8 +1168,8 @@ export class TransitionAnimationEngine {
if (details && details.removedBeforeQueried) return new NoopAnimationPlayer(); if (details && details.removedBeforeQueried) return new NoopAnimationPlayer();
const isQueriedElement = element !== rootElement; const isQueriedElement = element !== rootElement;
const previousPlayers = const previousPlayers = flattenGroupPlayers(
(allPreviousPlayersMap.get(element) || EMPTY_PLAYER_ARRAY).map(p => p.getRealPlayer()); (allPreviousPlayersMap.get(element) || EMPTY_PLAYER_ARRAY).map(p => p.getRealPlayer()));
const preStyles = preStylesMap.get(element); const preStyles = preStylesMap.get(element);
const postStyles = postStylesMap.get(element); const postStyles = postStylesMap.get(element);
@ -1464,3 +1464,20 @@ function removeNodesAfterAnimationDone(
engine: TransitionAnimationEngine, element: any, players: AnimationPlayer[]) { engine: TransitionAnimationEngine, element: any, players: AnimationPlayer[]) {
optimizeGroupPlayer(players).onDone(() => engine.processLeaveNode(element)); optimizeGroupPlayer(players).onDone(() => engine.processLeaveNode(element));
} }
function flattenGroupPlayers(players: AnimationPlayer[]): AnimationPlayer[] {
const finalPlayers: AnimationPlayer[] = [];
_flattenGroupPlayersRecur(players, finalPlayers);
return finalPlayers;
}
function _flattenGroupPlayersRecur(players: AnimationPlayer[], finalPlayers: AnimationPlayer[]) {
for (let i = 0; i < players.length; i++) {
const player = players[i];
if (player instanceof AnimationGroupPlayer) {
_flattenGroupPlayersRecur(player.players, finalPlayers);
} else {
finalPlayers.push(player as AnimationPlayer);
}
}
}

View File

@ -132,4 +132,12 @@ export class AnimationGroupPlayer implements AnimationPlayer {
} }
get players(): AnimationPlayer[] { return this._players; } get players(): AnimationPlayer[] { return this._players; }
beforeDestroy(): void {
this.players.forEach(player => {
if (player.beforeDestroy) {
player.beforeDestroy();
}
});
}
} }

View File

@ -12,7 +12,7 @@ import 'rxjs/add/operator/toPromise';
import {ddescribe, describe, iit, it} from '@angular/core/testing/src/testing_internal'; import {ddescribe, describe, iit, it} from '@angular/core/testing/src/testing_internal';
import {HttpClient} from '../src/client'; import {HttpClient} from '../src/client';
import {HttpEventType, HttpResponse} from '../src/response'; import {HttpErrorResponse, HttpEventType, HttpResponse} from '../src/response';
import {HttpClientTestingBackend} from '../testing/src/backend'; import {HttpClientTestingBackend} from '../testing/src/backend';
export function main() { export function main() {
@ -123,5 +123,15 @@ export function main() {
.flush('hello world'); .flush('hello world');
}); });
}); });
describe('makes a request for an error response', () => {
it('with a JSON body', (done: DoneFn) => {
client.get('/test').subscribe(() => {}, (res: HttpErrorResponse) => {
expect(res.error.data).toEqual('hello world');
done();
});
backend.expectOne('/test').flush(
{'data': 'hello world'}, {status: 500, statusText: 'Server error'});
});
});
}); });
} }

View File

@ -61,12 +61,11 @@ export class TestRequest {
if (statusText === undefined) { if (statusText === undefined) {
throw new Error('statusText is required when setting a custom status.'); throw new Error('statusText is required when setting a custom status.');
} }
const res = {body, headers, status, statusText, url};
if (status >= 200 && status < 300) { if (status >= 200 && status < 300) {
this.observer.next(new HttpResponse<any>(res)); this.observer.next(new HttpResponse<any>({body, headers, status, statusText, url}));
this.observer.complete(); this.observer.complete();
} else { } else {
this.observer.error(new HttpErrorResponse(res)); this.observer.error(new HttpErrorResponse({error: body, headers, status, statusText, url}));
} }
} }

View File

@ -47,7 +47,6 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<body> <body>
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html"> <trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
<source>translate me</source> <source>translate me</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/basic.ts</context> <context context-type="sourcefile">src/basic.ts</context>
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
@ -57,7 +56,6 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
</trans-unit> </trans-unit>
<trans-unit id="65cc4ab3b4c438e07c89be2b677d08369fb62da2" datatype="html"> <trans-unit id="65cc4ab3b4c438e07c89be2b677d08369fb62da2" datatype="html">
<source>Welcome</source> <source>Welcome</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/basic.ts</context> <context context-type="sourcefile">src/basic.ts</context>
<context context-type="linenumber">5</context> <context context-type="linenumber">5</context>
@ -70,7 +68,6 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<trans-unit id="b0a17f08a4bd742b2acf39780c257c2f519d33ed" datatype="html"> <trans-unit id="b0a17f08a4bd742b2acf39780c257c2f519d33ed" datatype="html">
<source>other-3rdP-component <source>other-3rdP-component
multi-lines</source> multi-lines</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">node_modules/third_party/other_comp.d.ts</context> <context context-type="sourcefile">node_modules/third_party/other_comp.d.ts</context>
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>

View File

@ -9,7 +9,7 @@
"ng-xi18n": "./src/extract_i18n.js" "ng-xi18n": "./src/extract_i18n.js"
}, },
"dependencies": { "dependencies": {
"@angular/tsc-wrapped": "4.3.0", "@angular/tsc-wrapped": "4.3.1",
"reflect-metadata": "^0.1.2", "reflect-metadata": "^0.1.2",
"minimist": "^1.2.0" "minimist": "^1.2.0"
}, },

View File

@ -53,7 +53,7 @@ export class Xliff extends Serializer {
const transUnit = new xml.Tag(_UNIT_TAG, {id: message.id, datatype: 'html'}); const transUnit = new xml.Tag(_UNIT_TAG, {id: message.id, datatype: 'html'});
transUnit.children.push( transUnit.children.push(
new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)), new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)),
new xml.CR(8), new xml.Tag(_TARGET_TAG), ...contextTags); ...contextTags);
if (message.description) { if (message.description) {
transUnit.children.push( transUnit.children.push(

View File

@ -43,7 +43,7 @@ export function jitStatements(sourceUrl: string, statements: o.Statement[]): {[k
return evalExpression(sourceUrl, ctx, converter.getArgs()); return evalExpression(sourceUrl, ctx, converter.getArgs());
} }
class JitEmitterVisitor extends AbstractJsEmitterVisitor { export class JitEmitterVisitor extends AbstractJsEmitterVisitor {
private _evalArgNames: string[] = []; private _evalArgNames: string[] = [];
private _evalArgValues: any[] = []; private _evalArgValues: any[] = [];
private _evalExportedVars: string[] = []; private _evalExportedVars: string[] = [];
@ -69,7 +69,7 @@ class JitEmitterVisitor extends AbstractJsEmitterVisitor {
id = this._evalArgValues.length; id = this._evalArgValues.length;
this._evalArgValues.push(value); this._evalArgValues.push(value);
const name = identifierName({reference: ast.value.runtime}) || 'val'; const name = identifierName({reference: ast.value.runtime}) || 'val';
this._evalArgNames.push(`jit_${name}${id}`); this._evalArgNames.push(`jit_${name}_${id}`);
} }
ctx.print(ast, this._evalArgNames[id]); ctx.print(ast, this._evalArgNames[id]);
return null; return null;

View File

@ -162,7 +162,6 @@ const XLIFF_TOMERGE = `
const XLIFF_EXTRACTED = ` const XLIFF_EXTRACTED = `
<trans-unit id="3cb04208df1c2f62553ed48e75939cf7107f9dad" datatype="html"> <trans-unit id="3cb04208df1c2f62553ed48e75939cf7107f9dad" datatype="html">
<source>i18n attribute on tags</source> <source>i18n attribute on tags</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">3</context> <context context-type="linenumber">3</context>
@ -170,7 +169,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="52895b1221effb3f3585b689f049d2784d714952" datatype="html"> <trans-unit id="52895b1221effb3f3585b689f049d2784d714952" datatype="html">
<source>nested</source> <source>nested</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">5</context> <context context-type="linenumber">5</context>
@ -178,7 +176,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="88d5f22050a9df477ee5646153558b3a4862d47e" datatype="html"> <trans-unit id="88d5f22050a9df477ee5646153558b3a4862d47e" datatype="html">
<source>nested</source> <source>nested</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">7</context> <context context-type="linenumber">7</context>
@ -187,7 +184,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="34fec9cc62e28e8aa6ffb306fa8569ef0a8087fe" datatype="html"> <trans-unit id="34fec9cc62e28e8aa6ffb306fa8569ef0a8087fe" datatype="html">
<source><x id="START_ITALIC_TEXT" ctype="x-i"/>with placeholders<x id="CLOSE_ITALIC_TEXT" ctype="x-i"/></source> <source><x id="START_ITALIC_TEXT" ctype="x-i"/>with placeholders<x id="CLOSE_ITALIC_TEXT" ctype="x-i"/></source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">9</context> <context context-type="linenumber">9</context>
@ -199,7 +195,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="1fe4616cce80a57c7707bac1c97054aa8e244a67" datatype="html"> <trans-unit id="1fe4616cce80a57c7707bac1c97054aa8e244a67" datatype="html">
<source>on not translatable node</source> <source>on not translatable node</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">13</context> <context context-type="linenumber">13</context>
@ -207,7 +202,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="67162b5af5f15fd0eb6480c88688dafdf952b93a" datatype="html"> <trans-unit id="67162b5af5f15fd0eb6480c88688dafdf952b93a" datatype="html">
<source>on translatable node</source> <source>on translatable node</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">14</context> <context context-type="linenumber">14</context>
@ -215,7 +209,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="dc5536bb9e0e07291c185a0d306601a2ecd4813f" datatype="html"> <trans-unit id="dc5536bb9e0e07291c185a0d306601a2ecd4813f" datatype="html">
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source> <source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">19</context> <context context-type="linenumber">19</context>
@ -229,7 +222,6 @@ const XLIFF_EXTRACTED = `
<source> <source>
<x id="ICU"/> <x id="ICU"/>
</source> </source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">21</context> <context context-type="linenumber">21</context>
@ -237,7 +229,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="c0ca5e58fe954d528bbfa516007a5a11690a7e99" datatype="html"> <trans-unit id="c0ca5e58fe954d528bbfa516007a5a11690a7e99" datatype="html">
<source>{VAR_SELECT, select, m {male} f {female} }</source> <source>{VAR_SELECT, select, m {male} f {female} }</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">22</context> <context context-type="linenumber">22</context>
@ -247,7 +238,6 @@ const XLIFF_EXTRACTED = `
<source> <source>
<x id="ICU"/> <x id="ICU"/>
</source> </source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">24</context> <context context-type="linenumber">24</context>
@ -255,7 +245,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="a25cf2e21a299f30be1392e731163825233edc61" datatype="html"> <trans-unit id="a25cf2e21a299f30be1392e731163825233edc61" datatype="html">
<source>{VAR_SELECT, select, m {male} f {female} }</source> <source>{VAR_SELECT, select, m {male} f {female} }</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">25</context> <context context-type="linenumber">25</context>
@ -263,7 +252,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="d9879678f727b244bc7c7e20f22b63d98cb14890" datatype="html"> <trans-unit id="d9879678f727b244bc7c7e20f22b63d98cb14890" datatype="html">
<source><x id="INTERPOLATION"/></source> <source><x id="INTERPOLATION"/></source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">28</context> <context context-type="linenumber">28</context>
@ -271,7 +259,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="50dac33dc6fc0578884baac79d875785ed77c928" datatype="html"> <trans-unit id="50dac33dc6fc0578884baac79d875785ed77c928" datatype="html">
<source>sex = <x id="INTERPOLATION"/></source> <source>sex = <x id="INTERPOLATION"/></source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">29</context> <context context-type="linenumber">29</context>
@ -279,7 +266,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383" datatype="html"> <trans-unit id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383" datatype="html">
<source><x id="CUSTOM_NAME"/></source> <source><x id="CUSTOM_NAME"/></source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">30</context> <context context-type="linenumber">30</context>
@ -287,7 +273,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="2ec983b4893bcd5b24af33bebe3ecba63868453c" datatype="html"> <trans-unit id="2ec983b4893bcd5b24af33bebe3ecba63868453c" datatype="html">
<source>in a translatable section</source> <source>in a translatable section</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">35</context> <context context-type="linenumber">35</context>
@ -303,7 +288,6 @@ const XLIFF_EXTRACTED = `
<x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/> <x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/>
<x id="START_TAG_DIV_1" ctype="x-div"/><x id="ICU"/><x id="CLOSE_TAG_DIV" ctype="x-div"/> <x id="START_TAG_DIV_1" ctype="x-div"/><x id="ICU"/><x id="CLOSE_TAG_DIV" ctype="x-div"/>
</source> </source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">33</context> <context context-type="linenumber">33</context>
@ -311,7 +295,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="93a30c67d4e6c9b37aecfe2ac0f2b5d366d7b520" datatype="html"> <trans-unit id="93a30c67d4e6c9b37aecfe2ac0f2b5d366d7b520" datatype="html">
<source>it <x id="START_BOLD_TEXT" ctype="x-b"/>should<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> work</source> <source>it <x id="START_BOLD_TEXT" ctype="x-b"/>should<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> work</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">39</context> <context context-type="linenumber">39</context>
@ -319,7 +302,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="i18n16" datatype="html"> <trans-unit id="i18n16" datatype="html">
<source>with an explicit ID</source> <source>with an explicit ID</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">41</context> <context context-type="linenumber">41</context>
@ -327,7 +309,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="i18n17" datatype="html"> <trans-unit id="i18n17" datatype="html">
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source> <source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">42</context> <context context-type="linenumber">42</context>
@ -335,7 +316,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="2370d995bdcc1e7496baa32df20654aff65c2d10" datatype="html"> <trans-unit id="2370d995bdcc1e7496baa32df20654aff65c2d10" datatype="html">
<source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <x id="INTERPOLATION"/> results} }</source> <source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <x id="INTERPOLATION"/> results} }</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">45</context> <context context-type="linenumber">45</context>
@ -344,7 +324,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="296ab5eab8d370822488c152586db3a5875ee1a2" datatype="html"> <trans-unit id="296ab5eab8d370822488c152586db3a5875ee1a2" datatype="html">
<source>foo<x id="START_LINK" ctype="x-a"/>bar<x id="CLOSE_LINK" ctype="x-a"/></source> <source>foo<x id="START_LINK" ctype="x-a"/>bar<x id="CLOSE_LINK" ctype="x-a"/></source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">53</context> <context context-type="linenumber">53</context>
@ -352,7 +331,6 @@ const XLIFF_EXTRACTED = `
</trans-unit> </trans-unit>
<trans-unit id="2e013b311caa0916478941a985887e091d8288b6" datatype="html"> <trans-unit id="2e013b311caa0916478941a985887e091d8288b6" datatype="html">
<source><x id="MAP NAME"/></source> <source><x id="MAP NAME"/></source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">55</context> <context context-type="linenumber">55</context>

View File

@ -35,7 +35,6 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<body> <body>
<trans-unit id="983775b9a51ce14b036be72d4cfd65d68d64e231" datatype="html"> <trans-unit id="983775b9a51ce14b036be72d4cfd65d68d64e231" datatype="html">
<source>translatable attribute</source> <source>translatable attribute</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">2</context> <context context-type="linenumber">2</context>
@ -43,7 +42,6 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
</trans-unit> </trans-unit>
<trans-unit id="ec1d033f2436133c14ab038286c4f5df4697484a" datatype="html"> <trans-unit id="ec1d033f2436133c14ab038286c4f5df4697484a" datatype="html">
<source>translatable element <x id="START_BOLD_TEXT" ctype="x-b"/>with placeholders<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> <x id="INTERPOLATION"/></source> <source>translatable element <x id="START_BOLD_TEXT" ctype="x-b"/>with placeholders<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> <x id="INTERPOLATION"/></source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">3</context> <context context-type="linenumber">3</context>
@ -51,7 +49,6 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
</trans-unit> </trans-unit>
<trans-unit id="e2ccf3d131b15f54aa1fcf1314b1ca77c14bfcc2" datatype="html"> <trans-unit id="e2ccf3d131b15f54aa1fcf1314b1ca77c14bfcc2" datatype="html">
<source>{VAR_PLURAL, plural, =0 {<x id="START_PARAGRAPH" ctype="x-p"/>test<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} }</source> <source>{VAR_PLURAL, plural, =0 {<x id="START_PARAGRAPH" ctype="x-p"/>test<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} }</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">4</context> <context context-type="linenumber">4</context>
@ -59,7 +56,6 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
</trans-unit> </trans-unit>
<trans-unit id="db3e0a6a5a96481f60aec61d98c3eecddef5ac23" datatype="html"> <trans-unit id="db3e0a6a5a96481f60aec61d98c3eecddef5ac23" datatype="html">
<source>foo</source> <source>foo</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">5</context> <context context-type="linenumber">5</context>
@ -73,7 +69,6 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
</trans-unit> </trans-unit>
<trans-unit id="i" datatype="html"> <trans-unit id="i" datatype="html">
<source>foo</source> <source>foo</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">7</context> <context context-type="linenumber">7</context>
@ -83,7 +78,6 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
</trans-unit> </trans-unit>
<trans-unit id="bar" datatype="html"> <trans-unit id="bar" datatype="html">
<source>foo</source> <source>foo</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">8</context> <context context-type="linenumber">8</context>
@ -91,7 +85,6 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
</trans-unit> </trans-unit>
<trans-unit id="d7fa2d59aaedcaa5309f13028c59af8c85b8c49d" datatype="html"> <trans-unit id="d7fa2d59aaedcaa5309f13028c59af8c85b8c49d" datatype="html">
<source><x id="LINE_BREAK" ctype="lb"/><x id="TAG_IMG" ctype="image"/><x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/></source> <source><x id="LINE_BREAK" ctype="lb"/><x id="TAG_IMG" ctype="image"/><x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/></source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">9</context> <context context-type="linenumber">9</context>
@ -100,7 +93,6 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
</trans-unit> </trans-unit>
<trans-unit id="baz" datatype="html"> <trans-unit id="baz" datatype="html">
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source> <source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">10</context> <context context-type="linenumber">10</context>
@ -108,7 +100,6 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
</trans-unit> </trans-unit>
<trans-unit id="0e16a673a5a7a135c9f7b957ec2c5c6f6ee6e2c4" datatype="html"> <trans-unit id="0e16a673a5a7a135c9f7b957ec2c5c6f6ee6e2c4" datatype="html">
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source> <source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">11</context> <context context-type="linenumber">11</context>
@ -117,7 +108,6 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<trans-unit id="fcfa109b0e152d4c217dbc02530be0bcb8123ad1" datatype="html"> <trans-unit id="fcfa109b0e152d4c217dbc02530be0bcb8123ad1" datatype="html">
<source>multi <source>multi
lines</source> lines</source>
<target/>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">file.ts</context> <context context-type="sourcefile">file.ts</context>
<context context-type="linenumber">12</context> <context context-type="linenumber">12</context>

View File

@ -0,0 +1,35 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {EmitterVisitorContext} from '@angular/compiler/src/output/abstract_emitter';
import * as o from '@angular/compiler/src/output/output_ast';
import {JitEmitterVisitor} from '@angular/compiler/src/output/output_jit';
const anotherModuleUrl = 'somePackage/someOtherPath';
export function main() {
describe('Output JIT', () => {
describe('regression', () => {
it('should generate unique argument names', () => {
const externalIds = new Array(10).fill(1).map(
(_, index) =>
new o.ExternalReference(anotherModuleUrl, `id_${index}_`, {name: `id_${index}_`}));
const externalIds1 = new Array(10).fill(1).map(
(_, index) => new o.ExternalReference(
anotherModuleUrl, `id_${index}_1`, {name: `id_${index}_1`}));
const ctx = EmitterVisitorContext.createRoot();
const converter = new JitEmitterVisitor();
converter.visitAllStatements(
[o.literalArr([...externalIds1, ...externalIds].map(id => o.importExpr(id))).toStmt()],
ctx);
const args = converter.getArgs();
expect(Object.keys(args).length).toBe(20);
});
});
})
}

View File

@ -5,10 +5,10 @@
* Use of this source code is governed by an MIT-style license that can be * Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {AUTO_STYLE, AnimationEvent, AnimationOptions, animate, animateChild, group, keyframes, query, state, style, transition, trigger} from '@angular/animations'; import {AUTO_STYLE, AnimationEvent, AnimationOptions, animate, animateChild, group, keyframes, query, state, style, transition, trigger, ɵPRE_STYLE as PRE_STYLE} from '@angular/animations';
import {AnimationDriver, ɵAnimationEngine, ɵNoopAnimationDriver} from '@angular/animations/browser'; import {AnimationDriver, ɵAnimationEngine, ɵNoopAnimationDriver} from '@angular/animations/browser';
import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing'; import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing';
import {Component, HostBinding, HostListener, RendererFactory2, ViewChild} from '@angular/core'; import {ChangeDetectorRef, Component, HostBinding, HostListener, RendererFactory2, ViewChild} from '@angular/core';
import {ɵDomRendererFactory2} from '@angular/platform-browser'; import {ɵDomRendererFactory2} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -799,6 +799,75 @@ export function main() {
expect(p3.previousStyles).toEqual({}); expect(p3.previousStyles).toEqual({});
}); });
it('should provide the styling of previous players that are grouped', () => {
@Component({
selector: 'ani-cmp',
template: `
<div [@myAnimation]="exp"></div>
`,
animations: [trigger(
'myAnimation',
[
transition(
'1 => 2',
[
group([
animate(500, style({'width': '100px'})),
animate(500, style({'height': '100px'})),
]),
animate(500, keyframes([
style({'opacity': '0'}),
style({'opacity': '1'})
]))
]),
transition(
'2 => 3',
[
style({'opacity': '0'}),
animate(500, style({'opacity': '1'})),
]),
])],
})
class Cmp {
exp: any = false;
}
TestBed.configureTestingModule({declarations: [Cmp]});
const engine = TestBed.get(ɵAnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
fixture.detectChanges();
engine.flush();
cmp.exp = '1';
fixture.detectChanges();
engine.flush();
expect(getLog().length).toEqual(0);
resetLog();
cmp.exp = '2';
fixture.detectChanges();
engine.flush();
expect(getLog().length).toEqual(3);
resetLog();
cmp.exp = '3';
fixture.detectChanges();
engine.flush();
const players = getLog();
expect(players.length).toEqual(1);
const player = players[0] as MockAnimationPlayer;
const pp = player.previousPlayers as MockAnimationPlayer[];
expect(pp.length).toEqual(3);
expect(pp[0].currentSnapshot).toEqual({width: AUTO_STYLE});
expect(pp[1].currentSnapshot).toEqual({height: AUTO_STYLE});
expect(pp[2].currentSnapshot).toEqual({opacity: AUTO_STYLE});
});
it('should properly balance styles between states even if there are no destination state styles', it('should properly balance styles between states even if there are no destination state styles',
() => { () => {
@Component({ @Component({
@ -1413,6 +1482,65 @@ export function main() {
]); ]);
}); });
it('should not flush animations twice when an inner component runs change detection', () => {
@Component({
selector: 'outer-cmp',
template: `
<div *ngIf="exp" @outer></div>
<inner-cmp #inner></inner-cmp>
`,
animations: [trigger(
'outer',
[transition(':enter', [style({opacity: 0}), animate('1s', style({opacity: 1}))])])]
})
class OuterCmp {
@ViewChild('inner') public inner: any;
public exp: any = null;
update() { this.exp = 'go'; }
ngDoCheck() {
if (this.exp == 'go') {
this.inner.update();
}
}
}
@Component({
selector: 'inner-cmp',
template: `
<div *ngIf="exp" @inner></div>
`,
animations: [trigger('inner', [transition(
':enter',
[
style({opacity: 0}),
animate('1s', style({opacity: 1})),
])])]
})
class InnerCmp {
public exp: any;
constructor(private _ref: ChangeDetectorRef) {}
update() {
this.exp = 'go';
this._ref.detectChanges();
}
}
TestBed.configureTestingModule({declarations: [OuterCmp, InnerCmp]});
const engine = TestBed.get(ɵAnimationEngine);
const fixture = TestBed.createComponent(OuterCmp);
const cmp = fixture.componentInstance;
fixture.detectChanges();
expect(getLog()).toEqual([]);
cmp.update();
fixture.detectChanges();
const players = getLog();
expect(players.length).toEqual(2);
});
}); });
describe('animation listeners', () => { describe('animation listeners', () => {
@ -2153,9 +2281,141 @@ export function main() {
expect(cmp.startEvent.totalTime).toEqual(9876); expect(cmp.startEvent.totalTime).toEqual(9876);
// the done event isn't fired because it's an actual animation // the done event isn't fired because it's an actual animation
})); }));
it('should work when there are no animations on the component handling the disable/enable flag',
() => {
@Component({
selector: 'parent-cmp',
template: `
<div [@.disabled]="disableExp">
<child-cmp #child></child-cmp>
</div>
`
})
class ParentCmp {
@ViewChild('child') public child: ChildCmp|null = null;
disableExp = false;
}
@Component({
selector: 'child-cmp',
template: `
<div [@myAnimation]="exp"></div>
`,
animations: [trigger(
'myAnimation',
[transition(
'* => go, * => goAgain',
[style({opacity: 0}), animate('1s', style({opacity: 1}))])])]
})
class ChildCmp {
public exp = '';
}
TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]});
const fixture = TestBed.createComponent(ParentCmp);
const cmp = fixture.componentInstance;
cmp.disableExp = true;
fixture.detectChanges();
resetLog();
const child = cmp.child !;
child.exp = 'go';
fixture.detectChanges();
expect(getLog().length).toEqual(0);
resetLog();
cmp.disableExp = false;
child.exp = 'goAgain';
fixture.detectChanges();
expect(getLog().length).toEqual(1);
});
}); });
}); });
describe('animation normalization', () => {
it('should convert hyphenated properties to camelcase by default', () => {
@Component({
selector: 'cmp',
template: `
<div [@myAnimation]="exp"></div>
`,
animations: [
trigger(
'myAnimation',
[
transition(
'* => go',
[
style({'background-color': 'red', height: '100px', fontSize: '100px'}),
animate(
'1s',
style(
{'background-color': 'blue', height: '200px', fontSize: '200px'})),
]),
]),
]
})
class Cmp {
exp: any = false;
}
TestBed.configureTestingModule({declarations: [Cmp]});
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
cmp.exp = 'go';
fixture.detectChanges();
const players = getLog();
expect(players.length).toEqual(1);
expect(players[0].keyframes).toEqual([
{backgroundColor: 'red', height: '100px', fontSize: '100px', offset: 0},
{backgroundColor: 'blue', height: '200px', fontSize: '200px', offset: 1},
]);
});
it('should convert hyphenated properties to camelcase by default that are auto/pre style properties',
() => {
@Component({
selector: 'cmp',
template: `
<div [@myAnimation]="exp"></div>
`,
animations: [
trigger(
'myAnimation',
[
transition(
'* => go',
[
style({'background-color': AUTO_STYLE, 'font-size': '100px'}),
animate(
'1s', style({'background-color': 'blue', 'font-size': PRE_STYLE})),
]),
]),
]
})
class Cmp {
exp: any = false;
}
TestBed.configureTestingModule({declarations: [Cmp]});
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
cmp.exp = 'go';
fixture.detectChanges();
const players = getLog();
expect(players.length).toEqual(1);
expect(players[0].keyframes).toEqual([
{backgroundColor: AUTO_STYLE, fontSize: '100px', offset: 0},
{backgroundColor: 'blue', fontSize: PRE_STYLE, offset: 1},
]);
});
});
it('should throw neither state() or transition() are used inside of trigger()', () => { it('should throw neither state() or transition() are used inside of trigger()', () => {
@Component({ @Component({
selector: 'if-cmp', selector: 'if-cmp',

View File

@ -9,12 +9,16 @@ import {AnimationTriggerMetadata} from '@angular/animations';
import {ɵAnimationEngine as AnimationEngine} from '@angular/animations/browser'; import {ɵAnimationEngine as AnimationEngine} from '@angular/animations/browser';
import {Injectable, NgZone, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '@angular/core'; import {Injectable, NgZone, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '@angular/core';
const ANIMATION_PREFIX = '@';
const DISABLE_ANIMATIONS_FLAG = '@.disabled';
@Injectable() @Injectable()
export class AnimationRendererFactory implements RendererFactory2 { export class AnimationRendererFactory implements RendererFactory2 {
private _currentId: number = 0; private _currentId: number = 0;
private _microtaskId: number = 1; private _microtaskId: number = 1;
private _animationCallbacksBuffer: [(e: any) => any, any][] = []; private _animationCallbacksBuffer: [(e: any) => any, any][] = [];
private _rendererCache = new Map<Renderer2, BaseAnimationRenderer>(); private _rendererCache = new Map<Renderer2, BaseAnimationRenderer>();
private _cdRecurDepth = 0;
constructor( constructor(
private delegate: RendererFactory2, private engine: AnimationEngine, private _zone: NgZone) { private delegate: RendererFactory2, private engine: AnimationEngine, private _zone: NgZone) {
@ -58,6 +62,7 @@ export class AnimationRendererFactory implements RendererFactory2 {
} }
begin() { begin() {
this._cdRecurDepth++;
if (this.delegate.begin) { if (this.delegate.begin) {
this.delegate.begin(); this.delegate.begin();
} }
@ -90,10 +95,16 @@ export class AnimationRendererFactory implements RendererFactory2 {
} }
end() { end() {
this._zone.runOutsideAngular(() => { this._cdRecurDepth--;
this._scheduleCountTask();
this.engine.flush(this._microtaskId); // this is to prevent animations from running twice when an inner
}); // component does CD when a parent component insted has inserted it
if (this._cdRecurDepth == 0) {
this._zone.runOutsideAngular(() => {
this._scheduleCountTask();
this.engine.flush(this._microtaskId);
});
}
if (this.delegate.end) { if (this.delegate.end) {
this.delegate.end(); this.delegate.end();
} }
@ -166,7 +177,11 @@ export class BaseAnimationRenderer implements Renderer2 {
} }
setProperty(el: any, name: string, value: any): void { setProperty(el: any, name: string, value: any): void {
this.delegate.setProperty(el, name, value); if (name.charAt(0) == ANIMATION_PREFIX && name == DISABLE_ANIMATIONS_FLAG) {
this.disableAnimations(el, !!value);
} else {
this.delegate.setProperty(el, name, value);
}
} }
setValue(node: any, value: string): void { this.delegate.setValue(node, value); } setValue(node: any, value: string): void { this.delegate.setValue(node, value); }
@ -174,6 +189,10 @@ export class BaseAnimationRenderer implements Renderer2 {
listen(target: any, eventName: string, callback: (event: any) => boolean | void): () => void { listen(target: any, eventName: string, callback: (event: any) => boolean | void): () => void {
return this.delegate.listen(target, eventName, callback); return this.delegate.listen(target, eventName, callback);
} }
protected disableAnimations(element: any, value: boolean) {
this.engine.disableAnimations(element, value);
}
} }
export class AnimationRenderer extends BaseAnimationRenderer implements Renderer2 { export class AnimationRenderer extends BaseAnimationRenderer implements Renderer2 {
@ -185,9 +204,12 @@ export class AnimationRenderer extends BaseAnimationRenderer implements Renderer
} }
setProperty(el: any, name: string, value: any): void { setProperty(el: any, name: string, value: any): void {
if (name.charAt(0) == '@') { if (name.charAt(0) == ANIMATION_PREFIX) {
name = name.substr(1); if (name.charAt(1) == '.' && name == DISABLE_ANIMATIONS_FLAG) {
this.engine.process(this.namespaceId, el, name, value); this.disableAnimations(el, !!value);
} else {
this.engine.process(this.namespaceId, el, name.substr(1), value);
}
} else { } else {
this.delegate.setProperty(el, name, value); this.delegate.setProperty(el, name, value);
} }
@ -195,11 +217,13 @@ export class AnimationRenderer extends BaseAnimationRenderer implements Renderer
listen(target: 'window'|'document'|'body'|any, eventName: string, callback: (event: any) => any): listen(target: 'window'|'document'|'body'|any, eventName: string, callback: (event: any) => any):
() => void { () => void {
if (eventName.charAt(0) == '@') { if (eventName.charAt(0) == ANIMATION_PREFIX) {
const element = resolveElementFromTarget(target); const element = resolveElementFromTarget(target);
let name = eventName.substr(1); let name = eventName.substr(1);
let phase = ''; let phase = '';
if (name.charAt(0) != '@') { // transition-specific // @listener.phase is for trigger animation callbacks
// @@listener is for animation builder callbacks
if (name.charAt(0) != ANIMATION_PREFIX) {
[name, phase] = parseTriggerCallbackName(name); [name, phase] = parseTriggerCallbackName(name);
} }
return this.engine.listen(this.namespaceId, element, name, phase, event => { return this.engine.listen(this.namespaceId, element, name, phase, event => {

View File

@ -11,7 +11,7 @@ const xhr2: any = require('xhr2');
import {Injectable, Optional, Provider} from '@angular/core'; import {Injectable, Optional, Provider} from '@angular/core';
import {BrowserXhr, Connection, ConnectionBackend, Http, ReadyState, Request, RequestOptions, Response, XHRBackend, XSRFStrategy} from '@angular/http'; import {BrowserXhr, Connection, ConnectionBackend, Http, ReadyState, Request, RequestOptions, Response, XHRBackend, XSRFStrategy} from '@angular/http';
import {HttpClient, HttpRequest, HttpHandler, HttpInterceptor, HttpResponse, HTTP_INTERCEPTORS, HttpBackend, XhrFactory, ɵinterceptingHandler as interceptingHandler} from '@angular/common/http'; import {HttpClient, HttpEvent, HttpRequest, HttpHandler, HttpInterceptor, HttpResponse, HTTP_INTERCEPTORS, HttpBackend, XhrFactory, ɵinterceptingHandler as interceptingHandler} from '@angular/common/http';
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer'; import {Observer} from 'rxjs/Observer';
@ -143,12 +143,12 @@ export class ZoneMacroTaskBackend implements ConnectionBackend {
} }
export class ZoneClientBackend extends export class ZoneClientBackend extends
ZoneMacroTaskWrapper<HttpRequest<any>, HttpResponse<any>> implements HttpBackend { ZoneMacroTaskWrapper<HttpRequest<any>, HttpEvent<any>> implements HttpBackend {
constructor(private backend: HttpBackend) { super(); } constructor(private backend: HttpBackend) { super(); }
handle(request: HttpRequest<any>): Observable<HttpResponse<any>> { return this.wrap(request); } handle(request: HttpRequest<any>): Observable<HttpEvent<any>> { return this.wrap(request); }
protected delegate(request: HttpRequest<any>): Observable<HttpResponse<any>> { protected delegate(request: HttpRequest<any>): Observable<HttpEvent<any>> {
return this.backend.handle(request); return this.backend.handle(request);
} }
} }
@ -167,7 +167,7 @@ export function zoneWrappedInterceptingHandler(
export const SERVER_HTTP_PROVIDERS: Provider[] = [ export const SERVER_HTTP_PROVIDERS: Provider[] = [
{provide: Http, useFactory: httpFactory, deps: [XHRBackend, RequestOptions]}, {provide: Http, useFactory: httpFactory, deps: [XHRBackend, RequestOptions]},
{provide: BrowserXhr, useClass: ServerXhr}, {provide: XSRFStrategy, useClass: ServerXsrfStrategy}, {provide: BrowserXhr, useClass: ServerXhr}, {provide: XSRFStrategy, useClass: ServerXsrfStrategy},
{ {provide: XhrFactory, useClass: ServerXhr}, {
provide: HttpHandler, provide: HttpHandler,
useFactory: zoneWrappedInterceptingHandler, useFactory: zoneWrappedInterceptingHandler,
deps: [HttpBackend, [new Optional(), HTTP_INTERCEPTORS]] deps: [HttpBackend, [new Optional(), HTTP_INTERCEPTORS]]

View File

@ -18,9 +18,26 @@ import {INITIAL_CONFIG} from './tokens';
const parse5 = require('parse5'); const parse5 = require('parse5');
/**
* Options used to configure the server Platform instance that is created in {@link renderModule}
* and {@link renderModuleFactory}.
*
* @experimental
*/
export interface PlatformOptions { export interface PlatformOptions {
/**
* The full document HTML of the page to render as a string.
*/
document?: string; document?: string;
/**
* The URL for the current render request.
*/
url?: string; url?: string;
/**
* Platform level providers for the current render request.
*/
extraProviders?: Provider[]; extraProviders?: Provider[];
} }

View File

@ -8,6 +8,8 @@
import {animate, style, transition, trigger} from '@angular/animations'; import {animate, style, transition, trigger} from '@angular/animations';
import {APP_BASE_HREF, PlatformLocation, isPlatformServer} from '@angular/common'; import {APP_BASE_HREF, PlatformLocation, isPlatformServer} from '@angular/common';
import {HttpClient, HttpClientModule} from '@angular/common/http';
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
import {ApplicationRef, CompilerFactory, Component, HostListener, Input, NgModule, NgModuleRef, NgZone, PLATFORM_ID, PlatformRef, ViewEncapsulation, destroyPlatform, getPlatform} from '@angular/core'; import {ApplicationRef, CompilerFactory, Component, HostListener, Input, NgModule, NgModuleRef, NgZone, PLATFORM_ID, PlatformRef, ViewEncapsulation, destroyPlatform, getPlatform} from '@angular/core';
import {TestBed, async, inject} from '@angular/core/testing'; import {TestBed, async, inject} from '@angular/core/testing';
import {Http, HttpModule, Response, ResponseOptions, XHRBackend} from '@angular/http'; import {Http, HttpModule, Response, ResponseOptions, XHRBackend} from '@angular/http';
@ -145,6 +147,14 @@ export class HttpBeforeExampleModule {
export class HttpAfterExampleModule { export class HttpAfterExampleModule {
} }
@NgModule({
bootstrap: [MyServerApp],
declarations: [MyServerApp],
imports: [ServerModule, HttpClientModule, HttpClientTestingModule],
})
export class HttpClientExmapleModule {
}
@Component({selector: 'app', template: `<img [src]="'link'">`}) @Component({selector: 'app', template: `<img [src]="'link'">`})
class ImageApp { class ImageApp {
} }
@ -534,5 +544,45 @@ export function main() {
}); });
})); }));
}); });
describe('HttpClient', () => {
it('can inject HttpClient', async(() => {
const platform = platformDynamicServer(
[{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]);
platform.bootstrapModule(HttpClientExmapleModule).then(ref => {
expect(ref.injector.get(HttpClient) instanceof HttpClient).toBeTruthy();
});
}));
it('can make HttpClient requests', async(() => {
const platform = platformDynamicServer(
[{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]);
platform.bootstrapModule(HttpClientExmapleModule).then(ref => {
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get('http://localhost/testing').subscribe(body => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
});
mock.expectOne('http://localhost/testing').flush('success!');
});
});
}));
it('requests are macrotasks', async(() => {
const platform = platformDynamicServer(
[{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]);
platform.bootstrapModule(HttpClientExmapleModule).then(ref => {
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get('http://localhost/testing').subscribe(body => {
expect(body).toEqual('success!');
});
expect(ref.injector.get(NgZone).hasPendingMacrotasks).toBeTruthy();
mock.expectOne('http://localhost/testing').flush('success!');
expect(ref.injector.get(NgZone).hasPendingMacrotasks).toBeFalsy();
});
});
}));
});
}); });
} }

View File

@ -181,7 +181,9 @@ function match(segmentGroup: UrlSegmentGroup, route: Route, segments: UrlSegment
const posParams: {[n: string]: string} = {}; const posParams: {[n: string]: string} = {};
forEach(res.posParams !, (v: UrlSegment, k: string) => { posParams[k] = v.path; }); forEach(res.posParams !, (v: UrlSegment, k: string) => { posParams[k] = v.path; });
const parameters = {...posParams, ...res.consumed[res.consumed.length - 1].parameters}; const parameters = res.consumed.length > 0 ?
{...posParams, ...res.consumed[res.consumed.length - 1].parameters} :
posParams;
return {consumedSegments: res.consumed, lastChild: res.consumed.length, parameters}; return {consumedSegments: res.consumed, lastChild: res.consumed.length, parameters};
} }

View File

@ -17,6 +17,7 @@ import {of } from 'rxjs/observable/of';
import {concatMap} from 'rxjs/operator/concatMap'; import {concatMap} from 'rxjs/operator/concatMap';
import {every} from 'rxjs/operator/every'; import {every} from 'rxjs/operator/every';
import {first} from 'rxjs/operator/first'; import {first} from 'rxjs/operator/first';
import {last} from 'rxjs/operator/last';
import {map} from 'rxjs/operator/map'; import {map} from 'rxjs/operator/map';
import {mergeMap} from 'rxjs/operator/mergeMap'; import {mergeMap} from 'rxjs/operator/mergeMap';
import {reduce} from 'rxjs/operator/reduce'; import {reduce} from 'rxjs/operator/reduce';
@ -312,7 +313,7 @@ export class Router {
get events(): Observable<Event> { return this.routerEvents; } get events(): Observable<Event> { return this.routerEvents; }
/** @internal */ /** @internal */
triggerEvent(e: Event) { this.routerEvents.next(e); } triggerEvent(e: Event): void { this.routerEvents.next(e); }
/** /**
* Resets the configuration used for navigation and generating links. * Resets the configuration used for navigation and generating links.
@ -331,10 +332,11 @@ export class Router {
resetConfig(config: Routes): void { resetConfig(config: Routes): void {
validateConfig(config); validateConfig(config);
this.config = config; this.config = config;
this.navigated = false;
} }
/** @docsNotRequired */ /** @docsNotRequired */
ngOnDestroy() { this.dispose(); } ngOnDestroy(): void { this.dispose(); }
/** Disposes of the router */ /** Disposes of the router */
dispose(): void { dispose(): void {
@ -837,11 +839,10 @@ export class PreActivation {
// reusing the node // reusing the node
if (curr && future._routeConfig === curr._routeConfig) { if (curr && future._routeConfig === curr._routeConfig) {
if (this.shouldRunGuardsAndResolvers( const shouldRunGuardsAndResolvers = this.shouldRunGuardsAndResolvers(
curr, future, future._routeConfig !.runGuardsAndResolvers)) { curr, future, future._routeConfig !.runGuardsAndResolvers);
if (shouldRunGuardsAndResolvers) {
this.canActivateChecks.push(new CanActivate(futurePath)); this.canActivateChecks.push(new CanActivate(futurePath));
const outlet = context !.outlet !;
this.canDeactivateChecks.push(new CanDeactivate(outlet.component, curr));
} else { } else {
// we need to set the data // we need to set the data
future.data = curr.data; future.data = curr.data;
@ -857,6 +858,11 @@ export class PreActivation {
} else { } else {
this.traverseChildRoutes(futureNode, currNode, parentContexts, futurePath); this.traverseChildRoutes(futureNode, currNode, parentContexts, futurePath);
} }
if (shouldRunGuardsAndResolvers) {
const outlet = context !.outlet !;
this.canDeactivateChecks.push(new CanDeactivate(outlet.component, curr));
}
} else { } else {
if (curr) { if (curr) {
this.deactivateRouteAndItsChildren(currNode, context); this.deactivateRouteAndItsChildren(currNode, context);
@ -1004,11 +1010,29 @@ export class PreActivation {
} }
private resolveNode(resolve: ResolveData, future: ActivatedRouteSnapshot): Observable<any> { private resolveNode(resolve: ResolveData, future: ActivatedRouteSnapshot): Observable<any> {
return waitForMap(resolve, (k, v) => { const keys = Object.keys(resolve);
const resolver = this.getToken(v, future); if (keys.length === 0) {
return resolver.resolve ? wrapIntoObservable(resolver.resolve(future, this.future)) : return of ({});
wrapIntoObservable(resolver(future, this.future)); }
if (keys.length === 1) {
const key = keys[0];
return map.call(
this.getResolver(resolve[key], future), (value: any) => { return {[key]: value}; });
}
const data: {[k: string]: any} = {};
const runningResolvers$ = mergeMap.call(from(keys), (key: string) => {
return map.call(this.getResolver(resolve[key], future), (value: any) => {
data[key] = value;
return value;
});
}); });
return map.call(last.call(runningResolvers$), () => data);
}
private getResolver(injectionToken: any, future: ActivatedRouteSnapshot): Observable<any> {
const resolver = this.getToken(injectionToken, future);
return resolver.resolve ? wrapIntoObservable(resolver.resolve(future, this.future)) :
wrapIntoObservable(resolver(future, this.future));
} }
private getToken(token: any, snapshot: ActivatedRouteSnapshot): any { private getToken(token: any, snapshot: ActivatedRouteSnapshot): any {

View File

@ -13,6 +13,8 @@ import {By} from '@angular/platform-browser/src/dom/debug/by';
import {expect} from '@angular/platform-browser/testing/src/matchers'; import {expect} from '@angular/platform-browser/testing/src/matchers';
import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, CanDeactivate, DetachedRouteHandle, Event, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, PRIMARY_OUTLET, ParamMap, Params, PreloadAllModules, PreloadingStrategy, Resolve, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterModule, RouterPreloader, RouterStateSnapshot, RoutesRecognized, RunGuardsAndResolvers, UrlHandlingStrategy, UrlSegmentGroup, UrlTree} from '@angular/router'; import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, CanDeactivate, DetachedRouteHandle, Event, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, PRIMARY_OUTLET, ParamMap, Params, PreloadAllModules, PreloadingStrategy, Resolve, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterModule, RouterPreloader, RouterStateSnapshot, RoutesRecognized, RunGuardsAndResolvers, UrlHandlingStrategy, UrlSegmentGroup, UrlTree} from '@angular/router';
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';
import {of } from 'rxjs/observable/of';
import {map} from 'rxjs/operator/map'; import {map} from 'rxjs/operator/map';
import {forEach} from '../src/utils/collection'; import {forEach} from '../src/utils/collection';
@ -396,6 +398,25 @@ describe('Integration', () => {
expect(location.path()).toEqual('/team/22/user/victor'); expect(location.path()).toEqual('/team/22/user/victor');
}))); })));
it('should navigate to the same url when config changes',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp);
router.resetConfig([{path: 'a', component: SimpleCmp}]);
router.navigate(['/a']);
advance(fixture);
expect(location.path()).toEqual('/a');
expect(fixture.nativeElement).toHaveText('simple');
router.resetConfig([{path: 'a', component: RouteCmp}]);
router.navigate(['/a']);
advance(fixture);
expect(location.path()).toEqual('/a');
expect(fixture.nativeElement).toHaveText('route');
})));
it('should navigate when locations changes', it('should navigate when locations changes',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
@ -913,13 +934,12 @@ describe('Integration', () => {
{provide: 'resolveFour', useValue: (a: any, b: any) => 4}, {provide: 'resolveFour', useValue: (a: any, b: any) => 4},
{provide: 'resolveSix', useClass: ResolveSix}, {provide: 'resolveSix', useClass: ResolveSix},
{provide: 'resolveError', useValue: (a: any, b: any) => Promise.reject('error')}, {provide: 'resolveError', useValue: (a: any, b: any) => Promise.reject('error')},
{provide: 'numberOfUrlSegments', useValue: (a: any, b: any) => a.url.length} {provide: 'numberOfUrlSegments', useValue: (a: any, b: any) => a.url.length},
] ]
}); });
}); });
it('should provide resolved data', it('should provide resolved data', fakeAsync(inject([Router], (router: Router) => {
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmpWithTwoOutlets); const fixture = createRoot(router, RootCmpWithTwoOutlets);
router.resetConfig([{ router.resetConfig([{
@ -1025,6 +1045,57 @@ describe('Integration', () => {
expect(cmp.route.snapshot.data).toEqual({numberOfUrlSegments: 3}); expect(cmp.route.snapshot.data).toEqual({numberOfUrlSegments: 3});
}))); })));
describe('should run resolvers for the same route concurrently', () => {
let log: string[];
let observer: Observer<any>;
beforeEach(() => {
log = [];
TestBed.configureTestingModule({
providers: [
{
provide: 'resolver1',
useValue: () => {
const obs$ = new Observable((obs: Observer<any>) => {
observer = obs;
return () => {};
});
return map.call(obs$, () => log.push('resolver1'));
}
},
{
provide: 'resolver2',
useValue: () => {
return map.call(of (null), () => {
log.push('resolver2');
observer.next(null);
observer.complete()
});
}
},
]
});
});
it('works', fakeAsync(inject([Router], (router: Router) => {
const fixture = createRoot(router, RootCmp);
router.resetConfig([{
path: 'a',
resolve: {
one: 'resolver1',
two: 'resolver2',
},
component: SimpleCmp
}]);
router.navigateByUrl('/a');
advance(fixture);
expect(log).toEqual(['resolver2', 'resolver1']);
})));
});
}); });
describe('router links', () => { describe('router links', () => {
@ -2364,6 +2435,11 @@ describe('Integration', () => {
provide: 'canDeactivate_team', provide: 'canDeactivate_team',
useFactory: (logger: Logger) => () => (logger.add('canDeactivate_team'), true), useFactory: (logger: Logger) => () => (logger.add('canDeactivate_team'), true),
deps: [Logger] deps: [Logger]
},
{
provide: 'canDeactivate_simple',
useFactory: (logger: Logger) => () => (logger.add('canDeactivate_simple'), true),
deps: [Logger]
} }
] ]
}); });
@ -2397,6 +2473,31 @@ describe('Integration', () => {
'canDeactivate_team', 'canActivateChild_parent', 'canActivate_team' 'canDeactivate_team', 'canActivateChild_parent', 'canActivate_team'
]); ]);
}))); })));
it('should call deactivate guards from bottom to top',
fakeAsync(inject(
[Router, Location, Logger], (router: Router, location: Location, logger: Logger) => {
const fixture = createRoot(router, RootCmp);
router.resetConfig([{
path: '',
children: [{
path: 'team/:id',
canDeactivate: ['canDeactivate_team'],
children:
[{path: '', component: SimpleCmp, canDeactivate: ['canDeactivate_simple']}],
component: TeamCmp
}]
}]);
router.navigateByUrl('/team/22');
advance(fixture);
router.navigateByUrl('/team/33');
advance(fixture);
expect(logger.logs).toEqual(['canDeactivate_simple', 'canDeactivate_team']);
})));
}); });
}); });

View File

@ -658,6 +658,26 @@ describe('recognize', () => {
checkActivatedRoute(a.firstChild !, 'b', {}, ComponentB); checkActivatedRoute(a.firstChild !, 'b', {}, ComponentB);
}); });
}); });
it('should work with terminal route', () => {
const matcher = (s: any, g: any, r: any) => s.length === 0 ? ({consumed: s}) : null;
checkRecognize([{matcher, component: ComponentA}] as any, '', (s: RouterStateSnapshot) => {
const a = s.root.firstChild !;
checkActivatedRoute(a, '', {}, ComponentA);
});
});
it('should work with child terminal route', () => {
const matcher = (s: any, g: any, r: any) => s.length === 0 ? ({consumed: s}) : null;
checkRecognize(
[{path: 'a', component: ComponentA, children: [{matcher, component: ComponentB}]}] as any,
'a', (s: RouterStateSnapshot) => {
const a = s.root.firstChild !;
checkActivatedRoute(a, 'a', {}, ComponentA);
});
});
}); });
describe('query parameters', () => { describe('query parameters', () => {

View File

@ -50,7 +50,7 @@ export interface IRootScopeService {
$destroy(): any; $destroy(): any;
$apply(exp?: Ng1Expression): any; $apply(exp?: Ng1Expression): any;
$digest(): any; $digest(): any;
$evalAsync(): any; $evalAsync(exp: Ng1Expression, locals?: any): void;
$on(event: string, fn?: (event?: any, ...args: any[]) => void): Function; $on(event: string, fn?: (event?: any, ...args: any[]) => void): Function;
$$childTail: IScope; $$childTail: IScope;
$$childHead: IScope; $$childHead: IScope;
@ -101,7 +101,10 @@ export interface IComponent {
templateUrl?: string|Function; templateUrl?: string|Function;
transclude?: DirectiveTranscludeProperty; transclude?: DirectiveTranscludeProperty;
} }
export interface IAttributes { $observe(attr: string, fn: (v: string) => void): void; } export interface IAttributes {
$observe(attr: string, fn: (v: string) => void): void;
[key: string]: any;
}
export interface ITranscludeFunction { export interface ITranscludeFunction {
// If the scope is provided, then the cloneAttachFn must be as well. // If the scope is provided, then the cloneAttachFn must be as well.
(scope: IScope, cloneAttachFn: ICloneAttachFunction): IAugmentedJQuery; (scope: IScope, cloneAttachFn: ICloneAttachFunction): IAugmentedJQuery;
@ -135,7 +138,10 @@ export interface IProvideService {
decorator(token: Ng1Token, factory: IInjectable): void; decorator(token: Ng1Token, factory: IInjectable): void;
} }
export interface IParseService { (expression: string): ICompiledExpression; } export interface IParseService { (expression: string): ICompiledExpression; }
export interface ICompiledExpression { assign(context: any, value: any): any; } export interface ICompiledExpression {
(context: any, locals: any): any;
assign?: (context: any, value: any) => any;
}
export interface IHttpBackendService { export interface IHttpBackendService {
(method: string, url: string, post?: any, callback?: Function, headers?: any, timeout?: number, (method: string, url: string, post?: any, callback?: Function, headers?: any, timeout?: number,
withCredentials?: boolean): void; withCredentials?: boolean): void;
@ -211,8 +217,8 @@ function noNg() {
let angular: { let angular: {
bootstrap: (e: Element, modules: (string | IInjectable)[], config: IAngularBootstrapConfig) => bootstrap: (e: Element, modules: (string | IInjectable)[], config?: IAngularBootstrapConfig) =>
void, IInjectorService,
module: (prefix: string, dependencies?: string[]) => IModule, module: (prefix: string, dependencies?: string[]) => IModule,
element: (e: Element | string) => IAugmentedJQuery, element: (e: Element | string) => IAugmentedJQuery,
version: {major: number}, version: {major: number},
@ -256,16 +262,16 @@ export function getAngularLib(): any {
} }
export const bootstrap = export const bootstrap =
(e: Element, modules: (string | IInjectable)[], config: IAngularBootstrapConfig): void => (e: Element, modules: (string | IInjectable)[], config?: IAngularBootstrapConfig) =>
angular.bootstrap(e, modules, config); angular.bootstrap(e, modules, config);
export const module = (prefix: string, dependencies?: string[]): IModule => export const module = (prefix: string, dependencies?: string[]) =>
angular.module(prefix, dependencies); angular.module(prefix, dependencies);
export const element = (e: Element | string): IAugmentedJQuery => angular.element(e); export const element = (e: Element | string) => angular.element(e);
export const resumeBootstrap = (): void => angular.resumeBootstrap(); export const resumeBootstrap = () => angular.resumeBootstrap();
export const getTestability = (e: Element): ITestabilityService => angular.getTestability(e); export const getTestability = (e: Element) => angular.getTestability(e);
export const version = angular.version; export const version = angular.version;

View File

@ -69,7 +69,7 @@ export class DowngradeComponentAdapter {
const inputs = this.componentFactory.inputs || []; const inputs = this.componentFactory.inputs || [];
for (let i = 0; i < inputs.length; i++) { for (let i = 0; i < inputs.length; i++) {
const input = new PropertyBinding(inputs[i].propName, inputs[i].templateName); const input = new PropertyBinding(inputs[i].propName, inputs[i].templateName);
let expr: any /** TODO #9100 */ = null; let expr: string|null = null;
if (attrs.hasOwnProperty(input.attr)) { if (attrs.hasOwnProperty(input.attr)) {
const observeFn = (prop => { const observeFn = (prop => {
@ -91,20 +91,20 @@ export class DowngradeComponentAdapter {
// Use `$watch()` (in addition to `$observe()`) in order to initialize the input in time // Use `$watch()` (in addition to `$observe()`) in order to initialize the input in time
// for `ngOnChanges()`. This is necessary if we are already in a `$digest`, which means that // for `ngOnChanges()`. This is necessary if we are already in a `$digest`, which means that
// `ngOnChanges()` (which is called by a watcher) will run before the `$observe()` callback. // `ngOnChanges()` (which is called by a watcher) will run before the `$observe()` callback.
let unwatch: any = this.componentScope.$watch(() => { let unwatch: Function|null = this.componentScope.$watch(() => {
unwatch(''); unwatch !();
unwatch = null; unwatch = null;
observeFn((attrs as any)[input.attr]); observeFn(attrs[input.attr]);
}); });
} else if (attrs.hasOwnProperty(input.bindAttr)) { } else if (attrs.hasOwnProperty(input.bindAttr)) {
expr = (attrs as any /** TODO #9100 */)[input.bindAttr]; expr = attrs[input.bindAttr];
} else if (attrs.hasOwnProperty(input.bracketAttr)) { } else if (attrs.hasOwnProperty(input.bracketAttr)) {
expr = (attrs as any /** TODO #9100 */)[input.bracketAttr]; expr = attrs[input.bracketAttr];
} else if (attrs.hasOwnProperty(input.bindonAttr)) { } else if (attrs.hasOwnProperty(input.bindonAttr)) {
expr = (attrs as any /** TODO #9100 */)[input.bindonAttr]; expr = attrs[input.bindonAttr];
} else if (attrs.hasOwnProperty(input.bracketParenAttr)) { } else if (attrs.hasOwnProperty(input.bracketParenAttr)) {
expr = (attrs as any /** TODO #9100 */)[input.bracketParenAttr]; expr = attrs[input.bracketParenAttr];
} }
if (expr != null) { if (expr != null) {
const watchFn = const watchFn =
@ -132,24 +132,22 @@ export class DowngradeComponentAdapter {
const outputs = this.componentFactory.outputs || []; const outputs = this.componentFactory.outputs || [];
for (let j = 0; j < outputs.length; j++) { for (let j = 0; j < outputs.length; j++) {
const output = new PropertyBinding(outputs[j].propName, outputs[j].templateName); const output = new PropertyBinding(outputs[j].propName, outputs[j].templateName);
let expr: any /** TODO #9100 */ = null; let expr: string|null = null;
let assignExpr = false; let assignExpr = false;
const bindonAttr = const bindonAttr = output.bindonAttr.substring(0, output.bindonAttr.length - 6);
output.bindonAttr ? output.bindonAttr.substring(0, output.bindonAttr.length - 6) : null; const bracketParenAttr =
const bracketParenAttr = output.bracketParenAttr ? `[(${output.bracketParenAttr.substring(2, output.bracketParenAttr.length - 8)})]`;
`[(${output.bracketParenAttr.substring(2, output.bracketParenAttr.length - 8)})]` :
null;
if (attrs.hasOwnProperty(output.onAttr)) { if (attrs.hasOwnProperty(output.onAttr)) {
expr = (attrs as any /** TODO #9100 */)[output.onAttr]; expr = attrs[output.onAttr];
} else if (attrs.hasOwnProperty(output.parenAttr)) { } else if (attrs.hasOwnProperty(output.parenAttr)) {
expr = (attrs as any /** TODO #9100 */)[output.parenAttr]; expr = attrs[output.parenAttr];
} else if (attrs.hasOwnProperty(bindonAttr !)) { } else if (attrs.hasOwnProperty(bindonAttr)) {
expr = (attrs as any /** TODO #9100 */)[bindonAttr !]; expr = attrs[bindonAttr];
assignExpr = true; assignExpr = true;
} else if (attrs.hasOwnProperty(bracketParenAttr !)) { } else if (attrs.hasOwnProperty(bracketParenAttr)) {
expr = (attrs as any /** TODO #9100 */)[bracketParenAttr !]; expr = attrs[bracketParenAttr];
assignExpr = true; assignExpr = true;
} }
@ -162,10 +160,8 @@ export class DowngradeComponentAdapter {
const emitter = this.component[output.prop] as EventEmitter<any>; const emitter = this.component[output.prop] as EventEmitter<any>;
if (emitter) { if (emitter) {
emitter.subscribe({ emitter.subscribe({
next: assignExpr ? next: assignExpr ? (v: any) => setter !(this.scope, v) :
((setter: any) => (v: any /** TODO #9100 */) => setter(this.scope, v))(setter) : (v: any) => getter(this.scope, {'$event': v})
((getter: any) => (v: any /** TODO #9100 */) =>
getter(this.scope, {'$event': v}))(getter)
}); });
} else { } else {
throw new Error( throw new Error(

View File

@ -247,7 +247,7 @@ export class UpgradeModule {
const upgradeModule = angular.module(UPGRADE_MODULE_NAME, [INIT_MODULE_NAME].concat(modules)); const upgradeModule = angular.module(UPGRADE_MODULE_NAME, [INIT_MODULE_NAME].concat(modules));
// Make sure resumeBootstrap() only exists if the current bootstrap is deferred // Make sure resumeBootstrap() only exists if the current bootstrap is deferred
const windowAngular = (window as any /** TODO #???? */)['angular']; const windowAngular = (window as any)['angular'];
windowAngular.resumeBootstrap = undefined; windowAngular.resumeBootstrap = undefined;
// Bootstrap the AngularJS application inside our zone // Bootstrap the AngularJS application inside our zone

View File

@ -21,8 +21,8 @@ export function main() {
afterEach(() => destroyPlatform()); afterEach(() => destroyPlatform());
it('should interleave scope and component expressions', async(() => { it('should interleave scope and component expressions', async(() => {
const log: any[] /** TODO #9100 */ = []; const log: string[] = [];
const l = (value: any /** TODO #9100 */) => { const l = (value: string) => {
log.push(value); log.push(value);
return value + ';'; return value + ';';
}; };
@ -46,8 +46,7 @@ export function main() {
template: `{{l('2A')}}<ng1a></ng1a>{{l('2B')}}<ng1b></ng1b>{{l('2C')}}` template: `{{l('2A')}}<ng1a></ng1a>{{l('2B')}}<ng1b></ng1b>{{l('2C')}}`
}) })
class Ng2Component { class Ng2Component {
l: (value: any) => string; l = l;
constructor() { this.l = l; }
} }
@NgModule({ @NgModule({
@ -63,7 +62,7 @@ export function main() {
.directive('ng1a', () => ({template: '{{ l(\'ng1a\') }}'})) .directive('ng1a', () => ({template: '{{ l(\'ng1a\') }}'}))
.directive('ng1b', () => ({template: '{{ l(\'ng1b\') }}'})) .directive('ng1b', () => ({template: '{{ l(\'ng1b\') }}'}))
.directive('ng2', downgradeComponent({component: Ng2Component})) .directive('ng2', downgradeComponent({component: Ng2Component}))
.run(($rootScope: any /** TODO #9100 */) => { .run(($rootScope: angular.IRootScopeService) => {
$rootScope.l = l; $rootScope.l = l;
$rootScope.reset = () => log.length = 0; $rootScope.reset = () => log.length = 0;
}); });
@ -72,7 +71,6 @@ export function main() {
html('<div>{{reset(); l(\'1A\');}}<ng2>{{l(\'1B\')}}</ng2>{{l(\'1C\')}}</div>'); html('<div>{{reset(); l(\'1A\');}}<ng2>{{l(\'1B\')}}</ng2>{{l(\'1C\')}}</div>');
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
expect(document.body.textContent).toEqual('1A;2A;ng1a;2B;ng1b;2C;1C;'); expect(document.body.textContent).toEqual('1A;2A;ng1a;2B;ng1b;2C;1C;');
// https://github.com/angular/angular.js/issues/12983
expect(log).toEqual(['1A', '1C', '2A', '2B', '2C', 'ng1a', 'ng1b']); expect(log).toEqual(['1A', '1C', '2A', '2B', '2C', 'ng1a', 'ng1b']);
}); });
})); }));
@ -88,7 +86,7 @@ export function main() {
@Component({ @Component({
selector: 'my-child', selector: 'my-child',
template: '<div>{{valueFromPromise}}', template: '<div>{{ valueFromPromise }}</div>',
}) })
class ChildComponent { class ChildComponent {
valueFromPromise: number; valueFromPromise: number;

View File

@ -18,7 +18,7 @@ export function bootstrap(
// We bootstrap the Angular module first; then when it is ready (async) // We bootstrap the Angular module first; then when it is ready (async)
// We bootstrap the AngularJS module on the bootstrap element // We bootstrap the AngularJS module on the bootstrap element
return platform.bootstrapModule(Ng2Module).then(ref => { return platform.bootstrapModule(Ng2Module).then(ref => {
const upgrade = ref.injector.get(UpgradeModule) as UpgradeModule; const upgrade = ref.injector.get<UpgradeModule>(UpgradeModule);
upgrade.bootstrap(element, [ng1Module.name]); upgrade.bootstrap(element, [ng1Module.name]);
return upgrade; return upgrade;
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "@angular/tsc-wrapped", "name": "@angular/tsc-wrapped",
"version": "4.3.0", "version": "4.3.1",
"description": "Wraps the tsc CLI, allowing extensions.", "description": "Wraps the tsc CLI, allowing extensions.",
"homepage": "https://github.com/angular/angular/blob/master/tools/@angular/tsc-wrapped", "homepage": "https://github.com/angular/angular/blob/master/tools/@angular/tsc-wrapped",
"bugs": "https://github.com/angular/angular/issues", "bugs": "https://github.com/angular/angular/issues",