Compare commits
39 Commits
Author | SHA1 | Date | |
---|---|---|---|
54be25a7a1 | |||
b1757037fb | |||
f0476fcff0 | |||
a5c4bb5b96 | |||
4c1f32b0db | |||
383d8969ab | |||
333ffd8d32 | |||
d4679a0bc2 | |||
4ce29f3a5b | |||
17b7bc3e06 | |||
f19bd5f4f3 | |||
d503d25f29 | |||
5d275e994a | |||
d8c8b13bb8 | |||
4671168635 | |||
1ac78bfd5d | |||
4340beacea | |||
ec89f378fc | |||
4dd6863bc2 | |||
37c626e673 | |||
f0a110928b | |||
c39e7d1eb2 | |||
799bffb431 | |||
fda607cc2f | |||
cc3aa68123 | |||
306621d2d6 | |||
d204f7aa2a | |||
a94f5e8cbb | |||
1390afef23 | |||
b0346a6e45 | |||
e5da059994 | |||
ac92c3bb26 | |||
87157d7089 | |||
611dd12f0f | |||
969ce9dc2b | |||
34834a9e79 | |||
6e2ddccc2c | |||
55742e4737 | |||
0091b1e8db |
@ -256,6 +256,8 @@ groups:
|
||||
files:
|
||||
include:
|
||||
- "aio/*"
|
||||
exclude:
|
||||
- "aio/content/*"
|
||||
users:
|
||||
- petebacondarwin #primary
|
||||
- IgorMinar
|
||||
@ -276,6 +278,8 @@ groups:
|
||||
- Foxandxss
|
||||
- stephenfluin
|
||||
- wardbell
|
||||
- petebacondarwin
|
||||
- gkalpak
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
@ -289,5 +293,7 @@ groups:
|
||||
users:
|
||||
- juleskremer #primary
|
||||
- stephenfluin
|
||||
- petebacondarwin
|
||||
- gkalpak
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
21
CHANGELOG.md
21
CHANGELOG.md
@ -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>
|
||||
# [4.3.0](https://github.com/angular/angular/compare/4.3.0-rc.0...4.3.0) (2017-07-14)
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Bootstrapping
|
||||
|
||||
An NgModule class describes how the application parts fit together.
|
||||
Every application has at least one NgModule, the _root_ module
|
||||
that you [bootstrap](guide/appmodule#main) to launch the application.
|
||||
Every application has at least one NgModule, the _root_ module
|
||||
that you [bootstrap](#main) to launch the application.
|
||||
You can call it anything you want. The conventional name is `AppModule`.
|
||||
|
||||
The [setup](guide/setup) instructions produce a new project with the following minimal `AppModule`.
|
||||
@ -25,7 +25,7 @@ The `@NgModule` decorator identifies `AppModule` as an `NgModule` class.
|
||||
* **_bootstrap_** — the _root_ component that Angular creates and inserts into the `index.html` host web page.
|
||||
|
||||
The [NgModules](guide/ngmodule) guide dives deeply into the details of NgModules.
|
||||
All you need to know at the moment is a few basics about these three properties.
|
||||
All you need to know at the moment is a few basics about these three properties.
|
||||
|
||||
|
||||
{@a imports}
|
||||
@ -34,7 +34,7 @@ All you need to know at the moment is a few basics about these three properties.
|
||||
### The _imports_ array
|
||||
|
||||
NgModules are a way to consolidate features that belong together into discrete units.
|
||||
Many features of Angular itself are organized as NgModules.
|
||||
Many features of Angular itself are organized as NgModules.
|
||||
HTTP services are in the `HttpModule`. The router is in the `RouterModule`.
|
||||
Eventually you may create a feature module.
|
||||
|
||||
@ -70,7 +70,7 @@ You add `import` statements to almost every application file.
|
||||
They have nothing to do with Angular and Angular knows nothing about them.
|
||||
|
||||
The _module's_ `imports` array appears _exclusively_ in the `@NgModule` metadata object.
|
||||
It tells Angular about specific _other_ NgModules—all of them classes decorated
|
||||
It tells Angular about specific _other_ NgModules—all of them classes decorated
|
||||
with `@NgModule`—that the application needs to function properly.
|
||||
|
||||
</div>
|
||||
@ -110,7 +110,7 @@ Do not put any other kind of class in `declarations`; _not_ `NgModule` classes,
|
||||
|
||||
### The _bootstrap_ array
|
||||
|
||||
You launch the application by [_bootstrapping_](guide/bootstrapping#main) the root `AppModule`.
|
||||
You launch the application by [_bootstrapping_](#main) the root `AppModule`.
|
||||
Among other things, the _bootstrapping_ process creates the component(s) listed in the `bootstrap` array
|
||||
and inserts each one into the browser DOM.
|
||||
|
||||
@ -127,13 +127,6 @@ Which brings us to the _bootstrapping_ process itself.
|
||||
|
||||
{@a main}
|
||||
|
||||
|
||||
<l-main-section>
|
||||
|
||||
</l-main-section>
|
||||
|
||||
|
||||
|
||||
## Bootstrap in _main.ts_
|
||||
|
||||
There are many ways to bootstrap an application.
|
||||
|
@ -27,12 +27,12 @@ Revised samples are more clear and cover all topics discussed.
|
||||
## NEW: Samples re-structured with `src/` folder (2017-02-02)
|
||||
All documentation samples have been realigned with the default folder structure of the Angular CLI.
|
||||
That's a step along the road to basing the sample in the Angular CLI.
|
||||
But it's also good in its own right.
|
||||
But it's also good in its own right.
|
||||
It helps clearly separate app code from setup and configuration files.
|
||||
|
||||
All samples now have a `src/` folder at the project root.
|
||||
The former `app/` folder moves under `src/`.
|
||||
Read about moving your existing project to this structure in
|
||||
The former `app/` folder moves under `src/`.
|
||||
Read about moving your existing project to this structure in
|
||||
<a href="https://github.com/angular/quickstart#updating-to-a-newer-version-of-the-quickstart-repo" target="Migrating samples/quickstart app to the src folder">
|
||||
the QuickStart repo update instructions</a>.
|
||||
|
||||
@ -48,8 +48,8 @@ Notably:
|
||||
The new [**Reactive Forms**](guide/reactive-forms) guide explains how and why to build a "reactive form".
|
||||
"Reactive Forms" are the code-based counterpart to the declarative "Template Driven" forms approach
|
||||
introduced in the [Forms](guide/forms) guide.
|
||||
Check it out before you decide how to add forms to your app.
|
||||
Remember also that you can use both techniques in the same app,
|
||||
Check it out before you decide how to add forms to your app.
|
||||
Remember also that you can use both techniques in the same app,
|
||||
choosing the approach that best fits each scenario.
|
||||
|
||||
## NEW: Deployment guide (2017-01-30)
|
||||
@ -65,25 +65,25 @@ Revised samples are clearer and cover all topics discussed.
|
||||
|
||||
## Miscellaneous (2017-01-05)
|
||||
|
||||
* [Setup](guide/setup) guide:
|
||||
added (optional) instructions on how to remove _non-essential_ files.
|
||||
* [Setup](guide/setup) guide:
|
||||
added (optional) instructions on how to remove _non-essential_ files.
|
||||
* No longer consolidate RxJS operator imports in `rxjs-extensions` file; each file should import what it needs.
|
||||
* All samples prepend template/style URLs with `./` as a best practice.
|
||||
* [Style Guide](guide/styleguide): copy edits and revised rules.
|
||||
|
||||
## Router: more detail (2016-12-21)
|
||||
|
||||
Added more information to the [Router](guide/router) guide
|
||||
Added more information to the [Router](guide/router) guide
|
||||
including sections named outlets, wildcard routes, and preload strategies.
|
||||
|
||||
## HTTP: how to set default request headers (and other request options) (2016-12-14)
|
||||
|
||||
Added section on how to set default request headers (and other request options) to
|
||||
[HTTP](guide/http#override-default-request-options) guide.
|
||||
Added section on how to set default request headers (and other request options) to
|
||||
HTTP guide.
|
||||
|
||||
## Testing: added component test plunkers (2016-12-02)
|
||||
|
||||
Added two plunkers that each test _one simple component_ so you can write a component test plunker of your own: <live-example name="setup" plnkr="quickstart-specs">one</live-example> for the QuickStart seed's `AppComponent` and <live-example name="testing" plnkr="banner-specs">another</live-example> for the Testing guide's `BannerComponent`.
|
||||
Added two plunkers that each test _one simple component_ so you can write a component test plunker of your own: <live-example name="setup" plnkr="quickstart-specs">one</live-example> for the QuickStart seed's `AppComponent` and <live-example name="testing" plnkr="banner-specs">another</live-example> for the Testing guide's `BannerComponent`.
|
||||
Linked to these plunkers in [Testing](guide/testing#live-examples) and [Setup anatomy](guide/setup-systemjs-anatomy) guides.
|
||||
|
||||
## Internationalization: pluralization and _select_ (2016-11-30)
|
||||
@ -112,15 +112,15 @@ Docs and code samples updated and tested with Angular v.2.2.0.
|
||||
|
||||
## UPDATE: NgUpgrade Guide for the AOT friendly _upgrade/static_ module (2016-11-14)
|
||||
|
||||
The updated [NgUpgrade Guide](guide/upgrade) guide covers the
|
||||
new AOT friendly `upgrade/static` module
|
||||
The updated [NgUpgrade Guide](guide/upgrade) guide covers the
|
||||
new AOT friendly `upgrade/static` module
|
||||
released in v.2.2.0, which is the recommended
|
||||
facility for migrating from AngularJS to Angular.
|
||||
The documentation for the version prior to v.2.2.0 has been removed.
|
||||
|
||||
## ES6 described in "TypeScript to JavaScript" (2016-11-14)
|
||||
|
||||
The updated [TypeScript to JavaScript](guide/ts-to-js) guide
|
||||
The updated [TypeScript to JavaScript](guide/ts-to-js) guide
|
||||
now explains how to write apps in ES6/7
|
||||
by translating the common idioms in the TypeScript documentation examples
|
||||
(and elsewhere on the web) to ES6/7 and ES5.
|
||||
@ -156,7 +156,7 @@ in the `in-memory-web-api` repo.
|
||||
The router can lazily _preload_ modules _after_ the app starts and
|
||||
_before_ the user navigates to them for improved perceived performance.
|
||||
|
||||
New `:enter` and `:leave` aliases make animation more natural.
|
||||
New `:enter` and `:leave` aliases make animation more natural.
|
||||
|
||||
## Sync with Angular v.2.1.0 (2016-10-12)
|
||||
|
||||
@ -176,11 +176,11 @@ Docs and code samples updated and tested with Angular v.2.0.2.
|
||||
## "Routing and Navigation" guide with the _Router Module_ (2016-10-5)
|
||||
|
||||
The [Routing and Navigation](guide/router) guide now locates route configuration
|
||||
in a _Routing Module_.
|
||||
in a _Routing Module_.
|
||||
The _Routing Module_ replaces the previous _routing object_ involving the `ModuleWithProviders`.
|
||||
|
||||
All guided samples with routing use the _Routing Module_ and prose content has been updated,
|
||||
most conspicuously in the
|
||||
most conspicuously in the
|
||||
[NgModule](guide/ngmodule) guide and [NgModule FAQ](guide/ngmodule-faq) guide.
|
||||
|
||||
## New "Internationalization" guide (2016-09-30)
|
||||
@ -194,7 +194,7 @@ Many samples use the `angular-in-memory-web-api` to simulate a remote server.
|
||||
This library is also useful to you during early development before you have a server to talk to.
|
||||
|
||||
The package name was changed from "angular2-in-memory-web-api" which is still frozen-in-time on npm.
|
||||
The new "angular-in-memory-web-api" has new features.
|
||||
The new "angular-in-memory-web-api" has new features.
|
||||
<a href="https://github.com/angular/in-memory-web-api/blob/master/README.md">Read about them on github</a>.
|
||||
|
||||
## "Style Guide" with _NgModules_ (2016-09-27)
|
||||
@ -215,5 +215,5 @@ modules with SystemJS as the samples currently do.
|
||||
|
||||
## "Lifecycle Hooks" guide simplified (2016-09-24)
|
||||
|
||||
The [Lifecycle Hooks](guide/lifecycle-hooks) guide is shorter, simpler, and
|
||||
The [Lifecycle Hooks](guide/lifecycle-hooks) guide is shorter, simpler, and
|
||||
draws more attention to the order in which Angular calls the hooks.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Cheat Sheet
|
||||
<h1 class="no-toc">Cheat Sheet</h1>
|
||||
|
||||
<div id="cheatsheet">
|
||||
<table class="is-full-width is-fixed-layout">
|
||||
@ -23,28 +23,28 @@
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>@<b>NgModule</b>({ declarations: ..., imports: ...,<br> exports: ..., providers: ..., bootstrap: ...})<br>class MyModule {}</code></td>
|
||||
<td><code>@<b>NgModule</b>({ declarations: ..., imports: ...,<br> exports: ..., providers: ..., bootstrap: ...})<br>class MyModule {}</code></td>
|
||||
<td><p>Defines a module that contains components, directives, pipes, and providers.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>declarations:</b> [MyRedComponent, MyBlueComponent, MyDatePipe]</code></td>
|
||||
<td><code><b>declarations:</b> [MyRedComponent, MyBlueComponent, MyDatePipe]</code></td>
|
||||
<td><p>List of components, directives, and pipes that belong to this module.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>imports:</b> [BrowserModule, SomeOtherModule]</code></td>
|
||||
<td><code><b>imports:</b> [BrowserModule, SomeOtherModule]</code></td>
|
||||
<td><p>List of modules to import into this module. Everything from the imported modules
|
||||
is available to <code>declarations</code> of this module.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>exports:</b> [MyRedComponent, MyDatePipe]</code></td>
|
||||
<td><code><b>exports:</b> [MyRedComponent, MyDatePipe]</code></td>
|
||||
<td><p>List of components, directives, and pipes visible to modules that import this module.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>providers:</b> [MyService, { provide: ... }]</code></td>
|
||||
<td><code><b>providers:</b> [MyService, { provide: ... }]</code></td>
|
||||
<td><p>List of dependency injection providers visible both to the contents of this module and to importers of this module.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>bootstrap:</b> [MyAppComponent]</code></td>
|
||||
<td><code><b>bootstrap:</b> [MyAppComponent]</code></td>
|
||||
<td><p>List of components to bootstrap when this module is bootstrapped.</p>
|
||||
</td>
|
||||
</tr>
|
||||
@ -56,61 +56,61 @@ is available to <code>declarations</code> of this module.</p>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><input <b>[value]</b>="firstName"></code></td>
|
||||
<td><code><input <b>[value]</b>="firstName"></code></td>
|
||||
<td><p>Binds property <code>value</code> to the result of expression <code>firstName</code>.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><div <b>[attr.role]</b>="myAriaRole"></code></td>
|
||||
<td><code><div <b>[attr.role]</b>="myAriaRole"></code></td>
|
||||
<td><p>Binds attribute <code>role</code> to the result of expression <code>myAriaRole</code>.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><div <b>[class.extra-sparkle]</b>="isDelightful"></code></td>
|
||||
<td><code><div <b>[class.extra-sparkle]</b>="isDelightful"></code></td>
|
||||
<td><p>Binds the presence of the CSS class <code>extra-sparkle</code> on the element to the truthiness of the expression <code>isDelightful</code>.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><div <b>[style.width.px]</b>="mySize"></code></td>
|
||||
<td><code><div <b>[style.width.px]</b>="mySize"></code></td>
|
||||
<td><p>Binds style property <code>width</code> to the result of expression <code>mySize</code> in pixels. Units are optional.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><button <b>(click)</b>="readRainbow($event)"></code></td>
|
||||
<td><code><button <b>(click)</b>="readRainbow($event)"></code></td>
|
||||
<td><p>Calls method <code>readRainbow</code> when a click event is triggered on this button element (or its children) and passes in the event object.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><div title="Hello <b>{{ponyName}}</b>"></code></td>
|
||||
<td><code><div title="Hello <b>{{ponyName}}</b>"></code></td>
|
||||
<td><p>Binds a property to an interpolated string, for example, "Hello Seabiscuit". Equivalent to:
|
||||
<code><div [title]="'Hello ' + ponyName"></code></p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><p>Hello <b>{{ponyName}}</b></p></code></td>
|
||||
<td><code><p>Hello <b>{{ponyName}}</b></p></code></td>
|
||||
<td><p>Binds text content to an interpolated string, for example, "Hello Seabiscuit".</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><my-cmp <b>[(title)]</b>="name"></code></td>
|
||||
<td><code><my-cmp <b>[(title)]</b>="name"></code></td>
|
||||
<td><p>Sets up two-way data binding. Equivalent to: <code><my-cmp [title]="name" (titleChange)="name=$event"></code></p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><video <b>#movieplayer</b> ...><br> <button <b>(click)</b>="movieplayer.play()"><br></video></code></td>
|
||||
<td><code><video <b>#movieplayer</b> ...><br> <button <b>(click)</b>="movieplayer.play()"><br></video></code></td>
|
||||
<td><p>Creates a local variable <code>movieplayer</code> that provides access to the <code>video</code> element instance in data-binding and event-binding expressions in the current template.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><p <b>*myUnless</b>="myExpression">...</p></code></td>
|
||||
<td><code><p <b>*myUnless</b>="myExpression">...</p></code></td>
|
||||
<td><p>The <code>*</code> symbol turns the current element into an embedded template. Equivalent to:
|
||||
<code><ng-template [myUnless]="myExpression"><p>...</p></ng-template></code></p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><p>Card No.: <b>{{cardNumber | myCardNumberFormatter}}</b></p></code></td>
|
||||
<td><code><p>Card No.: <b>{{cardNumber | myCardNumberFormatter}}</b></p></code></td>
|
||||
<td><p>Transforms the current value of expression <code>cardNumber</code> via the pipe called <code>myCardNumberFormatter</code>.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><p>Employer: <b>{{employer?.companyName}}</b></p></code></td>
|
||||
<td><code><p>Employer: <b>{{employer?.companyName}}</b></p></code></td>
|
||||
<td><p>The safe navigation operator (<code>?</code>) means that the <code>employer</code> field is optional and if <code>undefined</code>, the rest of the expression should be ignored.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><<b>svg:</b>rect x="0" y="0" width="100" height="100"/></code></td>
|
||||
<td><code><<b>svg:</b>rect x="0" y="0" width="100" height="100"/></code></td>
|
||||
<td><p>An SVG snippet template needs an <code>svg:</code> prefix on its root element to disambiguate the SVG element from an HTML component.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><<b>svg</b>><br> <rect x="0" y="0" width="100" height="100"/><br></<b>svg</b>></code></td>
|
||||
<td><code><<b>svg</b>><br> <rect x="0" y="0" width="100" height="100"/><br></<b>svg</b>></code></td>
|
||||
<td><p>An <code><svg></code> root element is detected as an SVG element automatically, without the prefix.</p>
|
||||
</td>
|
||||
</tr>
|
||||
@ -124,19 +124,19 @@ is available to <code>declarations</code> of this module.</p>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><section <b>*ngIf</b>="showSection"></code></td>
|
||||
<td><code><section <b>*ngIf</b>="showSection"></code></td>
|
||||
<td><p>Removes or recreates a portion of the DOM tree based on the <code>showSection</code> expression.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><li <b>*ngFor</b>="let item of list"></code></td>
|
||||
<td><code><li <b>*ngFor</b>="let item of list"></code></td>
|
||||
<td><p>Turns the li element and its contents into a template, and uses that to instantiate a view for each item in list.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><div <b>[ngSwitch]</b>="conditionExpression"><br> <ng-template <b>[<b>ngSwitchCase</b>]</b>="case1Exp">...</ng-template><br> <ng-template <b>ngSwitchCase</b>="case2LiteralString">...</ng-template><br> <ng-template <b>ngSwitchDefault</b>>...</ng-template><br></div></code></td>
|
||||
<td><code><div <b>[ngSwitch]</b>="conditionExpression"><br> <ng-template <b>[<b>ngSwitchCase</b>]</b>="case1Exp">...</ng-template><br> <ng-template <b>ngSwitchCase</b>="case2LiteralString">...</ng-template><br> <ng-template <b>ngSwitchDefault</b>>...</ng-template><br></div></code></td>
|
||||
<td><p>Conditionally swaps the contents of the div by selecting one of the embedded templates based on the current value of <code>conditionExpression</code>.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><div <b>[ngClass]</b>="{'active': isActive, 'disabled': isDisabled}"></code></td>
|
||||
<td><code><div <b>[ngClass]</b>="{'active': isActive, 'disabled': isDisabled}"></code></td>
|
||||
<td><p>Binds the presence of CSS classes on the element to the truthiness of the associated map values. The right-hand expression should return {class-name: true/false} map.</p>
|
||||
</td>
|
||||
</tr>
|
||||
@ -150,7 +150,7 @@ is available to <code>declarations</code> of this module.</p>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><input <b>[(ngModel)]</b>="userName"></code></td>
|
||||
<td><code><input <b>[(ngModel)]</b>="userName"></code></td>
|
||||
<td><p>Provides two-way data-binding, parsing, and validation for form controls.</p>
|
||||
</td>
|
||||
</tr>
|
||||
@ -164,19 +164,19 @@ is available to <code>declarations</code> of this module.</p>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><b>@Component({...})</b><br>class MyComponent() {}</code></td>
|
||||
<td><code><b>@Component({...})</b><br>class MyComponent() {}</code></td>
|
||||
<td><p>Declares that a class is a component and provides metadata about the component.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@Directive({...})</b><br>class MyDirective() {}</code></td>
|
||||
<td><code><b>@Directive({...})</b><br>class MyDirective() {}</code></td>
|
||||
<td><p>Declares that a class is a directive and provides metadata about the directive.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@Pipe({...})</b><br>class MyPipe() {}</code></td>
|
||||
<td><code><b>@Pipe({...})</b><br>class MyPipe() {}</code></td>
|
||||
<td><p>Declares that a class is a pipe and provides metadata about the pipe.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@Injectable()</b><br>class MyService() {}</code></td>
|
||||
<td><code><b>@Injectable()</b><br>class MyService() {}</code></td>
|
||||
<td><p>Declares that a class has dependencies that should be injected into the constructor when the dependency injector is creating an instance of this class.
|
||||
</p>
|
||||
</td>
|
||||
@ -191,13 +191,13 @@ is available to <code>declarations</code> of this module.</p>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><b>selector:</b> '.cool-button:not(a)'</code></td>
|
||||
<td><code><b>selector:</b> '.cool-button:not(a)'</code></td>
|
||||
<td><p>Specifies a CSS selector that identifies this directive within a template. Supported selectors include <code>element</code>,
|
||||
<code>[attribute]</code>, <code>.class</code>, and <code>:not()</code>.</p>
|
||||
<p>Does not support parent-child relationship selectors.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>providers:</b> [MyService, { provide: ... }]</code></td>
|
||||
<td><code><b>providers:</b> [MyService, { provide: ... }]</code></td>
|
||||
<td><p>List of dependency injection providers for this directive and its children.</p>
|
||||
</td>
|
||||
</tr>
|
||||
@ -212,19 +212,19 @@ so the <code>@Directive</code> configuration applies to components as well</p>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><b>moduleId:</b> module.id</code></td>
|
||||
<td><code><b>moduleId:</b> module.id</code></td>
|
||||
<td><p>If set, the <code>templateUrl</code> and <code>styleUrl</code> are resolved relative to the component.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>viewProviders:</b> [MyService, { provide: ... }]</code></td>
|
||||
<td><code><b>viewProviders:</b> [MyService, { provide: ... }]</code></td>
|
||||
<td><p>List of dependency injection providers scoped to this component's view.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>template:</b> 'Hello {{name}}'<br><b>templateUrl:</b> 'my-component.html'</code></td>
|
||||
<td><code><b>template:</b> 'Hello {{name}}'<br><b>templateUrl:</b> 'my-component.html'</code></td>
|
||||
<td><p>Inline template or external template URL of the component's view.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>styles:</b> ['.primary {color: red}']<br><b>styleUrls:</b> ['my-component.css']</code></td>
|
||||
<td><code><b>styles:</b> ['.primary {color: red}']<br><b>styleUrls:</b> ['my-component.css']</code></td>
|
||||
<td><p>List of inline CSS styles or external stylesheet URLs for styling the component’s view.</p>
|
||||
</td>
|
||||
</tr>
|
||||
@ -238,36 +238,36 @@ so the <code>@Directive</code> configuration applies to components as well</p>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><b>@Input()</b> myProperty;</code></td>
|
||||
<td><code><b>@Input()</b> myProperty;</code></td>
|
||||
<td><p>Declares an input property that you can update via property binding (example:
|
||||
<code><my-cmp [myProperty]="someExpression"></code>).</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@Output()</b> myEvent = new EventEmitter();</code></td>
|
||||
<td><code><b>@Output()</b> myEvent = new EventEmitter();</code></td>
|
||||
<td><p>Declares an output property that fires events that you can subscribe to with an event binding (example: <code><my-cmp (myEvent)="doSomething()"></code>).</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@HostBinding('class.valid')</b> isValid;</code></td>
|
||||
<td><code><b>@HostBinding('class.valid')</b> isValid;</code></td>
|
||||
<td><p>Binds a host element property (here, the CSS class <code>valid</code>) to a directive/component property (<code>isValid</code>).</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@HostListener('click', ['$event'])</b> onClick(e) {...}</code></td>
|
||||
<td><code><b>@HostListener('click', ['$event'])</b> onClick(e) {...}</code></td>
|
||||
<td><p>Subscribes to a host element event (<code>click</code>) with a directive/component method (<code>onClick</code>), optionally passing an argument (<code>$event</code>).</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@ContentChild(myPredicate)</b> myChildComponent;</code></td>
|
||||
<td><code><b>@ContentChild(myPredicate)</b> myChildComponent;</code></td>
|
||||
<td><p>Binds the first result of the component content query (<code>myPredicate</code>) to a property (<code>myChildComponent</code>) of the class.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@ContentChildren(myPredicate)</b> myChildComponents;</code></td>
|
||||
<td><code><b>@ContentChildren(myPredicate)</b> myChildComponents;</code></td>
|
||||
<td><p>Binds the results of the component content query (<code>myPredicate</code>) to a property (<code>myChildComponents</code>) of the class.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@ViewChild(myPredicate)</b> myChildComponent;</code></td>
|
||||
<td><code><b>@ViewChild(myPredicate)</b> myChildComponent;</code></td>
|
||||
<td><p>Binds the first result of the component view query (<code>myPredicate</code>) to a property (<code>myChildComponent</code>) of the class. Not available for directives.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>@ViewChildren(myPredicate)</b> myChildComponents;</code></td>
|
||||
<td><code><b>@ViewChildren(myPredicate)</b> myChildComponents;</code></td>
|
||||
<td><p>Binds the results of the component view query (<code>myPredicate</code>) to a property (<code>myChildComponents</code>) of the class. Not available for directives.</p>
|
||||
</td>
|
||||
</tr>
|
||||
@ -281,39 +281,39 @@ so the <code>@Directive</code> configuration applies to components as well</p>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><b>constructor(myService: MyService, ...)</b> { ... }</code></td>
|
||||
<td><code><b>constructor(myService: MyService, ...)</b> { ... }</code></td>
|
||||
<td><p>Called before any other lifecycle hook. Use it to inject dependencies, but avoid any serious work here.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>ngOnChanges(changeRecord)</b> { ... }</code></td>
|
||||
<td><code><b>ngOnChanges(changeRecord)</b> { ... }</code></td>
|
||||
<td><p>Called after every change to input properties and before processing content or child views.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>ngOnInit()</b> { ... }</code></td>
|
||||
<td><code><b>ngOnInit()</b> { ... }</code></td>
|
||||
<td><p>Called after the constructor, initializing input properties, and the first call to <code>ngOnChanges</code>.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>ngDoCheck()</b> { ... }</code></td>
|
||||
<td><code><b>ngDoCheck()</b> { ... }</code></td>
|
||||
<td><p>Called every time that the input properties of a component or a directive are checked. Use it to extend change detection by performing a custom check.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>ngAfterContentInit()</b> { ... }</code></td>
|
||||
<td><code><b>ngAfterContentInit()</b> { ... }</code></td>
|
||||
<td><p>Called after <code>ngOnInit</code> when the component's or directive's content has been initialized.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>ngAfterContentChecked()</b> { ... }</code></td>
|
||||
<td><code><b>ngAfterContentChecked()</b> { ... }</code></td>
|
||||
<td><p>Called after every check of the component's or directive's content.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>ngAfterViewInit()</b> { ... }</code></td>
|
||||
<td><code><b>ngAfterViewInit()</b> { ... }</code></td>
|
||||
<td><p>Called after <code>ngAfterContentInit</code> when the component's view has been initialized. Applies to components only.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>ngAfterViewChecked()</b> { ... }</code></td>
|
||||
<td><code><b>ngAfterViewChecked()</b> { ... }</code></td>
|
||||
<td><p>Called after every check of the component's view. Applies to components only.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><b>ngOnDestroy()</b> { ... }</code></td>
|
||||
<td><code><b>ngOnDestroy()</b> { ... }</code></td>
|
||||
<td><p>Called once, before the instance is destroyed.</p>
|
||||
</td>
|
||||
</tr>
|
||||
@ -325,15 +325,15 @@ so the <code>@Directive</code> configuration applies to components as well</p>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>{ <b>provide</b>: MyService, <b>useClass</b>: MyMockService }</code></td>
|
||||
<td><code>{ <b>provide</b>: MyService, <b>useClass</b>: MyMockService }</code></td>
|
||||
<td><p>Sets or overrides the provider for <code>MyService</code> to the <code>MyMockService</code> class.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code>{ <b>provide</b>: MyService, <b>useFactory</b>: myFactory }</code></td>
|
||||
<td><code>{ <b>provide</b>: MyService, <b>useFactory</b>: myFactory }</code></td>
|
||||
<td><p>Sets or overrides the provider for <code>MyService</code> to the <code>myFactory</code> factory function.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code>{ <b>provide</b>: MyValue, <b>useValue</b>: 41 }</code></td>
|
||||
<td><code>{ <b>provide</b>: MyValue, <b>useValue</b>: 41 }</code></td>
|
||||
<td><p>Sets or overrides the provider for <code>MyValue</code> to the value <code>41</code>.</p>
|
||||
</td>
|
||||
</tr>
|
||||
@ -347,39 +347,39 @@ so the <code>@Directive</code> configuration applies to components as well</p>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>const routes: <b>Routes</b> = [<br> { path: '', component: HomeComponent },<br> { path: 'path/:routeParam', component: MyComponent },<br> { path: 'staticPath', component: ... },<br> { path: '**', component: ... },<br> { path: 'oldPath', redirectTo: '/staticPath' },<br> { path: ..., component: ..., data: { message: 'Custom' } }<br>]);<br><br>const routing = RouterModule.forRoot(routes);</code></td>
|
||||
<td><code>const routes: <b>Routes</b> = [<br> { path: '', component: HomeComponent },<br> { path: 'path/:routeParam', component: MyComponent },<br> { path: 'staticPath', component: ... },<br> { path: '**', component: ... },<br> { path: 'oldPath', redirectTo: '/staticPath' },<br> { path: ..., component: ..., data: { message: 'Custom' } }<br>]);<br><br>const routing = RouterModule.forRoot(routes);</code></td>
|
||||
<td><p>Configures routes for the application. Supports static, parameterized, redirect, and wildcard routes. Also supports custom route data and resolve.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><br><<b>router-outlet</b>></<b>router-outlet</b>><br><<b>router-outlet</b> name="aux"></<b>router-outlet</b>><br></code></td>
|
||||
<td><code><br><<b>router-outlet</b>></<b>router-outlet</b>><br><<b>router-outlet</b> name="aux"></<b>router-outlet</b>><br></code></td>
|
||||
<td><p>Marks the location to load the component of the active route.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><br><a routerLink="/path"><br><a <b>[routerLink]</b>="[ '/path', routeParam ]"><br><a <b>[routerLink]</b>="[ '/path', { matrixParam: 'value' } ]"><br><a <b>[routerLink]</b>="[ '/path' ]" [queryParams]="{ page: 1 }"><br><a <b>[routerLink]</b>="[ '/path' ]" fragment="anchor"><br></code></td>
|
||||
<td><code><br><a routerLink="/path"><br><a <b>[routerLink]</b>="[ '/path', routeParam ]"><br><a <b>[routerLink]</b>="[ '/path', { matrixParam: 'value' } ]"><br><a <b>[routerLink]</b>="[ '/path' ]" [queryParams]="{ page: 1 }"><br><a <b>[routerLink]</b>="[ '/path' ]" fragment="anchor"><br></code></td>
|
||||
<td><p>Creates a link to a different view based on a route instruction consisting of a route path, required and optional parameters, query parameters, and a fragment. To navigate to a root route, use the <code>/</code> prefix; for a child route, use the <code>./</code>prefix; for a sibling or parent, use the <code>../</code> prefix.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code><a [routerLink]="[ '/path' ]" routerLinkActive="active"></code></td>
|
||||
<td><code><a [routerLink]="[ '/path' ]" routerLinkActive="active"></code></td>
|
||||
<td><p>The provided classes are added to the element when the <code>routerLink</code> becomes the current active route.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code>class <b>CanActivate</b>Guard implements <b>CanActivate</b> {<br> canActivate(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<boolean>|Promise<boolean>|boolean { ... }<br>}<br><br>{ path: ..., canActivate: [<b>CanActivate</b>Guard] }</code></td>
|
||||
<td><code>class <b>CanActivate</b>Guard implements <b>CanActivate</b> {<br> canActivate(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<boolean>|Promise<boolean>|boolean { ... }<br>}<br><br>{ path: ..., canActivate: [<b>CanActivate</b>Guard] }</code></td>
|
||||
<td><p>An interface for defining a class that the router should call first to determine if it should activate this component. Should return a boolean or an Observable/Promise that resolves to a boolean.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code>class <b>CanDeactivate</b>Guard implements <b>CanDeactivate</b><T> {<br> canDeactivate(<br> component: T,<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<boolean>|Promise<boolean>|boolean { ... }<br>}<br><br>{ path: ..., canDeactivate: [<b>CanDeactivate</b>Guard] }</code></td>
|
||||
<td><code>class <b>CanDeactivate</b>Guard implements <b>CanDeactivate</b><T> {<br> canDeactivate(<br> component: T,<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<boolean>|Promise<boolean>|boolean { ... }<br>}<br><br>{ path: ..., canDeactivate: [<b>CanDeactivate</b>Guard] }</code></td>
|
||||
<td><p>An interface for defining a class that the router should call first to determine if it should deactivate this component after a navigation. Should return a boolean or an Observable/Promise that resolves to a boolean.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code>class <b>CanActivateChild</b>Guard implements <b>CanActivateChild</b> {<br> canActivateChild(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<boolean>|Promise<boolean>|boolean { ... }<br>}<br><br>{ path: ..., canActivateChild: [CanActivateGuard],<br> children: ... }</code></td>
|
||||
<td><code>class <b>CanActivateChild</b>Guard implements <b>CanActivateChild</b> {<br> canActivateChild(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<boolean>|Promise<boolean>|boolean { ... }<br>}<br><br>{ path: ..., canActivateChild: [CanActivateGuard],<br> children: ... }</code></td>
|
||||
<td><p>An interface for defining a class that the router should call first to determine if it should activate the child route. Should return a boolean or an Observable/Promise that resolves to a boolean.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code>class <b>Resolve</b>Guard implements <b>Resolve</b><T> {<br> resolve(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<any>|Promise<any>|any { ... }<br>}<br><br>{ path: ..., resolve: [<b>Resolve</b>Guard] }</code></td>
|
||||
<td><code>class <b>Resolve</b>Guard implements <b>Resolve</b><T> {<br> resolve(<br> route: ActivatedRouteSnapshot,<br> state: RouterStateSnapshot<br> ): Observable<any>|Promise<any>|any { ... }<br>}<br><br>{ path: ..., resolve: [<b>Resolve</b>Guard] }</code></td>
|
||||
<td><p>An interface for defining a class that the router should call first to resolve route data before rendering the route. Should return a value or an Observable/Promise that resolves to a value.</p>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td><code>class <b>CanLoad</b>Guard implements <b>CanLoad</b> {<br> canLoad(<br> route: Route<br> ): Observable<boolean>|Promise<boolean>|boolean { ... }<br>}<br><br>{ path: ..., canLoad: [<b>CanLoad</b>Guard], loadChildren: ... }</code></td>
|
||||
<td><code>class <b>CanLoad</b>Guard implements <b>CanLoad</b> {<br> canLoad(<br> route: Route<br> ): Observable<boolean>|Promise<boolean>|boolean { ... }<br>}<br><br>{ path: ..., canLoad: [<b>CanLoad</b>Guard], loadChildren: ... }</code></td>
|
||||
<td><p>An interface for defining a class that the router should call first to check if the lazy loaded module should be loaded. Should return a boolean or an Observable/Promise that resolves to a boolean.</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -50,7 +50,10 @@ The `get()` method on `HttpClient` makes accessing this data straightforward.
|
||||
|
||||
```javascript
|
||||
@Component(...)
|
||||
export class MyComponent implements NgOnInit {
|
||||
export class MyComponent implements OnInit {
|
||||
|
||||
results: string[];
|
||||
|
||||
// Inject HttpClient into your component or service.
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
@ -268,12 +271,12 @@ has a single `intercept()` method. Here is a simple interceptor which does nothi
|
||||
|
||||
```javascript
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest) from '@angular/common/http';
|
||||
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';
|
||||
|
||||
@Injectable()
|
||||
export class NoopInterceptor implements HttpInterceptor {
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
return next.handle(Req);
|
||||
return next.handle(req);
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -349,7 +352,7 @@ A common use of interceptors is to set default headers on outgoing responses. Fo
|
||||
|
||||
```javascript
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest) from '@angular/common/http';
|
||||
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';
|
||||
|
||||
@Injectable()
|
||||
export class AuthInterceptor implements HttpInterceptor {
|
||||
@ -357,7 +360,7 @@ export class AuthInterceptor implements HttpInterceptor {
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
// Get the auth header from the service.
|
||||
const authHeader: this.auth.getAuthorizationHeader();
|
||||
const authHeader = this.auth.getAuthorizationHeader();
|
||||
// Clone the request to add the new header.
|
||||
const authReq = req.clone({headers: req.headers.set('Authorization', authHeader)});
|
||||
// Pass on the cloned request instead of the original request.
|
||||
@ -389,12 +392,12 @@ export class TimingInterceptor implements HttpInterceptor {
|
||||
constructor(private auth: AuthService) {}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
const elapsed = Date.now();
|
||||
const started = Date.now();
|
||||
return next
|
||||
.handle(req)
|
||||
.do(event => {
|
||||
if (event instanceof HttpResponse) {
|
||||
const time = Date.now() - started;
|
||||
const elapsed = Date.now() - started;
|
||||
console.log(`Request for ${req.urlWithParams} took ${elapsed} ms.`);
|
||||
}
|
||||
});
|
||||
@ -598,7 +601,7 @@ it('expects a GET request', inject([HttpClient, HttpTestingController], (http: H
|
||||
|
||||
// At this point, the request is pending, and no response has been
|
||||
// sent. The next step is to expect that the request happened.
|
||||
const req = httpMock.expectOne('/test');
|
||||
const req = httpMock.expectOne('/data');
|
||||
|
||||
// If no request with that URL was made, or if multiple requests match,
|
||||
// expectOne() would throw. However this test makes only one request to
|
||||
@ -611,7 +614,7 @@ it('expects a GET request', inject([HttpClient, HttpTestingController], (http: H
|
||||
req.flush({name: 'Test Data'});
|
||||
|
||||
// Finally, assert that there are no outstanding requests.
|
||||
mockHttp.verify();
|
||||
httpMock.verify();
|
||||
}));
|
||||
```
|
||||
|
||||
@ -619,7 +622,7 @@ The last step, verifying that no requests remain outstanding, is common enough f
|
||||
|
||||
```javascript
|
||||
afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => {
|
||||
mockHttp.verify();
|
||||
httpMock.verify();
|
||||
}));
|
||||
```
|
||||
|
||||
@ -628,7 +631,7 @@ afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => {
|
||||
If matching by URL isn't sufficient, it's possible to implement your own matching function. For example, you could look for an outgoing request that has an Authorization header:
|
||||
|
||||
```javascript
|
||||
const req = mockHttp.expectOne((req) => req.headers.has('Authorization'));
|
||||
const req = httpMock.expectOne((req) => req.headers.has('Authorization'));
|
||||
```
|
||||
|
||||
Just as with the `expectOne()` by URL in the test above, if 0 or 2+ requests match this expectation, it will throw.
|
||||
@ -639,7 +642,7 @@ If you need to respond to duplicate requests in your test, use the `match()` API
|
||||
|
||||
```javascript
|
||||
// Expect that 5 pings have been made and flush them.
|
||||
const reqs = mockHttp.match('/ping');
|
||||
const reqs = httpMock.match('/ping');
|
||||
expect(reqs.length).toBe(5);
|
||||
reqs.forEach(req => req.flush());
|
||||
```
|
||||
|
@ -397,8 +397,7 @@ created under test or before you decide to display it.
|
||||
Constructors should do no more than set the initial local variables to simple values.
|
||||
|
||||
An `ngOnInit()` is a good place for a component to fetch its initial data. The
|
||||
[Tour of Heroes Tutorial](tutorial/toh-pt4#oninit) and [HTTP Client](guide/http#oninit)
|
||||
guides show how.
|
||||
[Tour of Heroes Tutorial](tutorial/toh-pt4#oninit) guide shows how.
|
||||
|
||||
|
||||
Remember also that a directive's data-bound input properties are not set until _after construction_.
|
||||
|
@ -163,7 +163,6 @@ without waiting for Angular updates.
|
||||
***angular-in-memory-web-api***: An Angular-supported library that simulates a remote server's web api
|
||||
without requiring an actual server or real HTTP calls.
|
||||
Good for demos, samples, and early stage development (before you even have a server).
|
||||
Read about it in the [HTTP Client](guide/http#in-mem-web-api) page.
|
||||
|
||||
***bootstrap***: [Bootstrap](http://getbootstrap.com/) is a popular HTML and CSS framework for designing responsive web apps.
|
||||
Some of the samples improve their appearance with *bootstrap*.
|
||||
|
@ -255,11 +255,11 @@ During each navigation, the `Router` emits navigation events through the `Router
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<code>RouteConfigLoadStart</code>
|
||||
<code>RouteConfigLoadEnd</code>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
An [event](api/router/RouteConfigLoadStart) triggered after a route has been lazy loaded.
|
||||
An [event](api/router/RouteConfigLoadEnd) triggered after a route has been lazy loaded.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -178,7 +178,7 @@ For a discussion of the unit testing setup files, [see below](guide/testing#setu
|
||||
{@a isolated-v-testing-utilities}
|
||||
|
||||
|
||||
### Isolated unit tests vs. the Angular testing utilites
|
||||
### Isolated unit tests vs. the Angular testing utilities
|
||||
|
||||
[Isolated unit tests](guide/testing#isolated-unit-tests "Unit testing without the Angular testing utilities")
|
||||
examine an instance of a class all by itself without any dependence on Angular or any injected values.
|
||||
|
@ -247,12 +247,10 @@ next to the original _ES5_ version for comparison:
|
||||
</code-pane>
|
||||
</code-tabs>
|
||||
|
||||
{@a name-constructor}
|
||||
|
||||
<div class="callout is-helpful">
|
||||
|
||||
{@a name-constructor}
|
||||
|
||||
### Name the constructor
|
||||
|
||||
A **named** constructor displays clearly in the console log
|
||||
if the component throws a runtime error.
|
||||
An **unnamed** constructor displays as an anonymous function, for example, `class0`,
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class="feature-section">
|
||||
<div class="feature-header">
|
||||
<div class="text-headline">Cross Platform</div>
|
||||
<img src="../assets/images/icons/feature-icon.svg" height="70px">
|
||||
<img src="assets/images/icons/feature-icon.svg" height="70px">
|
||||
</div>
|
||||
<div class="feature-row">
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
<div class="feature-section">
|
||||
<div class="feature-header">
|
||||
<div class="text-headline">Speed and Performance</div>
|
||||
<img src="../assets/images/icons/feature-icon.svg" height="70px">
|
||||
<img src="assets/images/icons/feature-icon.svg" height="70px">
|
||||
</div>
|
||||
<div class="feature-row">
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
<div class="feature-section">
|
||||
<div class="feature-header">
|
||||
<div class="text-headline">Productivity</div>
|
||||
<img src="../assets/images/icons/feature-icon.svg" height="70px">
|
||||
<img src="assets/images/icons/feature-icon.svg" height="70px">
|
||||
</div>
|
||||
<div class="feature-row">
|
||||
|
||||
@ -84,7 +84,7 @@
|
||||
<div class="feature-section">
|
||||
<div class="feature-header">
|
||||
<div class="text-headline">Full Development Story</div>
|
||||
<img src="../assets/images/icons/feature-icon.svg" height="70px">
|
||||
<img src="assets/images/icons/feature-icon.svg" height="70px">
|
||||
</div>
|
||||
<div class="feature-row">
|
||||
|
||||
|
@ -110,7 +110,7 @@
|
||||
|
||||
<a href="guide/quickstart">
|
||||
<div class="card">
|
||||
<img src="../assets/images/icons/code-icon.svg" height="70px">
|
||||
<img src="assets/images/icons/code-icon.svg" height="70px">
|
||||
<div class="card-text-container">
|
||||
<div class="text-headline">Get Started</div>
|
||||
<p>Start building your Angular application.</p>
|
||||
|
@ -1,116 +0,0 @@
|
||||
<header class="hero background-sky">
|
||||
<h1 class="hero-title no-toc">News</h1>
|
||||
<div class="clear"></div>
|
||||
</header>
|
||||
<artice>
|
||||
<div class="grid-fluid l-space-bottom-2">
|
||||
<div class="c12 text-center"><h3 class="text-headline text-uppercase"> Core Team</h3></div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
<div class="grid-fluid">
|
||||
<div class="c6">
|
||||
<div class="article-card">
|
||||
<div class="date">Oct 12, 2016</div>
|
||||
<div class="title"><a target="_blank"
|
||||
href="http://angularjs.blogspot.com/2016/10/angular-210-now-available.html">Angular
|
||||
2.1.0 Now Available</a></div>
|
||||
<p>Angular version 2.1.0 - incremental-metamorphosis - is a minor release following our
|
||||
announced adoption of Semantic Versioning...</p>
|
||||
<div class="author"><img src="generated/images/bios/stephenfluin.jpg">
|
||||
<div class="posted">Posted by <b>Stephen Fluin</b></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="c6">
|
||||
<div class="article-card">
|
||||
<div class="date">Oct 7, 2016</div>
|
||||
<div class="title"><a target="_blank"
|
||||
href="http://angularjs.blogspot.com/2016/10/versioning-and-releasing-angular.html">Versioning
|
||||
and Releasing Angular</a></div>
|
||||
<p>In order for the ecosystem around Angular to thrive, developers need stability from the
|
||||
Angular framework so that reusable components and libraries, tools and learned practices
|
||||
don’t go obsolete unexpectedly...</p>
|
||||
<div class="author"><img src="generated/images/bios/igor-minar.jpg">
|
||||
<div class="posted">Posted by <b>Igor Minar</b></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-fluid l-space-bottom-2 l-space-top-4">
|
||||
<div class="c12 text-center"><h3 class="text-headline text-uppercase"> Developer Community</h3>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
<div class="grid-fluid">
|
||||
<div class="c6">
|
||||
<div class="article-card">
|
||||
<div class="date">Oct 30, 2016</div>
|
||||
<div class="title"><a target="_blank"
|
||||
href="https://www.thepolyglotdeveloper.com/2016/10/use-pre-populated-sqlite-database-nativescript-angular-2/">Use
|
||||
A Pre-Populated SQLite Database With NativeScript And Angular 2</a></div>
|
||||
<p>I figured it would be a good idea to demonstrate how to ship a NativeScript Angular 2
|
||||
application with a pre-filled SQLite database rather than populating it on-the-fly....</p>
|
||||
<div class="author"><img src="generated/images/bios/shield-bio-placeholder.png">
|
||||
<div class="posted">Posted by <b>Nic Raboy</b></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="c6">
|
||||
<div class="article-card">
|
||||
<div class="date">Oct 13, 2016</div>
|
||||
<div class="title"><a target="_blank"
|
||||
href="http://blog.thoughtram.io/angular/2016/10/13/two-way-data-binding-in-angular-2.html">Two-way
|
||||
Data Binding in Angular 2</a></div>
|
||||
<p>If there was one feature in Angular that made us go “Wow”, then it was probably its
|
||||
two-way data binding system. Changes in the application state have been automagically
|
||||
reflected into the view...</p>
|
||||
<div class="author"><img src="generated/images/bios/angular-gde-bio-placeholder.png">
|
||||
<div class="posted">Posted by <b>Pascal Precht</b></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-fluid">
|
||||
<div class="c6">
|
||||
<div class="article-card">
|
||||
<div class="date">Oct 10, 2016</div>
|
||||
<div class="title"><a target="_blank"
|
||||
href="http://www.creativebloq.com/how-to/build-a-material-design-app-with-angular-2">Build
|
||||
a Material Design app with Angular 2</a></div>
|
||||
<p>This walkthrough reveals how to create a DialogComponent and to-do app with Angular
|
||||
Material and the Angular CLI...</p>
|
||||
<div class="author"><img src="generated/images/bios/shield-bio-placeholder.png">
|
||||
<div class="posted">Posted by <b>Daniel Zen</b></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="c6">
|
||||
<div class="article-card">
|
||||
<div class="date">Sept 30, 2016</div>
|
||||
<div class="title"><a target="_blank"
|
||||
href="http://www.simb.co/angular-cli-using-docker/?platform=hootsuite">Using
|
||||
Angular CLI to create Angular 2 applications in Docker</a></div>
|
||||
<p>Angular CLI is a great tool for developing Angular 2 applications. I thought it would be
|
||||
fun to do a quick demo...</p>
|
||||
<div class="author"><img src="generated/images/bios/shield-bio-placeholder.png">
|
||||
<div class="posted">Posted by <b>Simeon Bateman</b></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-fluid l-space-bottom-2 l-space-top-4">
|
||||
<div class="c12 text-center"><h3 class="text-headline text-uppercase">Twitter</h3></div>
|
||||
<div class="clear"></div>
|
||||
<div class="grid-fluid">
|
||||
<div class="c3"><p></p></div>
|
||||
<div class="c6">
|
||||
<div class="article-card">
|
||||
<div class="title"><a href="http://twitter.com/angularjs" data-show-count="false"
|
||||
class="twitter-follow-button">Follow @angularjs</a></div>
|
||||
<p><a class="twitter-timeline" data-chrome="nofooter noborders noheader"
|
||||
href="http://twitter.com/angularjs" data-widget-id="700150278465523713"></a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
@ -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
|
||||
skip it when you have a real web API server.
|
||||
|
||||
Read more about the in-memory web API in the
|
||||
[Appendix: Tour of Heroes in-memory web api](guide/http#in-mem-web-api)
|
||||
section of the [HTTP Client](guide/http#in-mem-web-api) page.
|
||||
|
||||
</div>
|
||||
div>
|
||||
|
||||
## Heroes and HTTP
|
||||
|
||||
|
@ -81,7 +81,7 @@
|
||||
"concurrently": "^3.4.0",
|
||||
"cross-spawn": "^5.1.0",
|
||||
"dgeni": "^0.4.7",
|
||||
"dgeni-packages": "^0.20.0-rc.5",
|
||||
"dgeni-packages": "^0.20.0-rc.6",
|
||||
"entities": "^1.1.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
|
@ -45,15 +45,20 @@ timestamp=$(date +%s)
|
||||
payloadData="$payloadData\"timestamp\": $timestamp, "
|
||||
|
||||
# Add change source: application, dependencies, or 'application+dependencies'
|
||||
applicationChanges=$(git diff --name-only $TRAVIS_COMMIT_RANGE $parentDir | grep -v ${parentDir}/content | grep -v ${parentDir}/yarn.lock | wc -l)
|
||||
dependencyChanges=$(git diff --name-only $TRAVIS_COMMIT_RANGE ${parentDir}/yarn.lock | wc -l)
|
||||
yarnChanged=false
|
||||
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
|
||||
change='dependencies'
|
||||
elif [[ $dependencyChanges -eq 1 ]] && [[ $applicationChanges -gt 0 ]]; then
|
||||
elif [[ $allChangedFiles -gt 1 ]] && [[ "$yarnChanged" = true ]]; then
|
||||
change='application+dependencies'
|
||||
elif [[ $applicationChanges -gt 0 ]]; then
|
||||
elif [[ $allChangedFiles -gt 0 ]]; then
|
||||
change='application'
|
||||
else
|
||||
# Nothing changed in aio/
|
||||
|
@ -48,7 +48,7 @@ export class AppComponent implements OnInit {
|
||||
* the styling of individual pages.
|
||||
* You will get three classes:
|
||||
*
|
||||
* * `page-...`: computed from the current document id (e.g. news, guide-security, tutorial-toh-pt2)
|
||||
* * `page-...`: computed from the current document id (e.g. events, guide-security, tutorial-toh-pt2)
|
||||
* * `folder-...`: computed from the top level folder for an id (e.g. guide, tutorial, etc)
|
||||
* * `view-...`: computef from the navigation view (e.g. SideNav, TopBar, etc)
|
||||
*/
|
||||
|
@ -65,6 +65,13 @@ describe('CodeExampleComponent', () => {
|
||||
expect(actual).toBe('Great Example');
|
||||
});
|
||||
|
||||
it('should remove the `title` attribute after initialisation', () => {
|
||||
TestBed.overrideComponent(HostComponent, {
|
||||
set: {template: '<code-example title="Great Example"></code-example>'}});
|
||||
createComponent(oneLineCode);
|
||||
expect(codeExampleDe.nativeElement.getAttribute('title')).toEqual(null);
|
||||
});
|
||||
|
||||
it('should pass hideCopy to CodeComonent', () => {
|
||||
TestBed.overrideComponent(HostComponent, {
|
||||
set: {template: '<code-example hideCopy="true"></code-example>'}});
|
||||
|
@ -45,6 +45,8 @@ export class CodeExampleComponent implements OnInit {
|
||||
this.path = element.getAttribute('path') || '';
|
||||
this.region = element.getAttribute('region') || '';
|
||||
this.title = element.getAttribute('title') || '';
|
||||
// Now remove the title attribute to prevent unwanted tooltip popups when hovering over the code.
|
||||
element.removeAttribute('title');
|
||||
|
||||
this.isAvoid = this.path.indexOf('.avoid.') !== -1;
|
||||
this.hideCopy = this.isAvoid || getBoolFromAttribute(element, ['hidecopy', 'hide-copy']);
|
||||
|
@ -17,6 +17,7 @@ aio-shell.page-docs {
|
||||
|
||||
.sidenav-content {
|
||||
min-height: 450px;
|
||||
padding: 80px 1rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,11 @@ aio-code pre {
|
||||
}
|
||||
}
|
||||
|
||||
.code-tab-group div.mat-tab-body-content {
|
||||
.code-tab-group .mat-tab-label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.code-tab-group .mat-tab-body-content {
|
||||
height: auto;
|
||||
transform: none;
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ table {
|
||||
background-color: $lightgray;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
th {
|
||||
@ -80,6 +80,35 @@ table {
|
||||
}
|
||||
}
|
||||
|
||||
#cheatsheet table tbody td {
|
||||
overflow: auto;
|
||||
}
|
||||
#cheatsheet {
|
||||
|
||||
table tbody td {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 990px) {
|
||||
|
||||
/* Force table to not be like tables anymore */
|
||||
table, thead, tbody, tfoot, tr, th, td {
|
||||
display: block;
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
|
||||
code {
|
||||
padding: 0;
|
||||
background-color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
th, td {
|
||||
&:not(:last-child) {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,14 +20,16 @@ module.exports = function addImageDimensions(getImageDimensions) {
|
||||
const src = props.src;
|
||||
if (!src) {
|
||||
file.message('Missing src in image tag `' + source(node, file) + '`');
|
||||
} else if (props.width === undefined && props.height === undefined) {
|
||||
} else {
|
||||
try {
|
||||
const dimensions = getImageDimensions(addImageDimensionsImpl.basePath, src);
|
||||
props.width = '' + dimensions.width;
|
||||
props.height = '' + dimensions.height;
|
||||
if (props.width === undefined && props.height === undefined) {
|
||||
props.width = '' + dimensions.width;
|
||||
props.height = '' + dimensions.height;
|
||||
}
|
||||
} catch(e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
file.message('Unable to load src in image tag `' + source(node, file) + '`');
|
||||
file.fail('Unable to load src in image tag `' + source(node, file) + '`');
|
||||
} else {
|
||||
file.fail(e.message);
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ describe('addImageDimensions post-processor', () => {
|
||||
expect(log.warn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should log a warning for images whose source cannot be loaded', () => {
|
||||
it('should fail for images whose source cannot be loaded', () => {
|
||||
getImageDimensionsSpy.and.callFake(() => {
|
||||
const error = new Error('no such file or directory');
|
||||
error.code = 'ENOENT';
|
||||
@ -68,13 +68,8 @@ describe('addImageDimensions post-processor', () => {
|
||||
docType: 'a',
|
||||
renderedContent: '<img src="missing">'
|
||||
}];
|
||||
processor.$process(docs);
|
||||
expect(() => processor.$process(docs)).toThrowError('Unable to load src in image tag `<img src="missing">` - doc (a) ');
|
||||
expect(getImageDimensionsSpy).toHaveBeenCalled();
|
||||
expect(docs).toEqual([jasmine.objectContaining({
|
||||
docType: 'a',
|
||||
renderedContent: '<img src="missing">'
|
||||
})]);
|
||||
expect(log.warn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should ignore images with width or height attributes', () => {
|
||||
@ -87,7 +82,6 @@ describe('addImageDimensions post-processor', () => {
|
||||
`
|
||||
}];
|
||||
processor.$process(docs);
|
||||
expect(getImageDimensionsSpy).not.toHaveBeenCalled();
|
||||
expect(docs).toEqual([jasmine.objectContaining({
|
||||
docType: 'a',
|
||||
renderedContent: `
|
||||
|
@ -47,4 +47,5 @@ module.exports = new Package('angular.io', [gitPackage, apiPackage, contentPacka
|
||||
}
|
||||
});
|
||||
checkAnchorLinksProcessor.pathVariants = ['', '/', '.html', '/index.html', '#top-of-page'];
|
||||
checkAnchorLinksProcessor.errorOnUnmatchedLinks = true;
|
||||
});
|
||||
|
@ -31,8 +31,14 @@ describe('example inline-tag-def', function() {
|
||||
};
|
||||
});
|
||||
|
||||
it('should return a <code-example> tag', () => {
|
||||
expect(handler({}, 'example', 'some/uri')).toEqual('<code-example>\n\n</code-example>');
|
||||
it('should throw an error if there is no matching 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', () => {
|
||||
|
@ -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) {
|
||||
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 (!exampleFile) {
|
||||
log.error(createDocMessage('Missing example file... relativePath: "' + relativePath + '".', doc));
|
||||
log.error('Example files can be found in: ' + EXAMPLES_FOLDERS.join(', '));
|
||||
return '';
|
||||
const message = createDocMessage('Missing example file... relativePath: "' + relativePath + '".', doc) + '\n' +
|
||||
'Example files can be found in: ' + EXAMPLES_FOLDERS.join(', ');
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
var sourceCodeDoc = exampleFile.regions[regionName || ''];
|
||||
if (!sourceCodeDoc) {
|
||||
log.error(createDocMessage('Missing example region... relativePath: "' + relativePath + '", region: "' + regionName + '".', doc));
|
||||
log.error('Regions available are:', Object.keys[exampleFile.regions]);
|
||||
return '';
|
||||
const message = createDocMessage('Missing example region... relativePath: "' + relativePath + '", region: "' + regionName + '".', doc) + '\n' +
|
||||
'Regions available are:' + Object.keys[exampleFile.regions];
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return sourceCodeDoc.renderedContent;
|
||||
|
@ -26,4 +26,13 @@ describe('getExampleRegion', () => {
|
||||
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');
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
{% block overview %}
|
||||
<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 %};
|
||||
</code-example>
|
||||
{% endblock %}
|
||||
@ -13,7 +13,7 @@ function {$ doc.name $}{$ params.paramList(doc.parameters) $}
|
||||
{% if doc.overloads.length %}
|
||||
<h2>Overloads</h2>{% for overload in doc.overloads %}
|
||||
<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 %};
|
||||
</code-example>
|
||||
<section class="description">
|
||||
|
@ -11,7 +11,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
<th>Source</th>
|
||||
|
@ -1904,9 +1904,9 @@ devtools-timeline-model@1.1.6:
|
||||
chrome-devtools-frontend "1.0.401423"
|
||||
resolve "1.1.7"
|
||||
|
||||
dgeni-packages@^0.20.0-rc.5:
|
||||
version "0.20.0-rc.5"
|
||||
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.20.0-rc.5.tgz#09dd8134a3d79595578d6c0192ec0d82c78354be"
|
||||
dgeni-packages@^0.20.0-rc.6:
|
||||
version "0.20.0-rc.6"
|
||||
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.20.0-rc.6.tgz#d615e0631305dcf091386c802d0e424ef86206d2"
|
||||
dependencies:
|
||||
canonical-path "0.0.2"
|
||||
catharsis "^0.8.1"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "4.3.0",
|
||||
"version": "4.3.1",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
|
@ -67,20 +67,17 @@ export class AnimationEngine {
|
||||
this._transitionEngine.removeNode(namespaceId, element, context);
|
||||
}
|
||||
|
||||
process(namespaceId: string, element: any, property: string, value: any): boolean {
|
||||
switch (property.charAt(0)) {
|
||||
case '.':
|
||||
if (property == '.disabled') {
|
||||
this._transitionEngine.markElementAsDisabled(element, !!value);
|
||||
}
|
||||
return false;
|
||||
case '@':
|
||||
const [id, action] = parseTimelineCommand(property);
|
||||
const args = value as any[];
|
||||
this._timelineEngine.command(id, element, action, args);
|
||||
return false;
|
||||
default:
|
||||
return this._transitionEngine.trigger(namespaceId, element, property, value);
|
||||
disableAnimations(element: any, disable: boolean) {
|
||||
this._transitionEngine.markElementAsDisabled(element, disable);
|
||||
}
|
||||
|
||||
process(namespaceId: string, element: any, property: string, value: any) {
|
||||
if (property.charAt(0) == '@') {
|
||||
const [id, action] = parseTimelineCommand(property);
|
||||
const args = value as any[];
|
||||
this._timelineEngine.command(id, element, action, args);
|
||||
} else {
|
||||
this._transitionEngine.trigger(namespaceId, element, property, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,13 +36,22 @@ export function normalizeKeyframes(
|
||||
Object.keys(kf).forEach(prop => {
|
||||
let normalizedProp = prop;
|
||||
let normalizedValue = kf[prop];
|
||||
if (normalizedValue == PRE_STYLE) {
|
||||
normalizedValue = preStyles[prop];
|
||||
} else if (normalizedValue == AUTO_STYLE) {
|
||||
normalizedValue = postStyles[prop];
|
||||
} else if (prop != 'offset') {
|
||||
normalizedProp = normalizer.normalizePropertyName(prop, errors);
|
||||
normalizedValue = normalizer.normalizeStyleValue(prop, normalizedProp, kf[prop], errors);
|
||||
if (prop !== 'offset') {
|
||||
normalizedProp = normalizer.normalizePropertyName(normalizedProp, errors);
|
||||
switch (normalizedValue) {
|
||||
case PRE_STYLE:
|
||||
normalizedValue = preStyles[prop];
|
||||
break;
|
||||
|
||||
case AUTO_STYLE:
|
||||
normalizedValue = postStyles[prop];
|
||||
break;
|
||||
|
||||
default:
|
||||
normalizedValue =
|
||||
normalizer.normalizeStyleValue(prop, normalizedProp, normalizedValue, errors);
|
||||
break;
|
||||
}
|
||||
}
|
||||
normalizedKeyframe[normalizedProp] = normalizedValue;
|
||||
});
|
||||
|
@ -5,7 +5,7 @@
|
||||
* 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 {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 {AnimationTransitionFactory} from '../dsl/animation_transition_factory';
|
||||
@ -1168,8 +1168,8 @@ export class TransitionAnimationEngine {
|
||||
if (details && details.removedBeforeQueried) return new NoopAnimationPlayer();
|
||||
|
||||
const isQueriedElement = element !== rootElement;
|
||||
const previousPlayers =
|
||||
(allPreviousPlayersMap.get(element) || EMPTY_PLAYER_ARRAY).map(p => p.getRealPlayer());
|
||||
const previousPlayers = flattenGroupPlayers(
|
||||
(allPreviousPlayersMap.get(element) || EMPTY_PLAYER_ARRAY).map(p => p.getRealPlayer()));
|
||||
|
||||
const preStyles = preStylesMap.get(element);
|
||||
const postStyles = postStylesMap.get(element);
|
||||
@ -1464,3 +1464,20 @@ function removeNodesAfterAnimationDone(
|
||||
engine: TransitionAnimationEngine, element: any, players: AnimationPlayer[]) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,4 +132,12 @@ export class AnimationGroupPlayer implements AnimationPlayer {
|
||||
}
|
||||
|
||||
get players(): AnimationPlayer[] { return this._players; }
|
||||
|
||||
beforeDestroy(): void {
|
||||
this.players.forEach(player => {
|
||||
if (player.beforeDestroy) {
|
||||
player.beforeDestroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import 'rxjs/add/operator/toPromise';
|
||||
import {ddescribe, describe, iit, it} from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {HttpClient} from '../src/client';
|
||||
import {HttpEventType, HttpResponse} from '../src/response';
|
||||
import {HttpErrorResponse, HttpEventType, HttpResponse} from '../src/response';
|
||||
import {HttpClientTestingBackend} from '../testing/src/backend';
|
||||
|
||||
export function main() {
|
||||
@ -123,5 +123,15 @@ export function main() {
|
||||
.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'});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -61,12 +61,11 @@ export class TestRequest {
|
||||
if (statusText === undefined) {
|
||||
throw new Error('statusText is required when setting a custom status.');
|
||||
}
|
||||
const res = {body, headers, status, statusText, url};
|
||||
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();
|
||||
} else {
|
||||
this.observer.error(new HttpErrorResponse(res));
|
||||
this.observer.error(new HttpErrorResponse({error: body, headers, status, statusText, url}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,6 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<body>
|
||||
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
|
||||
<source>translate me</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/basic.ts</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 id="65cc4ab3b4c438e07c89be2b677d08369fb62da2" datatype="html">
|
||||
<source>Welcome</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/basic.ts</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">
|
||||
<source>other-3rdP-component
|
||||
multi-lines</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/third_party/other_comp.d.ts</context>
|
||||
<context context-type="linenumber">1</context>
|
||||
|
@ -9,7 +9,7 @@
|
||||
"ng-xi18n": "./src/extract_i18n.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/tsc-wrapped": "4.3.0",
|
||||
"@angular/tsc-wrapped": "4.3.1",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
|
@ -53,7 +53,7 @@ export class Xliff extends Serializer {
|
||||
const transUnit = new xml.Tag(_UNIT_TAG, {id: message.id, datatype: 'html'});
|
||||
transUnit.children.push(
|
||||
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) {
|
||||
transUnit.children.push(
|
||||
|
@ -43,7 +43,7 @@ export function jitStatements(sourceUrl: string, statements: o.Statement[]): {[k
|
||||
return evalExpression(sourceUrl, ctx, converter.getArgs());
|
||||
}
|
||||
|
||||
class JitEmitterVisitor extends AbstractJsEmitterVisitor {
|
||||
export class JitEmitterVisitor extends AbstractJsEmitterVisitor {
|
||||
private _evalArgNames: string[] = [];
|
||||
private _evalArgValues: any[] = [];
|
||||
private _evalExportedVars: string[] = [];
|
||||
@ -69,7 +69,7 @@ class JitEmitterVisitor extends AbstractJsEmitterVisitor {
|
||||
id = this._evalArgValues.length;
|
||||
this._evalArgValues.push(value);
|
||||
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]);
|
||||
return null;
|
||||
|
@ -162,7 +162,6 @@ const XLIFF_TOMERGE = `
|
||||
const XLIFF_EXTRACTED = `
|
||||
<trans-unit id="3cb04208df1c2f62553ed48e75939cf7107f9dad" datatype="html">
|
||||
<source>i18n attribute on tags</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">3</context>
|
||||
@ -170,7 +169,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="52895b1221effb3f3585b689f049d2784d714952" datatype="html">
|
||||
<source>nested</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">5</context>
|
||||
@ -178,7 +176,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="88d5f22050a9df477ee5646153558b3a4862d47e" datatype="html">
|
||||
<source>nested</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">7</context>
|
||||
@ -187,7 +184,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">9</context>
|
||||
@ -199,7 +195,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="1fe4616cce80a57c7707bac1c97054aa8e244a67" datatype="html">
|
||||
<source>on not translatable node</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
@ -207,7 +202,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="67162b5af5f15fd0eb6480c88688dafdf952b93a" datatype="html">
|
||||
<source>on translatable node</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">14</context>
|
||||
@ -215,7 +209,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">19</context>
|
||||
@ -229,7 +222,6 @@ const XLIFF_EXTRACTED = `
|
||||
<source>
|
||||
<x id="ICU"/>
|
||||
</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">21</context>
|
||||
@ -237,7 +229,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="c0ca5e58fe954d528bbfa516007a5a11690a7e99" datatype="html">
|
||||
<source>{VAR_SELECT, select, m {male} f {female} }</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
@ -247,7 +238,6 @@ const XLIFF_EXTRACTED = `
|
||||
<source>
|
||||
<x id="ICU"/>
|
||||
</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">24</context>
|
||||
@ -255,7 +245,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="a25cf2e21a299f30be1392e731163825233edc61" datatype="html">
|
||||
<source>{VAR_SELECT, select, m {male} f {female} }</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">25</context>
|
||||
@ -263,7 +252,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="d9879678f727b244bc7c7e20f22b63d98cb14890" datatype="html">
|
||||
<source><x id="INTERPOLATION"/></source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">28</context>
|
||||
@ -271,7 +259,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="50dac33dc6fc0578884baac79d875785ed77c928" datatype="html">
|
||||
<source>sex = <x id="INTERPOLATION"/></source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">29</context>
|
||||
@ -279,7 +266,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383" datatype="html">
|
||||
<source><x id="CUSTOM_NAME"/></source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">30</context>
|
||||
@ -287,7 +273,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="2ec983b4893bcd5b24af33bebe3ecba63868453c" datatype="html">
|
||||
<source>in a translatable section</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</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_1" ctype="x-div"/><x id="ICU"/><x id="CLOSE_TAG_DIV" ctype="x-div"/>
|
||||
</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
@ -311,7 +295,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
@ -319,7 +302,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="i18n16" datatype="html">
|
||||
<source>with an explicit ID</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">41</context>
|
||||
@ -327,7 +309,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">42</context>
|
||||
@ -335,7 +316,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">45</context>
|
||||
@ -344,7 +324,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="296ab5eab8d370822488c152586db3a5875ee1a2" datatype="html">
|
||||
<source>foo<x id="START_LINK" ctype="x-a"/>bar<x id="CLOSE_LINK" ctype="x-a"/></source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">53</context>
|
||||
@ -352,7 +331,6 @@ const XLIFF_EXTRACTED = `
|
||||
</trans-unit>
|
||||
<trans-unit id="2e013b311caa0916478941a985887e091d8288b6" datatype="html">
|
||||
<source><x id="MAP NAME"/></source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
|
@ -35,7 +35,6 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<body>
|
||||
<trans-unit id="983775b9a51ce14b036be72d4cfd65d68d64e231" datatype="html">
|
||||
<source>translatable attribute</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</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 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>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</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 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>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</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 id="db3e0a6a5a96481f60aec61d98c3eecddef5ac23" datatype="html">
|
||||
<source>foo</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</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 id="i" datatype="html">
|
||||
<source>foo</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</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 id="bar" datatype="html">
|
||||
<source>foo</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</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 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>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</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 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>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</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 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>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</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">
|
||||
<source>multi
|
||||
lines</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
|
35
packages/compiler/test/output/output_jit_spec.ts
Normal file
35
packages/compiler/test/output/output_jit_spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
@ -5,10 +5,10 @@
|
||||
* 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 {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 {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 {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
@ -799,6 +799,75 @@ export function main() {
|
||||
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',
|
||||
() => {
|
||||
@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', () => {
|
||||
@ -1815,12 +1943,12 @@ export function main() {
|
||||
selector: 'my-cmp',
|
||||
template: `
|
||||
<div class="parent" [@parent]="exp" (@parent.done)="cb('all','done', $event)">
|
||||
<div *ngFor="let item of items"
|
||||
<div *ngFor="let item of items"
|
||||
class="item item-{{ item }}"
|
||||
@child
|
||||
(@child.start)="cb('c-' + item, 'start', $event)"
|
||||
(@child.done)="cb('c-' + item, 'done', $event)">
|
||||
{{ item }}
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
@ -2153,9 +2281,141 @@ export function main() {
|
||||
expect(cmp.startEvent.totalTime).toEqual(9876);
|
||||
// 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()', () => {
|
||||
@Component({
|
||||
selector: 'if-cmp',
|
||||
|
@ -9,12 +9,16 @@ import {AnimationTriggerMetadata} from '@angular/animations';
|
||||
import {ɵAnimationEngine as AnimationEngine} from '@angular/animations/browser';
|
||||
import {Injectable, NgZone, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '@angular/core';
|
||||
|
||||
const ANIMATION_PREFIX = '@';
|
||||
const DISABLE_ANIMATIONS_FLAG = '@.disabled';
|
||||
|
||||
@Injectable()
|
||||
export class AnimationRendererFactory implements RendererFactory2 {
|
||||
private _currentId: number = 0;
|
||||
private _microtaskId: number = 1;
|
||||
private _animationCallbacksBuffer: [(e: any) => any, any][] = [];
|
||||
private _rendererCache = new Map<Renderer2, BaseAnimationRenderer>();
|
||||
private _cdRecurDepth = 0;
|
||||
|
||||
constructor(
|
||||
private delegate: RendererFactory2, private engine: AnimationEngine, private _zone: NgZone) {
|
||||
@ -58,6 +62,7 @@ export class AnimationRendererFactory implements RendererFactory2 {
|
||||
}
|
||||
|
||||
begin() {
|
||||
this._cdRecurDepth++;
|
||||
if (this.delegate.begin) {
|
||||
this.delegate.begin();
|
||||
}
|
||||
@ -90,10 +95,16 @@ export class AnimationRendererFactory implements RendererFactory2 {
|
||||
}
|
||||
|
||||
end() {
|
||||
this._zone.runOutsideAngular(() => {
|
||||
this._scheduleCountTask();
|
||||
this.engine.flush(this._microtaskId);
|
||||
});
|
||||
this._cdRecurDepth--;
|
||||
|
||||
// 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) {
|
||||
this.delegate.end();
|
||||
}
|
||||
@ -166,7 +177,11 @@ export class BaseAnimationRenderer implements Renderer2 {
|
||||
}
|
||||
|
||||
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); }
|
||||
@ -174,6 +189,10 @@ export class BaseAnimationRenderer implements Renderer2 {
|
||||
listen(target: any, eventName: string, callback: (event: any) => boolean | void): () => void {
|
||||
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 {
|
||||
@ -185,9 +204,12 @@ export class AnimationRenderer extends BaseAnimationRenderer implements Renderer
|
||||
}
|
||||
|
||||
setProperty(el: any, name: string, value: any): void {
|
||||
if (name.charAt(0) == '@') {
|
||||
name = name.substr(1);
|
||||
this.engine.process(this.namespaceId, el, name, value);
|
||||
if (name.charAt(0) == ANIMATION_PREFIX) {
|
||||
if (name.charAt(1) == '.' && name == DISABLE_ANIMATIONS_FLAG) {
|
||||
this.disableAnimations(el, !!value);
|
||||
} else {
|
||||
this.engine.process(this.namespaceId, el, name.substr(1), value);
|
||||
}
|
||||
} else {
|
||||
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):
|
||||
() => void {
|
||||
if (eventName.charAt(0) == '@') {
|
||||
if (eventName.charAt(0) == ANIMATION_PREFIX) {
|
||||
const element = resolveElementFromTarget(target);
|
||||
let name = eventName.substr(1);
|
||||
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);
|
||||
}
|
||||
return this.engine.listen(this.namespaceId, element, name, phase, event => {
|
||||
|
@ -11,7 +11,7 @@ const xhr2: any = require('xhr2');
|
||||
import {Injectable, Optional, Provider} from '@angular/core';
|
||||
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 {Observer} from 'rxjs/Observer';
|
||||
@ -143,12 +143,12 @@ export class ZoneMacroTaskBackend implements ConnectionBackend {
|
||||
}
|
||||
|
||||
export class ZoneClientBackend extends
|
||||
ZoneMacroTaskWrapper<HttpRequest<any>, HttpResponse<any>> implements HttpBackend {
|
||||
ZoneMacroTaskWrapper<HttpRequest<any>, HttpEvent<any>> implements HttpBackend {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -167,7 +167,7 @@ export function zoneWrappedInterceptingHandler(
|
||||
export const SERVER_HTTP_PROVIDERS: Provider[] = [
|
||||
{provide: Http, useFactory: httpFactory, deps: [XHRBackend, RequestOptions]},
|
||||
{provide: BrowserXhr, useClass: ServerXhr}, {provide: XSRFStrategy, useClass: ServerXsrfStrategy},
|
||||
{
|
||||
{provide: XhrFactory, useClass: ServerXhr}, {
|
||||
provide: HttpHandler,
|
||||
useFactory: zoneWrappedInterceptingHandler,
|
||||
deps: [HttpBackend, [new Optional(), HTTP_INTERCEPTORS]]
|
||||
|
@ -18,9 +18,26 @@ import {INITIAL_CONFIG} from './tokens';
|
||||
|
||||
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 {
|
||||
/**
|
||||
* The full document HTML of the page to render as a string.
|
||||
*/
|
||||
document?: string;
|
||||
|
||||
/**
|
||||
* The URL for the current render request.
|
||||
*/
|
||||
url?: string;
|
||||
|
||||
/**
|
||||
* Platform level providers for the current render request.
|
||||
*/
|
||||
extraProviders?: Provider[];
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
import {animate, style, transition, trigger} from '@angular/animations';
|
||||
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 {TestBed, async, inject} from '@angular/core/testing';
|
||||
import {Http, HttpModule, Response, ResponseOptions, XHRBackend} from '@angular/http';
|
||||
@ -145,6 +147,14 @@ export class HttpBeforeExampleModule {
|
||||
export class HttpAfterExampleModule {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
bootstrap: [MyServerApp],
|
||||
declarations: [MyServerApp],
|
||||
imports: [ServerModule, HttpClientModule, HttpClientTestingModule],
|
||||
})
|
||||
export class HttpClientExmapleModule {
|
||||
}
|
||||
|
||||
@Component({selector: 'app', template: `<img [src]="'link'">`})
|
||||
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();
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -181,7 +181,9 @@ function match(segmentGroup: UrlSegmentGroup, route: Route, segments: UrlSegment
|
||||
|
||||
const posParams: {[n: string]: string} = {};
|
||||
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};
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import {of } from 'rxjs/observable/of';
|
||||
import {concatMap} from 'rxjs/operator/concatMap';
|
||||
import {every} from 'rxjs/operator/every';
|
||||
import {first} from 'rxjs/operator/first';
|
||||
import {last} from 'rxjs/operator/last';
|
||||
import {map} from 'rxjs/operator/map';
|
||||
import {mergeMap} from 'rxjs/operator/mergeMap';
|
||||
import {reduce} from 'rxjs/operator/reduce';
|
||||
@ -312,7 +313,7 @@ export class Router {
|
||||
get events(): Observable<Event> { return this.routerEvents; }
|
||||
|
||||
/** @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.
|
||||
@ -331,10 +332,11 @@ export class Router {
|
||||
resetConfig(config: Routes): void {
|
||||
validateConfig(config);
|
||||
this.config = config;
|
||||
this.navigated = false;
|
||||
}
|
||||
|
||||
/** @docsNotRequired */
|
||||
ngOnDestroy() { this.dispose(); }
|
||||
ngOnDestroy(): void { this.dispose(); }
|
||||
|
||||
/** Disposes of the router */
|
||||
dispose(): void {
|
||||
@ -837,11 +839,10 @@ export class PreActivation {
|
||||
|
||||
// reusing the node
|
||||
if (curr && future._routeConfig === curr._routeConfig) {
|
||||
if (this.shouldRunGuardsAndResolvers(
|
||||
curr, future, future._routeConfig !.runGuardsAndResolvers)) {
|
||||
const shouldRunGuardsAndResolvers = this.shouldRunGuardsAndResolvers(
|
||||
curr, future, future._routeConfig !.runGuardsAndResolvers);
|
||||
if (shouldRunGuardsAndResolvers) {
|
||||
this.canActivateChecks.push(new CanActivate(futurePath));
|
||||
const outlet = context !.outlet !;
|
||||
this.canDeactivateChecks.push(new CanDeactivate(outlet.component, curr));
|
||||
} else {
|
||||
// we need to set the data
|
||||
future.data = curr.data;
|
||||
@ -857,6 +858,11 @@ export class PreActivation {
|
||||
} else {
|
||||
this.traverseChildRoutes(futureNode, currNode, parentContexts, futurePath);
|
||||
}
|
||||
|
||||
if (shouldRunGuardsAndResolvers) {
|
||||
const outlet = context !.outlet !;
|
||||
this.canDeactivateChecks.push(new CanDeactivate(outlet.component, curr));
|
||||
}
|
||||
} else {
|
||||
if (curr) {
|
||||
this.deactivateRouteAndItsChildren(currNode, context);
|
||||
@ -1004,11 +1010,29 @@ export class PreActivation {
|
||||
}
|
||||
|
||||
private resolveNode(resolve: ResolveData, future: ActivatedRouteSnapshot): Observable<any> {
|
||||
return waitForMap(resolve, (k, v) => {
|
||||
const resolver = this.getToken(v, future);
|
||||
return resolver.resolve ? wrapIntoObservable(resolver.resolve(future, this.future)) :
|
||||
wrapIntoObservable(resolver(future, this.future));
|
||||
const keys = Object.keys(resolve);
|
||||
if (keys.length === 0) {
|
||||
return of ({});
|
||||
}
|
||||
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 {
|
||||
|
@ -13,6 +13,8 @@ import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
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 {Observable} from 'rxjs/Observable';
|
||||
import {Observer} from 'rxjs/Observer';
|
||||
import {of } from 'rxjs/observable/of';
|
||||
import {map} from 'rxjs/operator/map';
|
||||
|
||||
import {forEach} from '../src/utils/collection';
|
||||
@ -396,6 +398,25 @@ describe('Integration', () => {
|
||||
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',
|
||||
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
@ -913,13 +934,12 @@ describe('Integration', () => {
|
||||
{provide: 'resolveFour', useValue: (a: any, b: any) => 4},
|
||||
{provide: 'resolveSix', useClass: ResolveSix},
|
||||
{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',
|
||||
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
|
||||
it('should provide resolved data', fakeAsync(inject([Router], (router: Router) => {
|
||||
const fixture = createRoot(router, RootCmpWithTwoOutlets);
|
||||
|
||||
router.resetConfig([{
|
||||
@ -1025,6 +1045,57 @@ describe('Integration', () => {
|
||||
|
||||
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', () => {
|
||||
@ -2364,6 +2435,11 @@ describe('Integration', () => {
|
||||
provide: 'canDeactivate_team',
|
||||
useFactory: (logger: Logger) => () => (logger.add('canDeactivate_team'), true),
|
||||
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'
|
||||
]);
|
||||
})));
|
||||
|
||||
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']);
|
||||
})));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -658,6 +658,26 @@ describe('recognize', () => {
|
||||
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', () => {
|
||||
|
@ -50,7 +50,7 @@ export interface IRootScopeService {
|
||||
$destroy(): any;
|
||||
$apply(exp?: Ng1Expression): any;
|
||||
$digest(): any;
|
||||
$evalAsync(): any;
|
||||
$evalAsync(exp: Ng1Expression, locals?: any): void;
|
||||
$on(event: string, fn?: (event?: any, ...args: any[]) => void): Function;
|
||||
$$childTail: IScope;
|
||||
$$childHead: IScope;
|
||||
@ -101,7 +101,10 @@ export interface IComponent {
|
||||
templateUrl?: string|Function;
|
||||
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 {
|
||||
// If the scope is provided, then the cloneAttachFn must be as well.
|
||||
(scope: IScope, cloneAttachFn: ICloneAttachFunction): IAugmentedJQuery;
|
||||
@ -135,7 +138,10 @@ export interface IProvideService {
|
||||
decorator(token: Ng1Token, factory: IInjectable): void;
|
||||
}
|
||||
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 {
|
||||
(method: string, url: string, post?: any, callback?: Function, headers?: any, timeout?: number,
|
||||
withCredentials?: boolean): void;
|
||||
@ -211,8 +217,8 @@ function noNg() {
|
||||
|
||||
|
||||
let angular: {
|
||||
bootstrap: (e: Element, modules: (string | IInjectable)[], config: IAngularBootstrapConfig) =>
|
||||
void,
|
||||
bootstrap: (e: Element, modules: (string | IInjectable)[], config?: IAngularBootstrapConfig) =>
|
||||
IInjectorService,
|
||||
module: (prefix: string, dependencies?: string[]) => IModule,
|
||||
element: (e: Element | string) => IAugmentedJQuery,
|
||||
version: {major: number},
|
||||
@ -256,16 +262,16 @@ export function getAngularLib(): any {
|
||||
}
|
||||
|
||||
export const bootstrap =
|
||||
(e: Element, modules: (string | IInjectable)[], config: IAngularBootstrapConfig): void =>
|
||||
(e: Element, modules: (string | IInjectable)[], config?: IAngularBootstrapConfig) =>
|
||||
angular.bootstrap(e, modules, config);
|
||||
|
||||
export const module = (prefix: string, dependencies?: string[]): IModule =>
|
||||
export const module = (prefix: string, dependencies?: string[]) =>
|
||||
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;
|
||||
|
@ -69,7 +69,7 @@ export class DowngradeComponentAdapter {
|
||||
const inputs = this.componentFactory.inputs || [];
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
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)) {
|
||||
const observeFn = (prop => {
|
||||
@ -91,20 +91,20 @@ export class DowngradeComponentAdapter {
|
||||
// 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
|
||||
// `ngOnChanges()` (which is called by a watcher) will run before the `$observe()` callback.
|
||||
let unwatch: any = this.componentScope.$watch(() => {
|
||||
unwatch('');
|
||||
let unwatch: Function|null = this.componentScope.$watch(() => {
|
||||
unwatch !();
|
||||
unwatch = null;
|
||||
observeFn((attrs as any)[input.attr]);
|
||||
observeFn(attrs[input.attr]);
|
||||
});
|
||||
|
||||
} else if (attrs.hasOwnProperty(input.bindAttr)) {
|
||||
expr = (attrs as any /** TODO #9100 */)[input.bindAttr];
|
||||
expr = attrs[input.bindAttr];
|
||||
} else if (attrs.hasOwnProperty(input.bracketAttr)) {
|
||||
expr = (attrs as any /** TODO #9100 */)[input.bracketAttr];
|
||||
expr = attrs[input.bracketAttr];
|
||||
} else if (attrs.hasOwnProperty(input.bindonAttr)) {
|
||||
expr = (attrs as any /** TODO #9100 */)[input.bindonAttr];
|
||||
expr = attrs[input.bindonAttr];
|
||||
} else if (attrs.hasOwnProperty(input.bracketParenAttr)) {
|
||||
expr = (attrs as any /** TODO #9100 */)[input.bracketParenAttr];
|
||||
expr = attrs[input.bracketParenAttr];
|
||||
}
|
||||
if (expr != null) {
|
||||
const watchFn =
|
||||
@ -132,24 +132,22 @@ export class DowngradeComponentAdapter {
|
||||
const outputs = this.componentFactory.outputs || [];
|
||||
for (let j = 0; j < outputs.length; j++) {
|
||||
const output = new PropertyBinding(outputs[j].propName, outputs[j].templateName);
|
||||
let expr: any /** TODO #9100 */ = null;
|
||||
let expr: string|null = null;
|
||||
let assignExpr = false;
|
||||
|
||||
const bindonAttr =
|
||||
output.bindonAttr ? output.bindonAttr.substring(0, output.bindonAttr.length - 6) : null;
|
||||
const bracketParenAttr = output.bracketParenAttr ?
|
||||
`[(${output.bracketParenAttr.substring(2, output.bracketParenAttr.length - 8)})]` :
|
||||
null;
|
||||
const bindonAttr = output.bindonAttr.substring(0, output.bindonAttr.length - 6);
|
||||
const bracketParenAttr =
|
||||
`[(${output.bracketParenAttr.substring(2, output.bracketParenAttr.length - 8)})]`;
|
||||
|
||||
if (attrs.hasOwnProperty(output.onAttr)) {
|
||||
expr = (attrs as any /** TODO #9100 */)[output.onAttr];
|
||||
expr = attrs[output.onAttr];
|
||||
} else if (attrs.hasOwnProperty(output.parenAttr)) {
|
||||
expr = (attrs as any /** TODO #9100 */)[output.parenAttr];
|
||||
} else if (attrs.hasOwnProperty(bindonAttr !)) {
|
||||
expr = (attrs as any /** TODO #9100 */)[bindonAttr !];
|
||||
expr = attrs[output.parenAttr];
|
||||
} else if (attrs.hasOwnProperty(bindonAttr)) {
|
||||
expr = attrs[bindonAttr];
|
||||
assignExpr = true;
|
||||
} else if (attrs.hasOwnProperty(bracketParenAttr !)) {
|
||||
expr = (attrs as any /** TODO #9100 */)[bracketParenAttr !];
|
||||
} else if (attrs.hasOwnProperty(bracketParenAttr)) {
|
||||
expr = attrs[bracketParenAttr];
|
||||
assignExpr = true;
|
||||
}
|
||||
|
||||
@ -162,10 +160,8 @@ export class DowngradeComponentAdapter {
|
||||
const emitter = this.component[output.prop] as EventEmitter<any>;
|
||||
if (emitter) {
|
||||
emitter.subscribe({
|
||||
next: assignExpr ?
|
||||
((setter: any) => (v: any /** TODO #9100 */) => setter(this.scope, v))(setter) :
|
||||
((getter: any) => (v: any /** TODO #9100 */) =>
|
||||
getter(this.scope, {'$event': v}))(getter)
|
||||
next: assignExpr ? (v: any) => setter !(this.scope, v) :
|
||||
(v: any) => getter(this.scope, {'$event': v})
|
||||
});
|
||||
} else {
|
||||
throw new Error(
|
||||
|
@ -247,7 +247,7 @@ export class UpgradeModule {
|
||||
const upgradeModule = angular.module(UPGRADE_MODULE_NAME, [INIT_MODULE_NAME].concat(modules));
|
||||
|
||||
// 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;
|
||||
|
||||
// Bootstrap the AngularJS application inside our zone
|
||||
@ -280,4 +280,4 @@ class NgAdapterInjector implements Injector {
|
||||
|
||||
return this.modInjector.get(token, notFoundValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ export function main() {
|
||||
afterEach(() => destroyPlatform());
|
||||
|
||||
it('should interleave scope and component expressions', async(() => {
|
||||
const log: any[] /** TODO #9100 */ = [];
|
||||
const l = (value: any /** TODO #9100 */) => {
|
||||
const log: string[] = [];
|
||||
const l = (value: string) => {
|
||||
log.push(value);
|
||||
return value + ';';
|
||||
};
|
||||
@ -46,8 +46,7 @@ export function main() {
|
||||
template: `{{l('2A')}}<ng1a></ng1a>{{l('2B')}}<ng1b></ng1b>{{l('2C')}}`
|
||||
})
|
||||
class Ng2Component {
|
||||
l: (value: any) => string;
|
||||
constructor() { this.l = l; }
|
||||
l = l;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -63,7 +62,7 @@ export function main() {
|
||||
.directive('ng1a', () => ({template: '{{ l(\'ng1a\') }}'}))
|
||||
.directive('ng1b', () => ({template: '{{ l(\'ng1b\') }}'}))
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}))
|
||||
.run(($rootScope: any /** TODO #9100 */) => {
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope.l = l;
|
||||
$rootScope.reset = () => log.length = 0;
|
||||
});
|
||||
@ -72,7 +71,6 @@ export function main() {
|
||||
html('<div>{{reset(); l(\'1A\');}}<ng2>{{l(\'1B\')}}</ng2>{{l(\'1C\')}}</div>');
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
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']);
|
||||
});
|
||||
}));
|
||||
@ -88,7 +86,7 @@ export function main() {
|
||||
|
||||
@Component({
|
||||
selector: 'my-child',
|
||||
template: '<div>{{valueFromPromise}}',
|
||||
template: '<div>{{ valueFromPromise }}</div>',
|
||||
})
|
||||
class ChildComponent {
|
||||
valueFromPromise: number;
|
||||
|
@ -18,7 +18,7 @@ export function bootstrap(
|
||||
// We bootstrap the Angular module first; then when it is ready (async)
|
||||
// We bootstrap the AngularJS module on the bootstrap element
|
||||
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]);
|
||||
return upgrade;
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@angular/tsc-wrapped",
|
||||
"version": "4.3.0",
|
||||
"version": "4.3.1",
|
||||
"description": "Wraps the tsc CLI, allowing extensions.",
|
||||
"homepage": "https://github.com/angular/angular/blob/master/tools/@angular/tsc-wrapped",
|
||||
"bugs": "https://github.com/angular/angular/issues",
|
||||
|
Reference in New Issue
Block a user