Compare commits
130 Commits
Author | SHA1 | Date | |
---|---|---|---|
07bd4b0630 | |||
df1718d624 | |||
17e3410d98 | |||
5effc330ed | |||
3df00828d7 | |||
8c477b2f45 | |||
7787771aba | |||
7275e1beb3 | |||
12ba62e5e2 | |||
e6e007e2f1 | |||
91dd138fa5 | |||
d972d82354 | |||
bdcf46f82e | |||
79e1c7b807 | |||
d22eeb70b8 | |||
aa92512ac6 | |||
f782b08f58 | |||
4202936bbf | |||
e1faca6386 | |||
f5b0e22d35 | |||
00693d70a2 | |||
bcef5efffe | |||
13ecc140e8 | |||
709a6dea06 | |||
16cfb88c00 | |||
efee6f5199 | |||
2aa8aae76d | |||
afb4bd9ef6 | |||
d641c36a45 | |||
f4566f8128 | |||
a67c06708d | |||
d9d57d71dd | |||
e06303a987 | |||
40b92ddf21 | |||
1681e4f57f | |||
71b7654660 | |||
eaaec6979c | |||
c587c63591 | |||
f50c1da4e2 | |||
0254ce1f6c | |||
c9b765f5c0 | |||
8c975ed156 | |||
bb35fcb562 | |||
57230b70a9 | |||
43dc60ce4f | |||
230b3b73d8 | |||
0b7dc2f9ff | |||
de1f44f51f | |||
f1cfddf6d6 | |||
ef621a2f00 | |||
df9761951b | |||
f786c560f1 | |||
c5557de3e7 | |||
ec3a5b54de | |||
cf269d9ff4 | |||
5fa5ffb82a | |||
4a57dcfd8d | |||
43923ffcf5 | |||
50c37d45dc | |||
a63359689f | |||
43d3a84df3 | |||
8310c91823 | |||
b64b5ece65 | |||
ed9c2b6281 | |||
1cf5f5fa38 | |||
a32078f85e | |||
decd129a4d | |||
c3c9ecb302 | |||
af520947aa | |||
040bf57966 | |||
65a60b7456 | |||
756ef09d12 | |||
9316f95467 | |||
83d94b7504 | |||
a121136fae | |||
a6bb84e02b | |||
3898dc488e | |||
ca3f9926f9 | |||
1c012a035f | |||
38c5304b7f | |||
9a049be67f | |||
2045c9e8ee | |||
6c4ec05a4a | |||
f7bfda31ff | |||
a92b573309 | |||
4fd13d71c8 | |||
bf7b82b658 | |||
c143fee849 | |||
0286956107 | |||
e884f4854d | |||
df1822fc2a | |||
42b4b6d21b | |||
36bc2ff269 | |||
1564042fe8 | |||
41c8c30973 | |||
61129fa12d | |||
3a5b4882bc | |||
425c1e6042 | |||
58605cf350 | |||
34b31dea7c | |||
a241ab7c07 | |||
745e10e6d2 | |||
33340dbbd1 | |||
52812c08e2 | |||
52f5ae1961 | |||
9be895b6da | |||
9f1c82537e | |||
5ab5cc77bb | |||
f1b6c6efa1 | |||
45ad13560b | |||
2045268cec | |||
fb1076b44a | |||
6fc46526ae | |||
3ef5ede6d6 | |||
136621ebc9 | |||
f23b22a0f4 | |||
0ca971c5bd | |||
3a6fcee0e6 | |||
8972137c29 | |||
cc6481077f | |||
c041b93418 | |||
bc33765913 | |||
31dce72b7b | |||
212f8dbde7 | |||
44da4984f9 | |||
d95344430c | |||
131626fc61 | |||
676bb0fa7d | |||
5a849829c4 | |||
671f73448c |
84
CHANGELOG.md
84
CHANGELOG.md
@ -1,3 +1,39 @@
|
|||||||
|
<a name="2.1.0"></a>
|
||||||
|
# [2.1.0](https://github.com/angular/angular/compare/2.1.0-rc.0...2.1.0) (2016-10-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **benchmarks:** allow ng2_switch benchmark to be used with AoT. ([#12124](https://github.com/angular/angular/issues/12124)) ([de1f44f](https://github.com/angular/angular/commit/de1f44f))
|
||||||
|
* **compiler:** allow whitespace as `<ng-content>` content ([#12225](https://github.com/angular/angular/issues/12225)) ([df1718d](https://github.com/angular/angular/commit/df1718d))
|
||||||
|
* **compiler:** interpolation expressions report the correct offset ([#12125](https://github.com/angular/angular/issues/12125)) ([d641c36](https://github.com/angular/angular/commit/d641c36))
|
||||||
|
* **compiler:** properly shim `:host:before` and `:host(:before)` ([#12171](https://github.com/angular/angular/issues/12171)) ([aa92512](https://github.com/angular/angular/commit/aa92512)), closes [#12165](https://github.com/angular/angular/issues/12165)
|
||||||
|
* **compiler:** validate `[@HostBinding](https://github.com/HostBinding)` name ([#12139](https://github.com/angular/angular/issues/12139)) ([13ecc14](https://github.com/angular/angular/commit/13ecc14))
|
||||||
|
* **compiler-cli:** don't clone static symbols when simplifying annotation metadata ([#12158](https://github.com/angular/angular/issues/12158)) ([8c477b2](https://github.com/angular/angular/commit/8c477b2))
|
||||||
|
* **compiler-cli:** remove peerDependency on [@angular](https://github.com/angular)/platform-server ([#12122](https://github.com/angular/angular/issues/12122)) ([71b7654](https://github.com/angular/angular/commit/71b7654))
|
||||||
|
* **compiler-cli:** remove unused parse5 dependency from package.json ([eaaec69](https://github.com/angular/angular/commit/eaaec69))
|
||||||
|
* **forms:** allow optional fields with pattern and minlength validators ([#12147](https://github.com/angular/angular/issues/12147)) ([d22eeb7](https://github.com/angular/angular/commit/d22eeb7))
|
||||||
|
* **forms:** properly validate blank strings with minlength ([#12091](https://github.com/angular/angular/issues/12091)) ([f50c1da](https://github.com/angular/angular/commit/f50c1da))
|
||||||
|
* **http:** fix Headers initialization from Headers and Object ([#12106](https://github.com/angular/angular/issues/12106)) ([f4566f8](https://github.com/angular/angular/commit/f4566f8))
|
||||||
|
* **http:** Headers.append should append to the list ([a67c067](https://github.com/angular/angular/commit/a67c067))
|
||||||
|
* **platform-browser-dynamic:** mark platformBrowserDynamic as stable API ([#12154](https://github.com/angular/angular/issues/12154)) ([bcef5ef](https://github.com/angular/angular/commit/bcef5ef))
|
||||||
|
* **router:** improve error message ([#12102](https://github.com/angular/angular/issues/12102)) ([e06303a](https://github.com/angular/angular/commit/e06303a))
|
||||||
|
* **router:** parent resolve should complete before merging resolved data ([1681e4f](https://github.com/angular/angular/commit/1681e4f)), closes [#12032](https://github.com/angular/angular/issues/12032)
|
||||||
|
* **router:** wildcards routes should support lazy loading ([40b92dd](https://github.com/angular/angular/commit/40b92dd)), closes [#12024](https://github.com/angular/angular/issues/12024)
|
||||||
|
* **upgrade:** allow compilerOptions in bootstrap ([#10575](https://github.com/angular/angular/issues/10575)) ([5effc33](https://github.com/angular/angular/commit/5effc33))
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.1.0-rc.0"></a>
|
||||||
|
# [2.1.0-rc.0](https://github.com/angular/angular/compare/2.1.0-beta.0...2.1.0-rc.0) (2016-10-05)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **animations:** provide aliases for `:enter` and `:leave` transitions ([#11991](https://github.com/angular/angular/issues/11991)) ([e884f48](https://github.com/angular/angular/commit/e884f48))
|
||||||
|
|
||||||
|
Note: 2.1.0-rc.0 release also contains all the changes present in the 2.0.2 release.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.0.2"></a>
|
<a name="2.0.2"></a>
|
||||||
## [2.0.2](https://github.com/angular/angular/compare/2.0.1...2.0.2) (2016-10-05)
|
## [2.0.2](https://github.com/angular/angular/compare/2.0.1...2.0.2) (2016-10-05)
|
||||||
|
|
||||||
@ -7,18 +43,34 @@
|
|||||||
* **common:** correctly removes styles on IE ([#11953](https://github.com/angular/angular/pull/11953)), closes [#7916](https://github.com/angular/angular/issues/7916)
|
* **common:** correctly removes styles on IE ([#11953](https://github.com/angular/angular/pull/11953)), closes [#7916](https://github.com/angular/angular/issues/7916)
|
||||||
* **compiler:** do not embed templateUrl in view factories in non-debug mode. ([#11818](https://github.com/angular/angular/issues/11818)) ([51e1994](https://github.com/angular/angular/commit/51e1994)), closes [#11117](https://github.com/angular/angular/issues/11117)
|
* **compiler:** do not embed templateUrl in view factories in non-debug mode. ([#11818](https://github.com/angular/angular/issues/11818)) ([51e1994](https://github.com/angular/angular/commit/51e1994)), closes [#11117](https://github.com/angular/angular/issues/11117)
|
||||||
* **compiler:** move detection of unsafe properties for binding to ElementSchemaRegistry ([#11378](https://github.com/angular/angular/issues/11378)) ([5911c3b](https://github.com/angular/angular/commit/5911c3b))
|
* **compiler:** move detection of unsafe properties for binding to ElementSchemaRegistry ([#11378](https://github.com/angular/angular/issues/11378)) ([5911c3b](https://github.com/angular/angular/commit/5911c3b))
|
||||||
* **forms:** properly validate empty strings with patterns ([#11450](https://github.com/angular/angular/issues/11450)) ([e00de0c](https://github.com/angular/angular/commit/e00de0c))
|
|
||||||
* **http:** preserve case of the first init, `set()` or `append()` ([#12023](https://github.com/angular/angular/issues/12023)) ([adb17fe](https://github.com/angular/angular/commit/adb17fe)), closes [#11624](https://github.com/angular/angular/issues/11624)
|
|
||||||
* **compiler-cli:** allow ReflectorHost passed as argument to CodeGenerator#create ([#11951](https://github.com/angular/angular/issues/11951)) ([826c98e](https://github.com/angular/angular/commit/826c98e))
|
|
||||||
* **router:** do not reset the router state when updating the component ([#11867](https://github.com/angular/angular/issues/11867)) ([cf750e1](https://github.com/angular/angular/commit/cf750e1))
|
|
||||||
* **compiler:** fix `:host(tag)` and `:host-context(tag)` ([a6bb84e0](https://github.com/angular/angular/commit/a6bb84e02b7579f8d957ef6ba5b10d83482ed756)), closes [#11972](https://github.com/angular/angular/issues/11972)
|
* **compiler:** fix `:host(tag)` and `:host-context(tag)` ([a6bb84e0](https://github.com/angular/angular/commit/a6bb84e02b7579f8d957ef6ba5b10d83482ed756)), closes [#11972](https://github.com/angular/angular/issues/11972)
|
||||||
* **compiler:** fix attribute selectors in :host and :host-context ([#12056](https://github.com/angular/angular/issues/12056)) ([6f7ed32](https://github.com/angular/angular/commit/6f7ed32)), closes [#11917](https://github.com/angular/angular/issues/11917)
|
* **compiler:** fix attribute selectors in :host and :host-context ([#12056](https://github.com/angular/angular/issues/12056)) ([6f7ed32](https://github.com/angular/angular/commit/6f7ed32)), closes [#11917](https://github.com/angular/angular/issues/11917)
|
||||||
* **compiler:** support `[@page](https://github.com/page)` and `[@document](https://github.com/document)` CSS rules ([#11878](https://github.com/angular/angular/issues/11878)) ([c99ef49](https://github.com/angular/angular/commit/c99ef49)), closes [#11860](https://github.com/angular/angular/issues/11860)
|
* **compiler:** support `@page` and `@document` CSS rules ([#11878](https://github.com/angular/angular/issues/11878)) ([c99ef49](https://github.com/angular/angular/commit/c99ef49)), closes [#11860](https://github.com/angular/angular/issues/11860)
|
||||||
* **compiler:** support `[attr="value with space"]` ([bd012ef](https://github.com/angular/angular/commit/bd012ef)), closes [#6249](https://github.com/angular/angular/issues/6249)
|
* **compiler:** support `[attr="value with space"]` ([bd012ef](https://github.com/angular/angular/commit/bd012ef)), closes [#6249](https://github.com/angular/angular/issues/6249)
|
||||||
* **compiler:** support quoted attribute values ([7395400](https://github.com/angular/angular/commit/7395400)), closes [#6085](https://github.com/angular/angular/issues/6085)
|
* **compiler:** support quoted attribute values ([7395400](https://github.com/angular/angular/commit/7395400)), closes [#6085](https://github.com/angular/angular/issues/6085)
|
||||||
* **upgrade:** bind optional properties when upgrading from ng1 ([#11411](https://github.com/angular/angular/issues/11411)) ([0851238](https://github.com/angular/angular/commit/0851238)), closes [#10181](https://github.com/angular/angular/issues/10181)
|
|
||||||
* **http:** change a behavior when a param value is null or undefined ([#11990](https://github.com/angular/angular/issues/11990)) ([9cc0a4e](https://github.com/angular/angular/commit/9cc0a4e))
|
|
||||||
* **compiler:** fix `<x>` ctype names ([7578d85](https://github.com/angular/angular/commit/7578d85)), closes [#12000](https://github.com/angular/angular/issues/12000)
|
* **compiler:** fix `<x>` ctype names ([7578d85](https://github.com/angular/angular/commit/7578d85)), closes [#12000](https://github.com/angular/angular/issues/12000)
|
||||||
|
* **compiler-cli:** allow ReflectorHost passed as argument to CodeGenerator#create ([#11951](https://github.com/angular/angular/issues/11951)) ([826c98e](https://github.com/angular/angular/commit/826c98e))
|
||||||
|
* **forms:** properly validate empty strings with patterns ([#11450](https://github.com/angular/angular/issues/11450)) ([e00de0c](https://github.com/angular/angular/commit/e00de0c))
|
||||||
|
* **http:** preserve case of the first init, `set()` or `append()` ([#12023](https://github.com/angular/angular/issues/12023)) ([adb17fe](https://github.com/angular/angular/commit/adb17fe)), closes [#11624](https://github.com/angular/angular/issues/11624)
|
||||||
|
* **http:** remove url params if provided value is null or undefined ([#11990](https://github.com/angular/angular/issues/11990)) ([9cc0a4e](https://github.com/angular/angular/commit/9cc0a4e))
|
||||||
|
* **router:** do not reset the router state when updating the component ([#11867](https://github.com/angular/angular/issues/11867)) ([cf750e1](https://github.com/angular/angular/commit/cf750e1))
|
||||||
|
* **upgrade:** bind optional properties when upgrading from ng1 ([#11411](https://github.com/angular/angular/issues/11411)) ([0851238](https://github.com/angular/angular/commit/0851238)), closes [#10181](https://github.com/angular/angular/issues/10181)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.1.0-beta.0"></a>
|
||||||
|
# [2.1.0-beta.0](https://github.com/angular/angular/compare/2.0.0...2.1.0-beta.0) (2016-09-23)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **router:** add router preloader to optimistically preload routes ([5a84982](https://github.com/angular/angular/commit/5a84982))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
* **router:** update the router not to reset router state when updating root component ([#11799](https://github.com/angular/angular/issues/11799)) ([31dce72](https://github.com/angular/angular/commit/31dce72))
|
||||||
|
|
||||||
|
Note: 2.1.0-beta.0 release also contains all the changes present in the 2.0.1 release.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.0.1"></a>
|
<a name="2.0.1"></a>
|
||||||
@ -254,7 +306,7 @@
|
|||||||
use `Type<any>` in place of `Type`.
|
use `Type<any>` in place of `Type`.
|
||||||
|
|
||||||
We don't expect that any user applications use the `Type` type directly.
|
We don't expect that any user applications use the `Type` type directly.
|
||||||
|
|
||||||
* core: Previously inconsistently named APIs SanitizationService and DomSanitizationService were renamed to Sanitizer and DomSanitizer
|
* core: Previously inconsistently named APIs SanitizationService and DomSanitizationService were renamed to Sanitizer and DomSanitizer
|
||||||
|
|
||||||
* core: previously deprecated @Component.directives and @Component.pipes support was removed.
|
* core: previously deprecated @Component.directives and @Component.pipes support was removed.
|
||||||
@ -268,11 +320,11 @@ use `Type<any>` in place of `Type`.
|
|||||||
* core: deprecated ComponentResolver was removed. Please use ComponentFactoryResolver instead.
|
* core: deprecated ComponentResolver was removed. Please use ComponentFactoryResolver instead.
|
||||||
|
|
||||||
* core: animations defined using an at-symbol prefix that are not property bound are now invalid.
|
* core: animations defined using an at-symbol prefix that are not property bound are now invalid.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- this is now invalid -->
|
<!-- this is now invalid -->
|
||||||
<div @flip="flipState"></div>
|
<div @flip="flipState"></div>
|
||||||
|
|
||||||
<!-- change that to -->
|
<!-- change that to -->
|
||||||
<div [@flip]="flipState"></div>
|
<div [@flip]="flipState"></div>
|
||||||
```
|
```
|
||||||
@ -298,7 +350,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
|||||||
```
|
```
|
||||||
{provide: MyClass, useFactory: ...}
|
{provide: MyClass, useFactory: ...}
|
||||||
```
|
```
|
||||||
|
|
||||||
* core: previously deprecated NgZoneError has been removed
|
* core: previously deprecated NgZoneError has been removed
|
||||||
|
|
||||||
* core: Exceptions are no longer part of the public API. We don't expect that anyone should be referring to the Exception types.
|
* core: Exceptions are no longer part of the public API. We don't expect that anyone should be referring to the Exception types.
|
||||||
@ -310,7 +362,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
|||||||
```
|
```
|
||||||
ErrorHandler.handleError(error: any): void;
|
ErrorHandler.handleError(error: any): void;
|
||||||
```
|
```
|
||||||
|
|
||||||
* core: deprecated DynamicComponentLoader was removed; see deprecation notice for migration instructions.
|
* core: deprecated DynamicComponentLoader was removed; see deprecation notice for migration instructions.
|
||||||
|
|
||||||
* core: deprecated SystemJsComponentResolver and SystemJsCmpFactoryResolver have been removed.
|
* core: deprecated SystemJsComponentResolver and SystemJsCmpFactoryResolver have been removed.
|
||||||
@ -344,13 +396,13 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
|||||||
* platform-browser-dynamic: `CACHED_TEMPLATE_PROVIDER` is now renamed to `RESOURCE_CACHE_PROVIDER`
|
* platform-browser-dynamic: `CACHED_TEMPLATE_PROVIDER` is now renamed to `RESOURCE_CACHE_PROVIDER`
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import {CACHED_TEMPLATE_PROVIDER} from '@angular/platform-browser-dynamic';
|
import {CACHED_TEMPLATE_PROVIDER} from '@angular/platform-browser-dynamic';
|
||||||
```
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import {RESOURCE_CACHE_PROVIDER} from '@angular/platform-browser-dynamic';
|
import {RESOURCE_CACHE_PROVIDER} from '@angular/platform-browser-dynamic';
|
||||||
```
|
```
|
||||||
@ -368,7 +420,7 @@ prefix using `animate-` must now be preixed using `bind-animate-`.
|
|||||||
* webworkers: web worker platform is now exported via separate packages.
|
* webworkers: web worker platform is now exported via separate packages.
|
||||||
|
|
||||||
Please use @angular/platform-webworker and @angular/platform-webworker-dynamic
|
Please use @angular/platform-webworker and @angular/platform-webworker-dynamic
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.0.0-rc.5"></a>
|
<a name="2.0.0-rc.5"></a>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Contributing to Angular 2
|
# Contributing to Angular
|
||||||
|
|
||||||
We would love for you to contribute to Angular 2 and help make it even better than it is
|
We would love for you to contribute to Angular and help make it even better than it is
|
||||||
today! As a contributor, here are the guidelines we would like you to follow:
|
today! As a contributor, here are the guidelines we would like you to follow:
|
||||||
|
|
||||||
- [Code of Conduct](#coc)
|
- [Code of Conduct](#coc)
|
||||||
@ -17,17 +17,26 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
|
|||||||
|
|
||||||
## <a name="question"></a> Got a Question or Problem?
|
## <a name="question"></a> Got a Question or Problem?
|
||||||
|
|
||||||
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
|
Please, do not open issues for the general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on [StackOverflow](stackoverflow.com/questions/tagged/angular) where the questions should be tagged with tag `angular`.
|
||||||
discussion list or [StackOverflow][stackoverflow]. Please note that the Angular team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
|
|
||||||
|
|
||||||
## <a name="issue"></a> Found an Issue?
|
StackOverflow is a much better place to ask questions since:
|
||||||
|
|
||||||
|
- there are thousands of people willing to help on StackOverflow
|
||||||
|
- questions and answers stay available for public viewing so your question / answer might help someone else
|
||||||
|
- StackOverflow's voting system assures that the best answers are prominently visible.
|
||||||
|
|
||||||
|
To save your and our time we will be systematically closing all the issues that are requests for general support and redirecting people to StackOverflow.
|
||||||
|
|
||||||
|
If you would like to chat about the question in real-time, you can reach out via [our gitter channel][gitter].
|
||||||
|
|
||||||
|
## <a name="issue"></a> Found a Bug?
|
||||||
If you find a bug in the source code, you can help us by
|
If you find a bug in the source code, you can help us by
|
||||||
[submitting an issue](#submit-issue) to our [GitHub Repository][github]. Even better, you can
|
[submitting an issue](#submit-issue) to our [GitHub Repository][github]. Even better, you can
|
||||||
[submit a Pull Request](#submit-pr) with a fix.
|
[submit a Pull Request](#submit-pr) with a fix.
|
||||||
|
|
||||||
## <a name="feature"></a> Want a Feature?
|
## <a name="feature"></a> Missing a Feature?
|
||||||
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub
|
You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub
|
||||||
Repository][github]. If you would like to *implement* a new feature, please submit an issue with
|
Repository. If you would like to *implement* a new feature, please submit an issue with
|
||||||
a proposal for your work first, to be sure that we can use it.
|
a proposal for your work first, to be sure that we can use it.
|
||||||
Please consider what kind of change it is:
|
Please consider what kind of change it is:
|
||||||
|
|
||||||
@ -39,24 +48,22 @@ and help you to craft the change so that it is successfully accepted into the pr
|
|||||||
## <a name="submit"></a> Submission Guidelines
|
## <a name="submit"></a> Submission Guidelines
|
||||||
|
|
||||||
### <a name="submit-issue"></a> Submitting an Issue
|
### <a name="submit-issue"></a> Submitting an Issue
|
||||||
Before you submit an issue, search the archive, maybe your question was already answered.
|
|
||||||
|
|
||||||
If your issue appears to be a bug, and hasn't been reported, open a new issue.
|
Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
|
||||||
Help us to maximize the effort we can spend fixing issues and adding new
|
|
||||||
features, by not reporting duplicate issues. Providing the following information will increase the
|
|
||||||
chances of your issue being dealt with quickly:
|
|
||||||
|
|
||||||
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
|
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs we will systematically ask you to provide a minimal reproduction scenario using http://plnkr.co. Having a live, reproducible scenario gives us wealth of important information without going back & forth to you with additional questions like:
|
||||||
* **Angular Version** - what version of Angular is affected (e.g. 2.0.0-alpha.53)
|
|
||||||
* **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
|
|
||||||
* **Browsers and Operating System** - is this a problem with all browsers?
|
|
||||||
* **Reproduce the Error** - provide a live example (using [Plunker][plunker],
|
|
||||||
[JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps
|
|
||||||
* **Related Issues** - has a similar issue been reported before?
|
|
||||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
|
||||||
causing the problem (line of code or commit)
|
|
||||||
|
|
||||||
You can file new issues by providing the above information [here](https://github.com/angular/angular/issues/new).
|
- version of Angular used
|
||||||
|
- 3rd-party libraries and their versions
|
||||||
|
- and most importantly - a use-case that fails
|
||||||
|
|
||||||
|
A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. If plunker is not a suitable way to demostrate the problem (for example for issues related to our npm packaging), please create a standalone git repository demostrating the problem.
|
||||||
|
|
||||||
|
We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal plunk. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
|
||||||
|
|
||||||
|
Unfortunately we are not able to investigate / fix bugs without a minimal reproductions, so if we don't hear back from you we are going to close an issue that don't have enough info to be reproduced.
|
||||||
|
|
||||||
|
You can file new issues by filling out our [new issue form](https://github.com/angular/angular/issues/new).
|
||||||
|
|
||||||
|
|
||||||
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||||
@ -94,7 +101,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
|||||||
* In GitHub, send a pull request to `angular:master`.
|
* In GitHub, send a pull request to `angular:master`.
|
||||||
* If we suggest changes then:
|
* If we suggest changes then:
|
||||||
* Make the required updates.
|
* Make the required updates.
|
||||||
* Re-run the Angular 2 test suites to ensure tests are still passing.
|
* Re-run the Angular test suites to ensure tests are still passing.
|
||||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
62
SAVED_REPLIES.md
Normal file
62
SAVED_REPLIES.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Saved Responses for Angular's Issue Tracker
|
||||||
|
|
||||||
|
The following are canned responses that the Angular team should use to close issues on our issue tracker that fall into the listed resolution categories.
|
||||||
|
|
||||||
|
Since GitHub currently doesn't allow us to have a repository-wide or organization-wide list of [saved replies](https://help.github.com/articles/working-with-saved-replies/), these replies need to be maintained by individual team members. Since the responses can be modified in the future, all responses are versioned to simplify the process of keeping the responses up to date.
|
||||||
|
|
||||||
|
|
||||||
|
## Angular: Already Fixed (v1)
|
||||||
|
```
|
||||||
|
Thanks for reporting this issue. Luckily it has already been fixed in one of the recent releases. Please update to the most recent version to resolve the problem.
|
||||||
|
|
||||||
|
If after upgrade the problem still exists in your application please open a new issue and provide a plunker reproducing the problem and describing the difference between the expected and current behavior. You can use this plunker template: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue
|
||||||
|
```
|
||||||
|
|
||||||
|
## Angular: Don't Understand (v1)
|
||||||
|
```
|
||||||
|
I'm sorry but we don't understand the problem you are reporting.
|
||||||
|
|
||||||
|
If the problem still exists please open a new issue and provide a plunker reproducing the problem and describing the difference between the expected and current behavior. You can use this plunker template: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Angular: Duplicate (v1)
|
||||||
|
```
|
||||||
|
Thanks for reporting this issue. However this issue is a duplicate of an existing issue #<ISSUE_NUMBER>. Please subscribe to that issue for future updates.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Angular: Insufficient Information Provided (v1)
|
||||||
|
```
|
||||||
|
Thanks for reporting this issue. However, you didn't provide sufficient information for us to understand and reproduce the problem. Please check out [our submission guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue) to understand why we can't act on issues that are lacking important information.
|
||||||
|
|
||||||
|
If the problem still persists, please file a new issue and ensure you provide all of the required information when filling out the issue template.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Angular: Issue Outside of Angular (v1)
|
||||||
|
```
|
||||||
|
I'm sorry but this issue is not caused by Angular. Please contact the author(s) of project <PROJECT NAME> or file issue on their issue tracker.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Angular: Non-reproducible (v1)
|
||||||
|
```
|
||||||
|
I'm sorry but we can't reproduce the problem following the instructions you provided.
|
||||||
|
|
||||||
|
If the problem still exists please open a new issue following [our submission guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue).
|
||||||
|
```
|
||||||
|
|
||||||
|
## Angular: Obsolete (v1)
|
||||||
|
```
|
||||||
|
Thanks for reporting this issue. This issue is now obsolete due to changes in the recent releases. Please update to the most recent Angular version.
|
||||||
|
|
||||||
|
If the problem still persists, please file a new issue and ensure you provide the version of Angular affected and include the steps to reproduce the problem when filling out the issue template.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Angular: Support Request (v1)
|
||||||
|
```
|
||||||
|
Hello, we reviewed this issue and determined that it doesn't fall into the bug report or feature request category. This issue tracker is not suitable for support requests, please repost your issue on [StackOverflow](http://stackoverflow.com/) using tag `angular`.
|
||||||
|
|
||||||
|
If you are wondering why we don't resolve support issues via the issue tracker, please [check out this explanation](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-got-a-question-or-problem).
|
||||||
|
```
|
@ -1,7 +1,7 @@
|
|||||||
# Triage Process and Github Labels for Angular 2
|
# Triage Process and Github Labels for Angular 2
|
||||||
|
|
||||||
This document describes how the Angular team uses labels and milestones
|
This document describes how the Angular team uses labels and milestones
|
||||||
to triage issues on github. The basic idea of the new process is that
|
to triage issues on github. The basic idea of the process is that
|
||||||
caretaker only assigns a component and type (bug, feature) label. The
|
caretaker only assigns a component and type (bug, feature) label. The
|
||||||
owner of the component than is in full control of how the issues should
|
owner of the component than is in full control of how the issues should
|
||||||
be triaged further.
|
be triaged further.
|
||||||
@ -35,7 +35,7 @@ There are few components which are cross-cutting. They don't have
|
|||||||
a clear location in the source tree. We will treat them as a component
|
a clear location in the source tree. We will treat them as a component
|
||||||
even thought no specific source tree is associated with them.
|
even thought no specific source tree is associated with them.
|
||||||
|
|
||||||
* `comp: documentation`: `@naomiblack`
|
* `comp: docs`: `@naomiblack`
|
||||||
* `comp: packaging`: `@IgorMinar`
|
* `comp: packaging`: `@IgorMinar`
|
||||||
* `comp: performance`: `@tbosch`
|
* `comp: performance`: `@tbosch`
|
||||||
* `comp: security`: `@IgorMinar`
|
* `comp: security`: `@IgorMinar`
|
||||||
@ -53,11 +53,11 @@ What kind of problem is this?
|
|||||||
|
|
||||||
## Caretaker Triage Process
|
## Caretaker Triage Process
|
||||||
|
|
||||||
It is the caretaker's responsibility to assign `comp: *` and `type: *`
|
It is the caretaker's responsibility to assign `comp: *` to each new
|
||||||
to each new issue as they come in. The reason why we limit the
|
issue as they come in. The reason why we limit the responsibility of the
|
||||||
responsibility of the caretaker to these two labels is that it is
|
caretaker to this one label is that it is likely that without domain
|
||||||
unlikely that without domain knowledge the caretaker could add any
|
knowledge the caretaker could mislabel issues or lack knowledge of
|
||||||
additional labels of value.
|
duplicate issues.
|
||||||
|
|
||||||
|
|
||||||
## Component's owner Triage Process
|
## Component's owner Triage Process
|
||||||
@ -68,11 +68,37 @@ process for their component.
|
|||||||
It will be up to the component owner to determine the order in which the
|
It will be up to the component owner to determine the order in which the
|
||||||
issues within the component will be resolved.
|
issues within the component will be resolved.
|
||||||
|
|
||||||
|
Several owners have adopted the issue categorization based on
|
||||||
|
[user pain](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html)
|
||||||
|
used by Angular 1. In this system every issue is assigned frequency and
|
||||||
|
severity based on which the total user pain score is calculated.
|
||||||
|
|
||||||
|
Following is the definition of various frequency and severity levels:
|
||||||
|
|
||||||
|
1. `freq(score): *` – How often does this issue come up? How many developers does this affect?
|
||||||
|
* low (1) - obscure issue affecting a handful of developers
|
||||||
|
* moderate (2) - impacts auxiliary usage patterns, only small number of applications are affected
|
||||||
|
* high (3) - impacts primary usage patterns, affecting most Angular apps
|
||||||
|
* critical (4) - impacts all Angular apps
|
||||||
|
1. `severity(score): *` - How bad is the issue?
|
||||||
|
* inconvenience (1) - causes ugly/boilerplate code in apps
|
||||||
|
* confusing (2) - unexpected or inconsistent behavior; hard-to-debug
|
||||||
|
* broken expected use (3) - it's hard or impossible for a developer using Angular to accomplish something that Angular should be able to do
|
||||||
|
* memory leak (4)
|
||||||
|
* regression (5) - functionality that used to work no longer works in a new release due to an unintentional change
|
||||||
|
* security issue (6)
|
||||||
|
|
||||||
|
|
||||||
|
These criteria are then used to calculate a "user pain" score as follows:
|
||||||
|
|
||||||
|
`pain = severity × frequency`
|
||||||
|
|
||||||
|
|
||||||
### Assigning Issues to Milestones
|
### Assigning Issues to Milestones
|
||||||
|
|
||||||
Any issue that is being worked on must have:
|
Any issue that is being worked on must have:
|
||||||
|
|
||||||
* An `assignee`: The person doing the work.
|
* An `Assignee`: The person doing the work.
|
||||||
* A `Milestone`: When we expect to complete this work.
|
* A `Milestone`: When we expect to complete this work.
|
||||||
|
|
||||||
We aim to only have at most three milestones open at a time:
|
We aim to only have at most three milestones open at a time:
|
||||||
|
40
docs/PUBLIC_API.md
Normal file
40
docs/PUBLIC_API.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Supported Public API Surface of Angular
|
||||||
|
|
||||||
|
Our SemVer, timed-release cycle and deprecation policy currently applies to these npm packages:
|
||||||
|
|
||||||
|
- `@angular/core`
|
||||||
|
- `@angular/common`
|
||||||
|
- `@angular/platform-browser`
|
||||||
|
- `@angular/platform-browser-dynamic`
|
||||||
|
- `@angular/platform-server`
|
||||||
|
- `@angular/platform-webworker`
|
||||||
|
- `@angular/platform-webworker-dynamic`
|
||||||
|
- `@angular/upgrade`
|
||||||
|
- `@angular/router`
|
||||||
|
- `@angular/forms`
|
||||||
|
- `@angular/http`
|
||||||
|
|
||||||
|
|
||||||
|
One intentional omission from this list is `@angular/compiler`, which is currently considered a low level api and is subject to internal changes. These changes will not affect any applications or libraries using the higher-level apis (the command line interface or JIT compilation via `@angular/platform-browser-dynamic`). Only very specific use-cases require direct access to the compiler API (mostly tooling integration for IDEs, linters, etc). If you are working on this kind of integration, please reach out to us first.
|
||||||
|
|
||||||
|
Additionally only the command line usage (not direct use of APIs) of `@angular/compiler-cli` is covered.
|
||||||
|
|
||||||
|
Other projects developed by the Angular team like angular-cli, Angular Material, benchpress, will be covered by these or similar guarantees in the future as they mature.
|
||||||
|
|
||||||
|
Within the supported packages, we provide guarantees for:
|
||||||
|
|
||||||
|
- symbols exported via the main entry point (e.g. `@angular/core`) and testing entry point (e.g. `@angular/core/testing`). This applies to both runtime/JavaScript values and TypeScript types.
|
||||||
|
- symbols exported via global namespace `ng` (e.g. `ng.core`)
|
||||||
|
- bundles located in the `bundles/` directory of our npm packages (e.g. `@angular/core/bundles/core.umd.js`)
|
||||||
|
|
||||||
|
|
||||||
|
We explicitly don't consider the following to be our public API surface:
|
||||||
|
|
||||||
|
- any file/import paths within our package except for the `/`, `/testing` and `/bundles/*`
|
||||||
|
- constructors of injectable classes (services and directives) - please use DI to obtain instances of these classes
|
||||||
|
- any class members or symbols marked as `private` or prefixed with underscore
|
||||||
|
- extending any of our classes unless the support for this is specifically documented in the API docs
|
||||||
|
- the contents and API surface of the code generated by Angular's compiler (with one notable exception: the existence and name of `NgModuleFactory` instances exported from generated code is guaranteed)
|
||||||
|
|
||||||
|
|
||||||
|
Our peer dependencies (e.g. typescript, zone.js, or rxjs) are not considered part of our API surface, but they are included in our SemVer policies. We might update the required version of any of these dependencies in minor releases if the update doesn't cause breaking changes for Angular applications. Peer dependency updates that result in non-trivial breaking changes must be deferred to major Angular releases.
|
@ -5,7 +5,7 @@ See [here for an example project](https://github.com/angular/benchpress-tree).
|
|||||||
|
|
||||||
The sources for this package are in the main [Angular2](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo.
|
The sources for this package are in the main [Angular2](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo.
|
||||||
|
|
||||||
License: Apache MIT 2.0
|
License: MIT
|
||||||
|
|
||||||
# Why?
|
# Why?
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {StringWrapper, isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||||
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
||||||
|
|
||||||
@ -48,6 +48,6 @@ export class FirefoxDriverExtension extends WebDriverExtension {
|
|||||||
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true, gc: true}); }
|
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true, gc: true}); }
|
||||||
|
|
||||||
supports(capabilities: {[key: string]: any}): boolean {
|
supports(capabilities: {[key: string]: any}): boolean {
|
||||||
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'firefox');
|
return capabilities['browserName'].toLowerCase() === 'firefox';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {StringWrapper, isBlank, isPresent} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||||
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
var records: any[] = [];
|
var records: any[] = [];
|
||||||
entries.forEach(entry => {
|
entries.forEach(entry => {
|
||||||
var message = JSON.parse(entry['message'])['message'];
|
var message = JSON.parse(entry['message'])['message'];
|
||||||
if (StringWrapper.equals(message['method'], 'Timeline.eventRecorded')) {
|
if (message['method'] === 'Timeline.eventRecorded') {
|
||||||
records.push(message['params']['record']);
|
records.push(message['params']['record']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -62,19 +62,16 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
var startTime = record['startTime'];
|
var startTime = record['startTime'];
|
||||||
var endTime = record['endTime'];
|
var endTime = record['endTime'];
|
||||||
|
|
||||||
if (StringWrapper.equals(type, 'FunctionCall') &&
|
if (type === 'FunctionCall' && (isBlank(data) || data['scriptName'] !== 'InjectedScript')) {
|
||||||
(isBlank(data) || !StringWrapper.equals(data['scriptName'], 'InjectedScript'))) {
|
|
||||||
events.push(createStartEvent('script', startTime));
|
events.push(createStartEvent('script', startTime));
|
||||||
endEvent = createEndEvent('script', endTime);
|
endEvent = createEndEvent('script', endTime);
|
||||||
} else if (StringWrapper.equals(type, 'Time')) {
|
} else if (type === 'Time') {
|
||||||
events.push(createMarkStartEvent(data['message'], startTime));
|
events.push(createMarkStartEvent(data['message'], startTime));
|
||||||
} else if (StringWrapper.equals(type, 'TimeEnd')) {
|
} else if (type === 'TimeEnd') {
|
||||||
events.push(createMarkEndEvent(data['message'], startTime));
|
events.push(createMarkEndEvent(data['message'], startTime));
|
||||||
} else if (
|
} else if (
|
||||||
StringWrapper.equals(type, 'RecalculateStyles') || StringWrapper.equals(type, 'Layout') ||
|
type === 'RecalculateStyles' || type === 'Layout' || type === 'UpdateLayerTree' ||
|
||||||
StringWrapper.equals(type, 'UpdateLayerTree') || StringWrapper.equals(type, 'Paint') ||
|
type === 'Paint' || type === 'Rasterize' || type === 'CompositeLayers') {
|
||||||
StringWrapper.equals(type, 'Rasterize') ||
|
|
||||||
StringWrapper.equals(type, 'CompositeLayers')) {
|
|
||||||
events.push(createStartEvent('render', startTime));
|
events.push(createStartEvent('render', startTime));
|
||||||
endEvent = createEndEvent('render', endTime);
|
endEvent = createEndEvent('render', endTime);
|
||||||
}
|
}
|
||||||
@ -92,7 +89,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true}); }
|
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true}); }
|
||||||
|
|
||||||
supports(capabilities: {[key: string]: any}): boolean {
|
supports(capabilities: {[key: string]: any}): boolean {
|
||||||
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'safari');
|
return capabilities['browserName'].toLowerCase() === 'safari';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ function createCountingValidator(
|
|||||||
return new MockValidator(log, (completeSample: MeasureValues[]) => {
|
return new MockValidator(log, (completeSample: MeasureValues[]) => {
|
||||||
count--;
|
count--;
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
return isPresent(validSample) ? validSample : completeSample;
|
return validSample || completeSample;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
|
|
||||||
import {Options, ReflectiveInjector, WebDriverExtension} from '../index';
|
import {Options, ReflectiveInjector, WebDriverExtension} from '../index';
|
||||||
import {StringWrapper, isPresent} from '../src/facade/lang';
|
import {isPresent} from '../src/facade/lang';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
function createExtension(ids: any[], caps: any) {
|
function createExtension(ids: any[], caps: any) {
|
||||||
@ -52,6 +52,6 @@ class MockExtension extends WebDriverExtension {
|
|||||||
constructor(public id: string) { super(); }
|
constructor(public id: string) { super(); }
|
||||||
|
|
||||||
supports(capabilities: {[key: string]: any}): boolean {
|
supports(capabilities: {[key: string]: any}): boolean {
|
||||||
return StringWrapper.equals(capabilities['browser'], this.id);
|
return capabilities['browser'] === this.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import {Component} from '@angular/core';
|
|||||||
import {TestBed, async} from '@angular/core/testing';
|
import {TestBed, async} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
import {Json, StringWrapper} from '../../src/facade/lang';
|
import {Json} from '../../src/facade/lang';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('JsonPipe', () => {
|
describe('JsonPipe', () => {
|
||||||
@ -20,7 +20,7 @@ export function main() {
|
|||||||
var inceptionObjString: string;
|
var inceptionObjString: string;
|
||||||
var pipe: JsonPipe;
|
var pipe: JsonPipe;
|
||||||
|
|
||||||
function normalize(obj: string): string { return StringWrapper.replace(obj, regNewLine, ''); }
|
function normalize(obj: string): string { return obj.replace(regNewLine, ''); }
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
inceptionObj = {dream: {dream: {dream: 'Limbo'}}};
|
inceptionObj = {dream: {dream: {dream: 'Limbo'}}};
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
# Overview
|
||||||
|
|
||||||
|
This folder will be filled with the benchmark sources
|
||||||
|
so that we can do offline compilation for them.
|
||||||
|
|
||||||
|
|
@ -15,5 +15,15 @@
|
|||||||
"declaration": true,
|
"declaration": true,
|
||||||
"lib": ["es6", "dom"],
|
"lib": ["es6", "dom"],
|
||||||
"baseUrl": "."
|
"baseUrl": "."
|
||||||
}
|
},
|
||||||
|
|
||||||
|
"files": [
|
||||||
|
"src/module",
|
||||||
|
"src/bootstrap",
|
||||||
|
"test/all_spec",
|
||||||
|
"benchmarks/src/tree/ng2/index_aot.ts",
|
||||||
|
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
||||||
|
"benchmarks/src/largetable/ng2/index_aot.ts",
|
||||||
|
"benchmarks/src/largetable/ng2_switch/index_aot.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,11 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/tsc-wrapped": "^0.3.0",
|
"@angular/tsc-wrapped": "^0.3.0",
|
||||||
"reflect-metadata": "^0.1.2",
|
"reflect-metadata": "^0.1.2",
|
||||||
"parse5": "^2.2.1",
|
|
||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^2.0.2",
|
"typescript": "^2.0.2",
|
||||||
"@angular/compiler": "0.0.0-PLACEHOLDER",
|
"@angular/compiler": "0.0.0-PLACEHOLDER",
|
||||||
"@angular/platform-server": "0.0.0-PLACEHOLDER",
|
|
||||||
"@angular/core": "0.0.0-PLACEHOLDER"
|
"@angular/core": "0.0.0-PLACEHOLDER"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -21,7 +21,7 @@ import {CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry,
|
|||||||
import {Console} from './private_import_core';
|
import {Console} from './private_import_core';
|
||||||
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
||||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||||
import {StaticReflector, StaticSymbol} from './static_reflector';
|
import {StaticReflector, StaticReflectorHost, StaticSymbol} from './static_reflector';
|
||||||
|
|
||||||
const nodeFs = require('fs');
|
const nodeFs = require('fs');
|
||||||
|
|
||||||
@ -36,11 +36,26 @@ const PREAMBLE = `/**
|
|||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export class CodeGenerator {
|
export class CodeGeneratorModuleCollector {
|
||||||
constructor(
|
constructor(
|
||||||
private options: AngularCompilerOptions, private program: ts.Program,
|
private staticReflector: StaticReflector, private reflectorHost: StaticReflectorHost,
|
||||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
private program: ts.Program, private options: AngularCompilerOptions) {}
|
||||||
private compiler: compiler.OfflineCompiler, private reflectorHost: ReflectorHost) {}
|
|
||||||
|
getModuleSymbols(program: ts.Program): {fileMetas: FileMetadata[], ngModules: StaticSymbol[]} {
|
||||||
|
// Compare with false since the default should be true
|
||||||
|
const skipFileNames = (this.options.generateCodeForLibraries === false) ?
|
||||||
|
GENERATED_OR_DTS_FILES :
|
||||||
|
GENERATED_FILES;
|
||||||
|
let filePaths = this.program.getSourceFiles()
|
||||||
|
.filter(sf => !skipFileNames.test(sf.fileName))
|
||||||
|
.map(sf => this.reflectorHost.getCanonicalFileName(sf.fileName));
|
||||||
|
const fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
|
||||||
|
const ngModules = fileMetas.reduce((ngModules, fileMeta) => {
|
||||||
|
ngModules.push(...fileMeta.ngModules);
|
||||||
|
return ngModules;
|
||||||
|
}, <StaticSymbol[]>[]);
|
||||||
|
return {fileMetas, ngModules};
|
||||||
|
}
|
||||||
|
|
||||||
private readFileMetadata(absSourcePath: string): FileMetadata {
|
private readFileMetadata(absSourcePath: string): FileMetadata {
|
||||||
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
|
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
|
||||||
@ -71,6 +86,18 @@ export class CodeGenerator {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CodeGenerator {
|
||||||
|
private moduleCollector: CodeGeneratorModuleCollector;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private options: AngularCompilerOptions, private program: ts.Program,
|
||||||
|
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||||
|
private compiler: compiler.OfflineCompiler, private reflectorHost: StaticReflectorHost) {
|
||||||
|
this.moduleCollector =
|
||||||
|
new CodeGeneratorModuleCollector(staticReflector, reflectorHost, program, options);
|
||||||
|
}
|
||||||
|
|
||||||
// Write codegen in a directory structure matching the sources.
|
// Write codegen in a directory structure matching the sources.
|
||||||
private calculateEmitPath(filePath: string): string {
|
private calculateEmitPath(filePath: string): string {
|
||||||
@ -95,18 +122,7 @@ export class CodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
codegen(): Promise<any> {
|
codegen(): Promise<any> {
|
||||||
// Compare with false since the default should be true
|
const {fileMetas, ngModules} = this.moduleCollector.getModuleSymbols(this.program);
|
||||||
const skipFileNames = (this.options.generateCodeForLibraries === false) ?
|
|
||||||
GENERATED_OR_DTS_FILES :
|
|
||||||
GENERATED_FILES;
|
|
||||||
let filePaths = this.program.getSourceFiles()
|
|
||||||
.filter(sf => !skipFileNames.test(sf.fileName))
|
|
||||||
.map(sf => this.reflectorHost.getCanonicalFileName(sf.fileName));
|
|
||||||
const fileMetas = filePaths.map((filePath) => this.readFileMetadata(filePath));
|
|
||||||
const ngModules = fileMetas.reduce((ngModules, fileMeta) => {
|
|
||||||
ngModules.push(...fileMeta.ngModules);
|
|
||||||
return ngModules;
|
|
||||||
}, <StaticSymbol[]>[]);
|
|
||||||
const analyzedNgModules = this.compiler.analyzeModules(ngModules);
|
const analyzedNgModules = this.compiler.analyzeModules(ngModules);
|
||||||
return Promise.all(fileMetas.map(
|
return Promise.all(fileMetas.map(
|
||||||
(fileMeta) =>
|
(fileMeta) =>
|
||||||
@ -185,7 +201,7 @@ export class CodeGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FileMetadata {
|
export interface FileMetadata {
|
||||||
fileUrl: string;
|
fileUrl: string;
|
||||||
components: StaticSymbol[];
|
components: StaticSymbol[];
|
||||||
ngModules: StaticSymbol[];
|
ngModules: StaticSymbol[];
|
||||||
|
@ -44,6 +44,8 @@ export interface StaticReflectorHost {
|
|||||||
animationMetadata: string,
|
animationMetadata: string,
|
||||||
provider: string
|
provider: string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getCanonicalFileName(fileName: string): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -366,6 +368,9 @@ export class StaticReflector implements ReflectorReader {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
if (expression instanceof StaticSymbol) {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
if (expression) {
|
if (expression) {
|
||||||
if (expression['__symbolic']) {
|
if (expression['__symbolic']) {
|
||||||
let staticSymbol: StaticSymbol;
|
let staticSymbol: StaticSymbol;
|
||||||
|
@ -119,6 +119,11 @@ describe('StaticReflector', () => {
|
|||||||
expect(simplify(noContext, 'some value')).toBe('some value');
|
expect(simplify(noContext, 'some value')).toBe('some value');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should simplify a static symbol into itself', () => {
|
||||||
|
const staticSymbol = new StaticSymbol('', '');
|
||||||
|
expect(simplify(noContext, staticSymbol)).toBe(staticSymbol);
|
||||||
|
});
|
||||||
|
|
||||||
it('should simplify an array into a copy of the array', () => {
|
it('should simplify an array into a copy of the array', () => {
|
||||||
expect(simplify(noContext, [1, 2, 3])).toEqual([1, 2, 3]);
|
expect(simplify(noContext, [1, 2, 3])).toEqual([1, 2, 3]);
|
||||||
});
|
});
|
||||||
@ -450,6 +455,9 @@ class MockReflectorHost implements StaticReflectorHost {
|
|||||||
provider: 'angular2/src/core/di/provider'
|
provider: 'angular2/src/core/di/provider'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCanonicalFileName(fileName: string): string { return fileName; }
|
||||||
|
|
||||||
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
|
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
|
||||||
var cacheKey = `${declarationFile}:${name}${members?'.'+members.join('.'):''}`;
|
var cacheKey = `${declarationFile}:${name}${members?'.'+members.join('.'):''}`;
|
||||||
var result = this.staticTypeCache.get(cacheKey);
|
var result = this.staticTypeCache.get(cacheKey);
|
||||||
|
@ -61,7 +61,9 @@ class _AnimationBuilder implements AnimationAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ast.styles.forEach(entry => {
|
ast.styles.forEach(entry => {
|
||||||
stylesArr.push(o.literalMap(Object.keys(entry).map(key => [key, o.literal(entry[key])])));
|
const entries =
|
||||||
|
Object.keys(entry).map((key): [string, o.Expression] => [key, o.literal(entry[key])]);
|
||||||
|
stylesArr.push(o.literalMap(entries));
|
||||||
});
|
});
|
||||||
|
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([
|
return o.importExpr(resolveIdentifier(Identifiers.AnimationStyles)).instantiate([
|
||||||
|
@ -115,13 +115,10 @@ function _parseAnimationStateTransition(
|
|||||||
stateStyles: {[key: string]: AnimationStylesAst},
|
stateStyles: {[key: string]: AnimationStylesAst},
|
||||||
errors: AnimationParseError[]): AnimationStateTransitionAst {
|
errors: AnimationParseError[]): AnimationStateTransitionAst {
|
||||||
var styles = new StylesCollection();
|
var styles = new StylesCollection();
|
||||||
var transitionExprs: any[] /** TODO #9100 */ = [];
|
var transitionExprs: AnimationStateTransitionExpression[] = [];
|
||||||
var transitionStates = transitionStateMetadata.stateChangeExpr.split(/\s*,\s*/);
|
var transitionStates = transitionStateMetadata.stateChangeExpr.split(/\s*,\s*/);
|
||||||
transitionStates.forEach(expr => {
|
transitionStates.forEach(
|
||||||
_parseAnimationTransitionExpr(expr, errors).forEach(transExpr => {
|
expr => { transitionExprs.push(..._parseAnimationTransitionExpr(expr, errors)); });
|
||||||
transitionExprs.push(transExpr);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
var entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
|
var entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
|
||||||
var animation = _normalizeStyleSteps(entry, stateStyles, errors);
|
var animation = _normalizeStyleSteps(entry, stateStyles, errors);
|
||||||
var animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
|
var animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
|
||||||
@ -136,9 +133,25 @@ function _parseAnimationStateTransition(
|
|||||||
return new AnimationStateTransitionAst(transitionExprs, stepsAst);
|
return new AnimationStateTransitionAst(transitionExprs, stepsAst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _parseAnimationAlias(alias: string, errors: AnimationParseError[]): string {
|
||||||
|
switch (alias) {
|
||||||
|
case ':enter':
|
||||||
|
return 'void => *';
|
||||||
|
case ':leave':
|
||||||
|
return '* => void';
|
||||||
|
default:
|
||||||
|
errors.push(
|
||||||
|
new AnimationParseError(`the transition alias value "${alias}" is not supported`));
|
||||||
|
return '* => *';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function _parseAnimationTransitionExpr(
|
function _parseAnimationTransitionExpr(
|
||||||
eventStr: string, errors: AnimationParseError[]): AnimationStateTransitionExpression[] {
|
eventStr: string, errors: AnimationParseError[]): AnimationStateTransitionExpression[] {
|
||||||
var expressions: any[] /** TODO #9100 */ = [];
|
var expressions: AnimationStateTransitionExpression[] = [];
|
||||||
|
if (eventStr[0] == ':') {
|
||||||
|
eventStr = _parseAnimationAlias(eventStr, errors);
|
||||||
|
}
|
||||||
var match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
|
var match = eventStr.match(/^(\*|[-\w]+)\s*(<?[=-]>)\s*(\*|[-\w]+)$/);
|
||||||
if (!isPresent(match) || match.length < 4) {
|
if (!isPresent(match) || match.length < 4) {
|
||||||
errors.push(new AnimationParseError(`the provided ${eventStr} is not of a supported format`));
|
errors.push(new AnimationParseError(`the provided ${eventStr} is not of a supported format`));
|
||||||
@ -165,8 +178,8 @@ function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnima
|
|||||||
|
|
||||||
function _normalizeStyleMetadata(
|
function _normalizeStyleMetadata(
|
||||||
entry: CompileAnimationStyleMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
entry: CompileAnimationStyleMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
||||||
errors: AnimationParseError[]): Array<{[key: string]: string | number}> {
|
errors: AnimationParseError[]): {[key: string]: string | number}[] {
|
||||||
var normalizedStyles: any[] /** TODO #9100 */ = [];
|
var normalizedStyles: {[key: string]: string | number}[] = [];
|
||||||
entry.styles.forEach(styleEntry => {
|
entry.styles.forEach(styleEntry => {
|
||||||
if (isString(styleEntry)) {
|
if (isString(styleEntry)) {
|
||||||
ListWrapper.addAll(
|
ListWrapper.addAll(
|
||||||
@ -338,7 +351,6 @@ function _parseAnimationKeyframes(
|
|||||||
ListWrapper.sort(rawKeyframes, (a, b) => a[0] <= b[0] ? -1 : 1);
|
ListWrapper.sort(rawKeyframes, (a, b) => a[0] <= b[0] ? -1 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var i: any /** TODO #9100 */;
|
|
||||||
var firstKeyframe = rawKeyframes[0];
|
var firstKeyframe = rawKeyframes[0];
|
||||||
if (firstKeyframe[0] != _INITIAL_KEYFRAME) {
|
if (firstKeyframe[0] != _INITIAL_KEYFRAME) {
|
||||||
ListWrapper.insert(rawKeyframes, 0, firstKeyframe = [_INITIAL_KEYFRAME, {}]);
|
ListWrapper.insert(rawKeyframes, 0, firstKeyframe = [_INITIAL_KEYFRAME, {}]);
|
||||||
@ -353,7 +365,7 @@ function _parseAnimationKeyframes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var lastKeyframeStyles = lastKeyframe[1];
|
var lastKeyframeStyles = lastKeyframe[1];
|
||||||
for (i = 1; i <= limit; i++) {
|
for (let i = 1; i <= limit; i++) {
|
||||||
let entry = rawKeyframes[i];
|
let entry = rawKeyframes[i];
|
||||||
let styles = entry[1];
|
let styles = entry[1];
|
||||||
|
|
||||||
@ -364,7 +376,7 @@ function _parseAnimationKeyframes(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = limit - 1; i >= 0; i--) {
|
for (let i = limit - 1; i >= 0; i--) {
|
||||||
let entry = rawKeyframes[i];
|
let entry = rawKeyframes[i];
|
||||||
let styles = entry[1];
|
let styles = entry[1];
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ export class CompileTemplateMetadata {
|
|||||||
this.styleUrls = _normalizeArray(styleUrls);
|
this.styleUrls = _normalizeArray(styleUrls);
|
||||||
this.externalStylesheets = _normalizeArray(externalStylesheets);
|
this.externalStylesheets = _normalizeArray(externalStylesheets);
|
||||||
this.animations = isPresent(animations) ? ListWrapper.flatten(animations) : [];
|
this.animations = isPresent(animations) ? ListWrapper.flatten(animations) : [];
|
||||||
this.ngContentSelectors = isPresent(ngContentSelectors) ? ngContentSelectors : [];
|
this.ngContentSelectors = ngContentSelectors || [];
|
||||||
if (isPresent(interpolation) && interpolation.length != 2) {
|
if (isPresent(interpolation) && interpolation.length != 2) {
|
||||||
throw new Error(`'interpolation' should have a start and an end symbol.`);
|
throw new Error(`'interpolation' should have a start and an end symbol.`);
|
||||||
}
|
}
|
||||||
@ -590,7 +590,7 @@ export function removeIdentifierDuplicates<T extends CompileMetadataWithIdentifi
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _normalizeArray(obj: any[]): any[] {
|
function _normalizeArray(obj: any[]): any[] {
|
||||||
return isPresent(obj) ? obj : [];
|
return obj || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isStaticSymbol(value: any): value is StaticSymbol {
|
export function isStaticSymbol(value: any): value is StaticSymbol {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import * as chars from '../chars';
|
import * as chars from '../chars';
|
||||||
import {BaseError} from '../facade/errors';
|
import {BaseError} from '../facade/errors';
|
||||||
import {StringWrapper, isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
|
|
||||||
export enum CssTokenType {
|
export enum CssTokenType {
|
||||||
EOF,
|
EOF,
|
||||||
@ -155,7 +155,7 @@ export class CssScanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
peekAt(index: number): number {
|
peekAt(index: number): number {
|
||||||
return index >= this.length ? chars.$EOF : StringWrapper.charCodeAt(this.input, index);
|
return index >= this.length ? chars.$EOF : this.input.charCodeAt(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
consumeEmptyStatements(): void {
|
consumeEmptyStatements(): void {
|
||||||
@ -310,7 +310,7 @@ export class CssScanner {
|
|||||||
return this.scanCharacter();
|
return this.scanCharacter();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`);
|
return this.error(`Unexpected character [${String.fromCharCode(peek)}]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
scanComment(): CssToken {
|
scanComment(): CssToken {
|
||||||
@ -476,8 +476,7 @@ export class CssScanner {
|
|||||||
var index: number = this.index;
|
var index: number = this.index;
|
||||||
var column: number = this.column;
|
var column: number = this.column;
|
||||||
var line: number = this.line;
|
var line: number = this.line;
|
||||||
errorTokenValue =
|
errorTokenValue = errorTokenValue || String.fromCharCode(this.peek);
|
||||||
isPresent(errorTokenValue) ? errorTokenValue : StringWrapper.fromCharCode(this.peek);
|
|
||||||
var invalidToken = new CssToken(index, column, line, CssTokenType.Invalid, errorTokenValue);
|
var invalidToken = new CssToken(index, column, line, CssTokenType.Invalid, errorTokenValue);
|
||||||
var errorMessage =
|
var errorMessage =
|
||||||
generateErrorMessage(this.input, message, errorTokenValue, index, line, column);
|
generateErrorMessage(this.input, message, errorTokenValue, index, line, column);
|
||||||
@ -696,11 +695,11 @@ function isValidCssCharacter(code: number, mode: CssLexerMode): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function charCode(input: string, index: number): number {
|
function charCode(input: string, index: number): number {
|
||||||
return index >= input.length ? chars.$EOF : StringWrapper.charCodeAt(input, index);
|
return index >= input.length ? chars.$EOF : input.charCodeAt(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
function charStr(code: number): string {
|
function charStr(code: number): string {
|
||||||
return StringWrapper.fromCharCode(code);
|
return String.fromCharCode(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNewline(code: number): boolean {
|
export function isNewline(code: number): boolean {
|
||||||
|
@ -71,6 +71,13 @@ export class DirectiveResolver {
|
|||||||
} else if (a instanceof HostBinding) {
|
} else if (a instanceof HostBinding) {
|
||||||
const hostBinding: HostBinding = a;
|
const hostBinding: HostBinding = a;
|
||||||
if (hostBinding.hostPropertyName) {
|
if (hostBinding.hostPropertyName) {
|
||||||
|
const startWith = hostBinding.hostPropertyName[0];
|
||||||
|
if (startWith === '(') {
|
||||||
|
throw new Error(`@HostBinding can not bind to events. Use @HostListener instead.`);
|
||||||
|
} else if (startWith === '[') {
|
||||||
|
throw new Error(
|
||||||
|
`@HostBinding parameter should be a property name, 'class.<name>', or 'attr.<name>'.`);
|
||||||
|
}
|
||||||
host[`[${hostBinding.hostPropertyName}]`] = propName;
|
host[`[${hostBinding.hostPropertyName}]`] = propName;
|
||||||
} else {
|
} else {
|
||||||
host[`[${propName}]`] = propName;
|
host[`[${propName}]`] = propName;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import * as chars from '../chars';
|
import * as chars from '../chars';
|
||||||
import {NumberWrapper, StringJoiner, StringWrapper, isPresent} from '../facade/lang';
|
import {NumberWrapper, StringJoiner, isPresent} from '../facade/lang';
|
||||||
|
|
||||||
export enum TokenType {
|
export enum TokenType {
|
||||||
Character,
|
Character,
|
||||||
@ -93,7 +93,7 @@ export class Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function newCharacterToken(index: number, code: number): Token {
|
function newCharacterToken(index: number, code: number): Token {
|
||||||
return new Token(index, TokenType.Character, code, StringWrapper.fromCharCode(code));
|
return new Token(index, TokenType.Character, code, String.fromCharCode(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
function newIdentifierToken(index: number, text: string): Token {
|
function newIdentifierToken(index: number, text: string): Token {
|
||||||
@ -133,8 +133,7 @@ class _Scanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
advance() {
|
advance() {
|
||||||
this.peek =
|
this.peek = ++this.index >= this.length ? chars.$EOF : this.input.charCodeAt(this.index);
|
||||||
++this.index >= this.length ? chars.$EOF : StringWrapper.charCodeAt(this.input, this.index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scanToken(): Token {
|
scanToken(): Token {
|
||||||
@ -146,7 +145,7 @@ class _Scanner {
|
|||||||
peek = chars.$EOF;
|
peek = chars.$EOF;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
peek = StringWrapper.charCodeAt(input, index);
|
peek = input.charCodeAt(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,16 +186,16 @@ class _Scanner {
|
|||||||
case chars.$SLASH:
|
case chars.$SLASH:
|
||||||
case chars.$PERCENT:
|
case chars.$PERCENT:
|
||||||
case chars.$CARET:
|
case chars.$CARET:
|
||||||
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
|
return this.scanOperator(start, String.fromCharCode(peek));
|
||||||
case chars.$QUESTION:
|
case chars.$QUESTION:
|
||||||
return this.scanComplexOperator(start, '?', chars.$PERIOD, '.');
|
return this.scanComplexOperator(start, '?', chars.$PERIOD, '.');
|
||||||
case chars.$LT:
|
case chars.$LT:
|
||||||
case chars.$GT:
|
case chars.$GT:
|
||||||
return this.scanComplexOperator(start, StringWrapper.fromCharCode(peek), chars.$EQ, '=');
|
return this.scanComplexOperator(start, String.fromCharCode(peek), chars.$EQ, '=');
|
||||||
case chars.$BANG:
|
case chars.$BANG:
|
||||||
case chars.$EQ:
|
case chars.$EQ:
|
||||||
return this.scanComplexOperator(
|
return this.scanComplexOperator(
|
||||||
start, StringWrapper.fromCharCode(peek), chars.$EQ, '=', chars.$EQ, '=');
|
start, String.fromCharCode(peek), chars.$EQ, '=', chars.$EQ, '=');
|
||||||
case chars.$AMPERSAND:
|
case chars.$AMPERSAND:
|
||||||
return this.scanComplexOperator(start, '&', chars.$AMPERSAND, '&');
|
return this.scanComplexOperator(start, '&', chars.$AMPERSAND, '&');
|
||||||
case chars.$BAR:
|
case chars.$BAR:
|
||||||
@ -207,7 +206,7 @@ class _Scanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.advance();
|
this.advance();
|
||||||
return this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`, 0);
|
return this.error(`Unexpected character [${String.fromCharCode(peek)}]`, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
scanCharacter(start: number, code: number): Token {
|
scanCharacter(start: number, code: number): Token {
|
||||||
@ -310,7 +309,7 @@ class _Scanner {
|
|||||||
unescapedCode = unescape(this.peek);
|
unescapedCode = unescape(this.peek);
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
buffer.add(StringWrapper.fromCharCode(unescapedCode));
|
buffer.add(String.fromCharCode(unescapedCode));
|
||||||
marker = this.index;
|
marker = this.index;
|
||||||
} else if (this.peek == chars.$EOF) {
|
} else if (this.peek == chars.$EOF) {
|
||||||
return this.error('Unterminated quote', 0);
|
return this.error('Unterminated quote', 0);
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
import * as chars from '../chars';
|
import * as chars from '../chars';
|
||||||
import {StringWrapper, escapeRegExp, isBlank, isPresent} from '../facade/lang';
|
import {escapeRegExp, isBlank, isPresent} from '../facade/lang';
|
||||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config';
|
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||||
|
|
||||||
import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, ParseSpan, ParserError, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding} from './ast';
|
import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, ParseSpan, ParserError, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding} from './ast';
|
||||||
@ -17,7 +17,7 @@ import {EOF, Lexer, Token, TokenType, isIdentifier, isQuote} from './lexer';
|
|||||||
|
|
||||||
|
|
||||||
export class SplitInterpolation {
|
export class SplitInterpolation {
|
||||||
constructor(public strings: string[], public expressions: string[]) {}
|
constructor(public strings: string[], public expressions: string[], public offsets: number[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TemplateBindingParseResult {
|
export class TemplateBindingParseResult {
|
||||||
@ -41,8 +41,12 @@ export class Parser {
|
|||||||
input: string, location: any,
|
input: string, location: any,
|
||||||
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): ASTWithSource {
|
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): ASTWithSource {
|
||||||
this._checkNoInterpolation(input, location, interpolationConfig);
|
this._checkNoInterpolation(input, location, interpolationConfig);
|
||||||
|
const sourceToLex = this._stripComments(input);
|
||||||
const tokens = this._lexer.tokenize(this._stripComments(input));
|
const tokens = this._lexer.tokenize(this._stripComments(input));
|
||||||
const ast = new _ParseAST(input, location, tokens, true, this.errors).parseChain();
|
const ast = new _ParseAST(
|
||||||
|
input, location, tokens, sourceToLex.length, true, this.errors,
|
||||||
|
input.length - sourceToLex.length)
|
||||||
|
.parseChain();
|
||||||
return new ASTWithSource(ast, input, location, this.errors);
|
return new ASTWithSource(ast, input, location, this.errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,8 +83,12 @@ export class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._checkNoInterpolation(input, location, interpolationConfig);
|
this._checkNoInterpolation(input, location, interpolationConfig);
|
||||||
var tokens = this._lexer.tokenize(this._stripComments(input));
|
const sourceToLex = this._stripComments(input);
|
||||||
return new _ParseAST(input, location, tokens, false, this.errors).parseChain();
|
const tokens = this._lexer.tokenize(sourceToLex);
|
||||||
|
return new _ParseAST(
|
||||||
|
input, location, tokens, sourceToLex.length, false, this.errors,
|
||||||
|
input.length - sourceToLex.length)
|
||||||
|
.parseChain();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseQuote(input: string, location: any): AST {
|
private _parseQuote(input: string, location: any): AST {
|
||||||
@ -95,7 +103,8 @@ export class Parser {
|
|||||||
|
|
||||||
parseTemplateBindings(input: string, location: any): TemplateBindingParseResult {
|
parseTemplateBindings(input: string, location: any): TemplateBindingParseResult {
|
||||||
var tokens = this._lexer.tokenize(input);
|
var tokens = this._lexer.tokenize(input);
|
||||||
return new _ParseAST(input, location, tokens, false, this.errors).parseTemplateBindings();
|
return new _ParseAST(input, location, tokens, input.length, false, this.errors, 0)
|
||||||
|
.parseTemplateBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
parseInterpolation(
|
parseInterpolation(
|
||||||
@ -107,8 +116,13 @@ export class Parser {
|
|||||||
let expressions: AST[] = [];
|
let expressions: AST[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < split.expressions.length; ++i) {
|
for (let i = 0; i < split.expressions.length; ++i) {
|
||||||
var tokens = this._lexer.tokenize(this._stripComments(split.expressions[i]));
|
const expressionText = split.expressions[i];
|
||||||
var ast = new _ParseAST(input, location, tokens, false, this.errors).parseChain();
|
const sourceToLex = this._stripComments(expressionText);
|
||||||
|
const tokens = this._lexer.tokenize(this._stripComments(split.expressions[i]));
|
||||||
|
const ast = new _ParseAST(
|
||||||
|
input, location, tokens, sourceToLex.length, false, this.errors,
|
||||||
|
split.offsets[i] + (expressionText.length - sourceToLex.length))
|
||||||
|
.parseChain();
|
||||||
expressions.push(ast);
|
expressions.push(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,20 +136,25 @@ export class Parser {
|
|||||||
input: string, location: string,
|
input: string, location: string,
|
||||||
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): SplitInterpolation {
|
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): SplitInterpolation {
|
||||||
const regexp = _createInterpolateRegExp(interpolationConfig);
|
const regexp = _createInterpolateRegExp(interpolationConfig);
|
||||||
const parts = StringWrapper.split(input, regexp);
|
const parts = input.split(regexp);
|
||||||
if (parts.length <= 1) {
|
if (parts.length <= 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const strings: string[] = [];
|
const strings: string[] = [];
|
||||||
const expressions: string[] = [];
|
const expressions: string[] = [];
|
||||||
|
const offsets: number[] = [];
|
||||||
|
let offset = 0;
|
||||||
for (let i = 0; i < parts.length; i++) {
|
for (let i = 0; i < parts.length; i++) {
|
||||||
var part: string = parts[i];
|
var part: string = parts[i];
|
||||||
if (i % 2 === 0) {
|
if (i % 2 === 0) {
|
||||||
// fixed string
|
// fixed string
|
||||||
strings.push(part);
|
strings.push(part);
|
||||||
|
offset += part.length;
|
||||||
} else if (part.trim().length > 0) {
|
} else if (part.trim().length > 0) {
|
||||||
|
offset += interpolationConfig.start.length;
|
||||||
expressions.push(part);
|
expressions.push(part);
|
||||||
|
offsets.push(offset);
|
||||||
|
offset += part.length + interpolationConfig.end.length;
|
||||||
} else {
|
} else {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
'Blank expressions are not allowed in interpolated strings', input,
|
'Blank expressions are not allowed in interpolated strings', input,
|
||||||
@ -143,7 +162,7 @@ export class Parser {
|
|||||||
location);
|
location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new SplitInterpolation(strings, expressions);
|
return new SplitInterpolation(strings, expressions, offsets);
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapLiteralPrimitive(input: string, location: any): ASTWithSource {
|
wrapLiteralPrimitive(input: string, location: any): ASTWithSource {
|
||||||
@ -160,8 +179,8 @@ export class Parser {
|
|||||||
private _commentStart(input: string): number {
|
private _commentStart(input: string): number {
|
||||||
var outerQuote: number = null;
|
var outerQuote: number = null;
|
||||||
for (let i = 0; i < input.length - 1; i++) {
|
for (let i = 0; i < input.length - 1; i++) {
|
||||||
const char = StringWrapper.charCodeAt(input, i);
|
const char = input.charCodeAt(i);
|
||||||
const nextChar = StringWrapper.charCodeAt(input, i + 1);
|
const nextChar = input.charCodeAt(i + 1);
|
||||||
|
|
||||||
if (char === chars.$SLASH && nextChar == chars.$SLASH && isBlank(outerQuote)) return i;
|
if (char === chars.$SLASH && nextChar == chars.$SLASH && isBlank(outerQuote)) return i;
|
||||||
|
|
||||||
@ -177,7 +196,7 @@ export class Parser {
|
|||||||
private _checkNoInterpolation(
|
private _checkNoInterpolation(
|
||||||
input: string, location: any, interpolationConfig: InterpolationConfig): void {
|
input: string, location: any, interpolationConfig: InterpolationConfig): void {
|
||||||
var regexp = _createInterpolateRegExp(interpolationConfig);
|
var regexp = _createInterpolateRegExp(interpolationConfig);
|
||||||
var parts = StringWrapper.split(input, regexp);
|
var parts = input.split(regexp);
|
||||||
if (parts.length > 1) {
|
if (parts.length > 1) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
`Got interpolation (${interpolationConfig.start}${interpolationConfig.end}) where expression was expected`,
|
`Got interpolation (${interpolationConfig.start}${interpolationConfig.end}) where expression was expected`,
|
||||||
@ -208,8 +227,9 @@ export class _ParseAST {
|
|||||||
index: number = 0;
|
index: number = 0;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public input: string, public location: any, public tokens: any[], public parseAction: boolean,
|
public input: string, public location: any, public tokens: Token[],
|
||||||
private errors: ParserError[]) {}
|
public inputLength: number, public parseAction: boolean, private errors: ParserError[],
|
||||||
|
private offset: number) {}
|
||||||
|
|
||||||
peek(offset: number): Token {
|
peek(offset: number): Token {
|
||||||
var i = this.index + offset;
|
var i = this.index + offset;
|
||||||
@ -219,7 +239,8 @@ export class _ParseAST {
|
|||||||
get next(): Token { return this.peek(0); }
|
get next(): Token { return this.peek(0); }
|
||||||
|
|
||||||
get inputIndex(): number {
|
get inputIndex(): number {
|
||||||
return (this.index < this.tokens.length) ? this.next.index : this.input.length;
|
return (this.index < this.tokens.length) ? this.next.index + this.offset :
|
||||||
|
this.inputLength + this.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
span(start: number) { return new ParseSpan(start, this.inputIndex); }
|
span(start: number) { return new ParseSpan(start, this.inputIndex); }
|
||||||
@ -239,7 +260,7 @@ export class _ParseAST {
|
|||||||
|
|
||||||
expectCharacter(code: number) {
|
expectCharacter(code: number) {
|
||||||
if (this.optionalCharacter(code)) return;
|
if (this.optionalCharacter(code)) return;
|
||||||
this.error(`Missing expected ${StringWrapper.fromCharCode(code)}`);
|
this.error(`Missing expected ${String.fromCharCode(code)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
optionalOperator(op: string): boolean {
|
optionalOperator(op: string): boolean {
|
||||||
@ -311,7 +332,7 @@ export class _ParseAST {
|
|||||||
while (this.optionalCharacter(chars.$COLON)) {
|
while (this.optionalCharacter(chars.$COLON)) {
|
||||||
args.push(this.parseExpression());
|
args.push(this.parseExpression());
|
||||||
}
|
}
|
||||||
result = new BindingPipe(this.span(result.span.start), result, name, args);
|
result = new BindingPipe(this.span(result.span.start - this.offset), result, name, args);
|
||||||
} while (this.optionalOperator('|'));
|
} while (this.optionalOperator('|'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,9 @@ export class ExpansionCase implements Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Attribute implements Node {
|
export class Attribute implements Node {
|
||||||
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
|
constructor(
|
||||||
|
public name: string, public value: string, public sourceSpan: ParseSourceSpan,
|
||||||
|
public valueSpan?: ParseSourceSpan) {}
|
||||||
visit(visitor: Visitor, context: any): any { return visitor.visitAttribute(this, context); }
|
visit(visitor: Visitor, context: any): any { return visitor.visitAttribute(this, context); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +54,10 @@ export class Comment implements Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Visitor {
|
export interface Visitor {
|
||||||
|
// Returning a truthy value from `visit()` will prevent `visitAll()` from the call to the typed
|
||||||
|
// method and result returned will become the result included in `visitAll()`s result array.
|
||||||
|
visit?(node: Node, context: any): any;
|
||||||
|
|
||||||
visitElement(element: Element, context: any): any;
|
visitElement(element: Element, context: any): any;
|
||||||
visitAttribute(attribute: Attribute, context: any): any;
|
visitAttribute(attribute: Attribute, context: any): any;
|
||||||
visitText(text: Text, context: any): any;
|
visitText(text: Text, context: any): any;
|
||||||
@ -62,8 +68,12 @@ export interface Visitor {
|
|||||||
|
|
||||||
export function visitAll(visitor: Visitor, nodes: Node[], context: any = null): any[] {
|
export function visitAll(visitor: Visitor, nodes: Node[], context: any = null): any[] {
|
||||||
let result: any[] = [];
|
let result: any[] = [];
|
||||||
|
|
||||||
|
let visit = visitor.visit ?
|
||||||
|
(ast: Node) => visitor.visit(ast, context) || ast.visit(visitor, context) :
|
||||||
|
(ast: Node) => ast.visit(visitor, context);
|
||||||
nodes.forEach(ast => {
|
nodes.forEach(ast => {
|
||||||
const astResult = ast.visit(visitor, context);
|
const astResult = visit(ast);
|
||||||
if (astResult) {
|
if (astResult) {
|
||||||
result.push(astResult);
|
result.push(astResult);
|
||||||
}
|
}
|
||||||
|
@ -331,12 +331,15 @@ class _TreeBuilder {
|
|||||||
const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
|
const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
|
||||||
let end = attrName.sourceSpan.end;
|
let end = attrName.sourceSpan.end;
|
||||||
let value = '';
|
let value = '';
|
||||||
|
let valueSpan: ParseSourceSpan;
|
||||||
if (this._peek.type === lex.TokenType.ATTR_VALUE) {
|
if (this._peek.type === lex.TokenType.ATTR_VALUE) {
|
||||||
const valueToken = this._advance();
|
const valueToken = this._advance();
|
||||||
value = valueToken.parts[0];
|
value = valueToken.parts[0];
|
||||||
end = valueToken.sourceSpan.end;
|
end = valueToken.sourceSpan.end;
|
||||||
|
valueSpan = valueToken.sourceSpan;
|
||||||
}
|
}
|
||||||
return new html.Attribute(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, end));
|
return new html.Attribute(
|
||||||
|
fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, end), valueSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getParentElement(): html.Element {
|
private _getParentElement(): html.Element {
|
||||||
|
@ -163,11 +163,11 @@ class _InjectorBuilder {
|
|||||||
if (isPresent(provider.useExisting)) {
|
if (isPresent(provider.useExisting)) {
|
||||||
result = this._getDependency(new CompileDiDependencyMetadata({token: provider.useExisting}));
|
result = this._getDependency(new CompileDiDependencyMetadata({token: provider.useExisting}));
|
||||||
} else if (isPresent(provider.useFactory)) {
|
} else if (isPresent(provider.useFactory)) {
|
||||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
|
var deps = provider.deps || provider.useFactory.diDeps;
|
||||||
var depsExpr = deps.map((dep) => this._getDependency(dep));
|
var depsExpr = deps.map((dep) => this._getDependency(dep));
|
||||||
result = o.importExpr(provider.useFactory).callFn(depsExpr);
|
result = o.importExpr(provider.useFactory).callFn(depsExpr);
|
||||||
} else if (isPresent(provider.useClass)) {
|
} else if (isPresent(provider.useClass)) {
|
||||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
|
var deps = provider.deps || provider.useClass.diDeps;
|
||||||
var depsExpr = deps.map((dep) => this._getDependency(dep));
|
var depsExpr = deps.map((dep) => this._getDependency(dep));
|
||||||
result =
|
result =
|
||||||
o.importExpr(provider.useClass).instantiate(depsExpr, o.importType(provider.useClass));
|
o.importExpr(provider.useClass).instantiate(depsExpr, o.importType(provider.useClass));
|
||||||
|
@ -26,7 +26,26 @@ export class SourceModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class NgModulesSummary {
|
export class NgModulesSummary {
|
||||||
constructor(public ngModuleByComponent: Map<StaticSymbol, CompileNgModuleMetadata>) {}
|
constructor(
|
||||||
|
public ngModuleByComponent: Map<StaticSymbol, CompileNgModuleMetadata>,
|
||||||
|
public ngModules: CompileNgModuleMetadata[]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function analyzeModules(
|
||||||
|
ngModules: StaticSymbol[], metadataResolver: CompileMetadataResolver) {
|
||||||
|
const ngModuleByComponent = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
||||||
|
const modules: CompileNgModuleMetadata[] = [];
|
||||||
|
|
||||||
|
ngModules.forEach((ngModule) => {
|
||||||
|
const ngModuleMeta = metadataResolver.getNgModuleMetadata(<any>ngModule);
|
||||||
|
modules.push(ngModuleMeta);
|
||||||
|
ngModuleMeta.declaredDirectives.forEach((dirMeta: CompileDirectiveMetadata) => {
|
||||||
|
if (dirMeta.isComponent) {
|
||||||
|
ngModuleByComponent.set(dirMeta.type.reference, ngModuleMeta);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return new NgModulesSummary(ngModuleByComponent, modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OfflineCompiler {
|
export class OfflineCompiler {
|
||||||
@ -41,17 +60,7 @@ export class OfflineCompiler {
|
|||||||
private _localeId: string, private _translationFormat: string) {}
|
private _localeId: string, private _translationFormat: string) {}
|
||||||
|
|
||||||
analyzeModules(ngModules: StaticSymbol[]): NgModulesSummary {
|
analyzeModules(ngModules: StaticSymbol[]): NgModulesSummary {
|
||||||
const ngModuleByComponent = new Map<StaticSymbol, CompileNgModuleMetadata>();
|
return analyzeModules(ngModules, this._metadataResolver);
|
||||||
|
|
||||||
ngModules.forEach((ngModule) => {
|
|
||||||
const ngModuleMeta = this._metadataResolver.getNgModuleMetadata(<any>ngModule);
|
|
||||||
ngModuleMeta.declaredDirectives.forEach((dirMeta) => {
|
|
||||||
if (dirMeta.isComponent) {
|
|
||||||
ngModuleByComponent.set(dirMeta.type.reference, ngModuleMeta);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return new NgModulesSummary(ngModuleByComponent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache() {
|
clearCache() {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {StringWrapper, isBlank, isPresent, isString} from '../facade/lang';
|
import {isBlank, isPresent, isString} from '../facade/lang';
|
||||||
|
|
||||||
import * as o from './output_ast';
|
import * as o from './output_ast';
|
||||||
|
|
||||||
@ -368,7 +368,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
|
|||||||
var useNewLine = ast.entries.length > 1;
|
var useNewLine = ast.entries.length > 1;
|
||||||
ctx.print(`{`, useNewLine);
|
ctx.print(`{`, useNewLine);
|
||||||
ctx.incIndent();
|
ctx.incIndent();
|
||||||
this.visitAllObjects((entry: any /** TODO #9100 */) => {
|
this.visitAllObjects(entry => {
|
||||||
ctx.print(`${escapeIdentifier(entry[0], this._escapeDollarInStrings, false)}: `);
|
ctx.print(`${escapeIdentifier(entry[0], this._escapeDollarInStrings, false)}: `);
|
||||||
entry[1].visitExpression(this, ctx);
|
entry[1].visitExpression(this, ctx);
|
||||||
}, ast.entries, ctx, ',', useNewLine);
|
}, ast.entries, ctx, ',', useNewLine);
|
||||||
@ -381,12 +381,11 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
|
|||||||
expressions: o.Expression[], ctx: EmitterVisitorContext, separator: string,
|
expressions: o.Expression[], ctx: EmitterVisitorContext, separator: string,
|
||||||
newLine: boolean = false): void {
|
newLine: boolean = false): void {
|
||||||
this.visitAllObjects(
|
this.visitAllObjects(
|
||||||
(expr: any /** TODO #9100 */) => expr.visitExpression(this, ctx), expressions, ctx,
|
expr => expr.visitExpression(this, ctx), expressions, ctx, separator, newLine);
|
||||||
separator, newLine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visitAllObjects(
|
visitAllObjects<T>(
|
||||||
handler: Function, expressions: any, ctx: EmitterVisitorContext, separator: string,
|
handler: (t: T) => void, expressions: T[], ctx: EmitterVisitorContext, separator: string,
|
||||||
newLine: boolean = false): void {
|
newLine: boolean = false): void {
|
||||||
for (var i = 0; i < expressions.length; i++) {
|
for (var i = 0; i < expressions.length; i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
@ -409,18 +408,17 @@ export function escapeIdentifier(
|
|||||||
if (isBlank(input)) {
|
if (isBlank(input)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var body = StringWrapper.replaceAllMapped(
|
var body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match: string[]) => {
|
||||||
input, _SINGLE_QUOTE_ESCAPE_STRING_RE, (match: any /** TODO #9100 */) => {
|
if (match[0] == '$') {
|
||||||
if (match[0] == '$') {
|
return escapeDollar ? '\\$' : '$';
|
||||||
return escapeDollar ? '\\$' : '$';
|
} else if (match[0] == '\n') {
|
||||||
} else if (match[0] == '\n') {
|
return '\\n';
|
||||||
return '\\n';
|
} else if (match[0] == '\r') {
|
||||||
} else if (match[0] == '\r') {
|
return '\\r';
|
||||||
return '\\r';
|
} else {
|
||||||
} else {
|
return `\\${match[0]}`;
|
||||||
return `\\${match[0]}`;
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
let requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body);
|
let requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body);
|
||||||
return requiresQuotes ? `'${body}'` : body;
|
return requiresQuotes ? `'${body}'` : body;
|
||||||
}
|
}
|
||||||
|
@ -144,11 +144,11 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
|
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
|
||||||
this.visitAllObjects((param: any /** TODO #9100 */) => ctx.print(param.name), params, ctx, ',');
|
this.visitAllObjects(param => ctx.print(param.name), params, ctx, ',');
|
||||||
}
|
}
|
||||||
|
|
||||||
getBuiltinMethodName(method: o.BuiltinMethod): string {
|
getBuiltinMethodName(method: o.BuiltinMethod): string {
|
||||||
var name: any /** TODO #9100 */;
|
var name: string;
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case o.BuiltinMethod.ConcatArray:
|
case o.BuiltinMethod.ConcatArray:
|
||||||
name = 'concat';
|
name = 'concat';
|
||||||
|
@ -20,7 +20,7 @@ export class JavaScriptEmitter implements OutputEmitter {
|
|||||||
var converter = new JsEmitterVisitor(moduleUrl);
|
var converter = new JsEmitterVisitor(moduleUrl);
|
||||||
var ctx = EmitterVisitorContext.createRoot(exportedVars);
|
var ctx = EmitterVisitorContext.createRoot(exportedVars);
|
||||||
converter.visitAllStatements(stmts, ctx);
|
converter.visitAllStatements(stmts, ctx);
|
||||||
var srcParts: any[] /** TODO #9100 */ = [];
|
var srcParts: string[] = [];
|
||||||
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
|
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
|
||||||
// Note: can't write the real word for import as it screws up system.js auto detection...
|
// Note: can't write the real word for import as it screws up system.js auto detection...
|
||||||
srcParts.push(
|
srcParts.push(
|
||||||
|
@ -215,7 +215,7 @@ export class ReadVarExpr extends Expression {
|
|||||||
export class WriteVarExpr extends Expression {
|
export class WriteVarExpr extends Expression {
|
||||||
public value: Expression;
|
public value: Expression;
|
||||||
constructor(public name: string, value: Expression, type: Type = null) {
|
constructor(public name: string, value: Expression, type: Type = null) {
|
||||||
super(isPresent(type) ? type : value.type);
|
super(type || value.type);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ export class WriteKeyExpr extends Expression {
|
|||||||
public value: Expression;
|
public value: Expression;
|
||||||
constructor(
|
constructor(
|
||||||
public receiver: Expression, public index: Expression, value: Expression, type: Type = null) {
|
public receiver: Expression, public index: Expression, value: Expression, type: Type = null) {
|
||||||
super(isPresent(type) ? type : value.type);
|
super(type || value.type);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
@ -246,7 +246,7 @@ export class WritePropExpr extends Expression {
|
|||||||
public value: Expression;
|
public value: Expression;
|
||||||
constructor(
|
constructor(
|
||||||
public receiver: Expression, public name: string, value: Expression, type: Type = null) {
|
public receiver: Expression, public name: string, value: Expression, type: Type = null) {
|
||||||
super(isPresent(type) ? type : value.type);
|
super(type || value.type);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
@ -322,7 +322,7 @@ export class ConditionalExpr extends Expression {
|
|||||||
constructor(
|
constructor(
|
||||||
public condition: Expression, trueCase: Expression, public falseCase: Expression = null,
|
public condition: Expression, trueCase: Expression, public falseCase: Expression = null,
|
||||||
type: Type = null) {
|
type: Type = null) {
|
||||||
super(isPresent(type) ? type : trueCase.type);
|
super(type || trueCase.type);
|
||||||
this.trueCase = trueCase;
|
this.trueCase = trueCase;
|
||||||
}
|
}
|
||||||
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
@ -369,7 +369,7 @@ export class BinaryOperatorExpr extends Expression {
|
|||||||
public lhs: Expression;
|
public lhs: Expression;
|
||||||
constructor(
|
constructor(
|
||||||
public operator: BinaryOperator, lhs: Expression, public rhs: Expression, type: Type = null) {
|
public operator: BinaryOperator, lhs: Expression, public rhs: Expression, type: Type = null) {
|
||||||
super(isPresent(type) ? type : lhs.type);
|
super(type || lhs.type);
|
||||||
this.lhs = lhs;
|
this.lhs = lhs;
|
||||||
}
|
}
|
||||||
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
@ -416,7 +416,7 @@ export class LiteralArrayExpr extends Expression {
|
|||||||
|
|
||||||
export class LiteralMapExpr extends Expression {
|
export class LiteralMapExpr extends Expression {
|
||||||
public valueType: Type = null;
|
public valueType: Type = null;
|
||||||
constructor(public entries: Array<Array<string|Expression>>, type: MapType = null) {
|
constructor(public entries: [string, Expression][], type: MapType = null) {
|
||||||
super(type);
|
super(type);
|
||||||
if (isPresent(type)) {
|
if (isPresent(type)) {
|
||||||
this.valueType = type.valueType;
|
this.valueType = type.valueType;
|
||||||
@ -479,7 +479,7 @@ export class DeclareVarStmt extends Statement {
|
|||||||
public name: string, public value: Expression, type: Type = null,
|
public name: string, public value: Expression, type: Type = null,
|
||||||
modifiers: StmtModifier[] = null) {
|
modifiers: StmtModifier[] = null) {
|
||||||
super(modifiers);
|
super(modifiers);
|
||||||
this.type = isPresent(type) ? type : value.type;
|
this.type = type || value.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitStatement(visitor: StatementVisitor, context: any): any {
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
@ -625,7 +625,7 @@ export class ExpressionTransformer implements StatementVisitor, ExpressionVisito
|
|||||||
expr.value.visitExpression(this, context));
|
expr.value.visitExpression(this, context));
|
||||||
}
|
}
|
||||||
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any {
|
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any {
|
||||||
var method = isPresent(ast.builtin) ? ast.builtin : ast.name;
|
var method = ast.builtin || ast.name;
|
||||||
return new InvokeMethodExpr(
|
return new InvokeMethodExpr(
|
||||||
ast.receiver.visitExpression(this, context), method,
|
ast.receiver.visitExpression(this, context), method,
|
||||||
this.visitAllExpressions(ast.args, context), ast.type);
|
this.visitAllExpressions(ast.args, context), ast.type);
|
||||||
@ -673,9 +673,11 @@ export class ExpressionTransformer implements StatementVisitor, ExpressionVisito
|
|||||||
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any {
|
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any {
|
||||||
return new LiteralArrayExpr(this.visitAllExpressions(ast.entries, context));
|
return new LiteralArrayExpr(this.visitAllExpressions(ast.entries, context));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
|
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
|
||||||
return new LiteralMapExpr(ast.entries.map(
|
const entries = ast.entries.map(
|
||||||
(entry) => [entry[0], (<Expression>entry[1]).visitExpression(this, context)]));
|
(entry): [string, Expression] => [entry[0], entry[1].visitExpression(this, context), ]);
|
||||||
|
return new LiteralMapExpr(entries);
|
||||||
}
|
}
|
||||||
visitAllExpressions(exprs: Expression[], context: any): Expression[] {
|
visitAllExpressions(exprs: Expression[], context: any): Expression[] {
|
||||||
return exprs.map(expr => expr.visitExpression(this, context));
|
return exprs.map(expr => expr.visitExpression(this, context));
|
||||||
@ -881,8 +883,7 @@ export function literalArr(values: Expression[], type: Type = null): LiteralArra
|
|||||||
return new LiteralArrayExpr(values, type);
|
return new LiteralArrayExpr(values, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function literalMap(
|
export function literalMap(values: [string, Expression][], type: MapType = null): LiteralMapExpr {
|
||||||
values: Array<Array<string|Expression>>, type: MapType = null): LiteralMapExpr {
|
|
||||||
return new LiteralMapExpr(values, type);
|
return new LiteralMapExpr(values, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,9 +26,9 @@ class JitEmitterVisitor extends AbstractJsEmitterVisitor {
|
|||||||
private _evalArgValues: any[] = [];
|
private _evalArgValues: any[] = [];
|
||||||
|
|
||||||
getArgs(): {[key: string]: any} {
|
getArgs(): {[key: string]: any} {
|
||||||
var result = {};
|
var result: {[key: string]: any} = {};
|
||||||
for (var i = 0; i < this._evalArgNames.length; i++) {
|
for (var i = 0; i < this._evalArgNames.length; i++) {
|
||||||
(result as any /** TODO #9100 */)[this._evalArgNames[i]] = this._evalArgValues[i];
|
result[this._evalArgNames[i]] = this._evalArgValues[i];
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ export class TypeScriptEmitter implements OutputEmitter {
|
|||||||
var converter = new _TsEmitterVisitor(moduleUrl);
|
var converter = new _TsEmitterVisitor(moduleUrl);
|
||||||
var ctx = EmitterVisitorContext.createRoot(exportedVars);
|
var ctx = EmitterVisitorContext.createRoot(exportedVars);
|
||||||
converter.visitAllStatements(stmts, ctx);
|
converter.visitAllStatements(stmts, ctx);
|
||||||
var srcParts: any[] /** TODO #9100 */ = [];
|
var srcParts: string[] = [];
|
||||||
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
|
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
|
||||||
// Note: can't write the real word for import as it screws up system.js auto detection...
|
// Note: can't write the real word for import as it screws up system.js auto detection...
|
||||||
srcParts.push(
|
srcParts.push(
|
||||||
@ -75,6 +75,22 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
|||||||
super.visitLiteralExpr(ast, ctx, '(null as any)');
|
super.visitLiteralExpr(ast, ctx, '(null as any)');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Temporary workaround to support strictNullCheck enabled consumers of ngc emit.
|
||||||
|
// In SNC mode, [] have the type never[], so we cast here to any[].
|
||||||
|
// TODO: narrow the cast to a more explicit type, or use a pattern that does not
|
||||||
|
// start with [].concat. see https://github.com/angular/angular/pull/11846
|
||||||
|
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
if (ast.entries.length === 0) {
|
||||||
|
ctx.print('(');
|
||||||
|
}
|
||||||
|
const result = super.visitLiteralArrayExpr(ast, ctx);
|
||||||
|
if (ast.entries.length === 0) {
|
||||||
|
ctx.print(' as any[])');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
||||||
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
||||||
return null;
|
return null;
|
||||||
@ -227,7 +243,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitBuiltintType(type: o.BuiltinType, ctx: EmitterVisitorContext): any {
|
visitBuiltintType(type: o.BuiltinType, ctx: EmitterVisitorContext): any {
|
||||||
var typeStr: any /** TODO #9100 */;
|
var typeStr: string;
|
||||||
switch (type.name) {
|
switch (type.name) {
|
||||||
case o.BuiltinTypeName.Bool:
|
case o.BuiltinTypeName.Bool:
|
||||||
typeStr = 'boolean';
|
typeStr = 'boolean';
|
||||||
@ -291,7 +307,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
|
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
|
||||||
this.visitAllObjects((param: any /** TODO #9100 */) => {
|
this.visitAllObjects(param => {
|
||||||
ctx.print(param.name);
|
ctx.print(param.name);
|
||||||
ctx.print(':');
|
ctx.print(':');
|
||||||
this.visitType(param.type, ctx);
|
this.visitType(param.type, ctx);
|
||||||
@ -320,8 +336,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
|||||||
}
|
}
|
||||||
if (isPresent(typeParams) && typeParams.length > 0) {
|
if (isPresent(typeParams) && typeParams.length > 0) {
|
||||||
ctx.print(`<`);
|
ctx.print(`<`);
|
||||||
this.visitAllObjects(
|
this.visitAllObjects(type => type.visitType(this, ctx), typeParams, ctx, ',');
|
||||||
(type: any /** TODO #9100 */) => type.visitType(this, ctx), typeParams, ctx, ',');
|
|
||||||
ctx.print(`>`);
|
ctx.print(`>`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ class _ValueOutputAstTransformer implements ValueTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitStringMap(map: {[key: string]: any}, type: o.MapType): o.Expression {
|
visitStringMap(map: {[key: string]: any}, type: o.MapType): o.Expression {
|
||||||
var entries: Array<string|o.Expression>[] = [];
|
const entries: [string, o.Expression][] = [];
|
||||||
Object.keys(map).forEach(key => { entries.push([key, visitValue(map[key], this, null)]); });
|
Object.keys(map).forEach(key => { entries.push([key, visitValue(map[key], this, null)]); });
|
||||||
return o.literalMap(entries, type);
|
return o.literalMap(entries, type);
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ export class ProviderElementContext {
|
|||||||
|
|
||||||
private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: Map<any, boolean>) {
|
private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: Map<any, boolean>) {
|
||||||
this._getQueriesFor(token).forEach((query) => {
|
this._getQueriesFor(token).forEach((query) => {
|
||||||
const queryReadToken = isPresent(query.read) ? query.read : token;
|
const queryReadToken = query.read || token;
|
||||||
if (isBlank(queryReadTokens.get(queryReadToken.reference))) {
|
if (isBlank(queryReadTokens.get(queryReadToken.reference))) {
|
||||||
queryReadTokens.set(queryReadToken.reference, true);
|
queryReadTokens.set(queryReadToken.reference, true);
|
||||||
}
|
}
|
||||||
@ -169,11 +169,11 @@ export class ProviderElementContext {
|
|||||||
transformedUseValue = existingDiDep.value;
|
transformedUseValue = existingDiDep.value;
|
||||||
}
|
}
|
||||||
} else if (isPresent(provider.useFactory)) {
|
} else if (isPresent(provider.useFactory)) {
|
||||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
|
var deps = provider.deps || provider.useFactory.diDeps;
|
||||||
transformedDeps =
|
transformedDeps =
|
||||||
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
||||||
} else if (isPresent(provider.useClass)) {
|
} else if (isPresent(provider.useClass)) {
|
||||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
|
var deps = provider.deps || provider.useClass.diDeps;
|
||||||
transformedDeps =
|
transformedDeps =
|
||||||
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
||||||
}
|
}
|
||||||
@ -338,11 +338,11 @@ export class NgModuleProviderAnalyzer {
|
|||||||
transformedUseValue = existingDiDep.value;
|
transformedUseValue = existingDiDep.value;
|
||||||
}
|
}
|
||||||
} else if (isPresent(provider.useFactory)) {
|
} else if (isPresent(provider.useFactory)) {
|
||||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
|
var deps = provider.deps || provider.useFactory.diDeps;
|
||||||
transformedDeps =
|
transformedDeps =
|
||||||
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
|
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
|
||||||
} else if (isPresent(provider.useClass)) {
|
} else if (isPresent(provider.useClass)) {
|
||||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
|
var deps = provider.deps || provider.useClass.diDeps;
|
||||||
transformedDeps =
|
transformedDeps =
|
||||||
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
|
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
|
||||||
}
|
}
|
||||||
|
@ -381,7 +381,10 @@ export class ShadowCss {
|
|||||||
|
|
||||||
if (_polyfillHostRe.test(selector)) {
|
if (_polyfillHostRe.test(selector)) {
|
||||||
const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector;
|
const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector;
|
||||||
return selector.replace(_polyfillHostNoCombinatorRe, (hnc, selector) => selector + replaceBy)
|
return selector
|
||||||
|
.replace(
|
||||||
|
_polyfillHostNoCombinatorRe,
|
||||||
|
(hnc, selector) => selector[0] === ':' ? replaceBy + selector : selector + replaceBy)
|
||||||
.replace(_polyfillHostRe, replaceBy + ' ');
|
.replace(_polyfillHostRe, replaceBy + ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
// Some of the code comes from WebComponents.JS
|
// Some of the code comes from WebComponents.JS
|
||||||
// https://github.com/webcomponents/webcomponentsjs/blob/master/src/HTMLImports/path.js
|
// https://github.com/webcomponents/webcomponentsjs/blob/master/src/HTMLImports/path.js
|
||||||
|
|
||||||
import {StringWrapper, isBlank, isPresent} from './facade/lang';
|
import {isBlank, isPresent} from './facade/lang';
|
||||||
|
|
||||||
import {UrlResolver} from './url_resolver';
|
import {UrlResolver} from './url_resolver';
|
||||||
|
|
||||||
@ -30,8 +30,8 @@ export function isStyleUrlResolvable(url: string): boolean {
|
|||||||
export function extractStyleUrls(
|
export function extractStyleUrls(
|
||||||
resolver: UrlResolver, baseUrl: string, cssText: string): StyleWithImports {
|
resolver: UrlResolver, baseUrl: string, cssText: string): StyleWithImports {
|
||||||
var foundUrls: string[] = [];
|
var foundUrls: string[] = [];
|
||||||
var modifiedCssText = StringWrapper.replaceAllMapped(cssText, _cssImportRe, (m: string[]) => {
|
var modifiedCssText = cssText.replace(_cssImportRe, function(...m: string[]) {
|
||||||
var url = isPresent(m[1]) ? m[1] : m[2];
|
const url = m[1] || m[2];
|
||||||
if (!isStyleUrlResolvable(url)) {
|
if (!isStyleUrlResolvable(url)) {
|
||||||
// Do not attempt to resolve non-package absolute URLs with URI scheme
|
// Do not attempt to resolve non-package absolute URLs with URI scheme
|
||||||
return m[0];
|
return m[0];
|
||||||
|
@ -10,7 +10,6 @@ import {SecurityContext} from '@angular/core';
|
|||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileProviderMetadata, CompileTokenMetadata} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileProviderMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||||
import {AST} from '../expression_parser/ast';
|
import {AST} from '../expression_parser/ast';
|
||||||
import {isPresent} from '../facade/lang';
|
|
||||||
import {ParseSourceSpan} from '../parse_util';
|
import {ParseSourceSpan} from '../parse_util';
|
||||||
import {LifecycleHooks} from '../private_import_core';
|
import {LifecycleHooks} from '../private_import_core';
|
||||||
|
|
||||||
@ -84,7 +83,7 @@ export class BoundEventAst implements TemplateAst {
|
|||||||
return visitor.visitEvent(this, context);
|
return visitor.visitEvent(this, context);
|
||||||
}
|
}
|
||||||
get fullName() {
|
get fullName() {
|
||||||
if (isPresent(this.target)) {
|
if (this.target) {
|
||||||
return `${this.target}:${this.name}`;
|
return `${this.target}:${this.name}`;
|
||||||
} else {
|
} else {
|
||||||
return this.name;
|
return this.name;
|
||||||
@ -124,7 +123,8 @@ export class ElementAst implements TemplateAst {
|
|||||||
public outputs: BoundEventAst[], public references: ReferenceAst[],
|
public outputs: BoundEventAst[], public references: ReferenceAst[],
|
||||||
public directives: DirectiveAst[], public providers: ProviderAst[],
|
public directives: DirectiveAst[], public providers: ProviderAst[],
|
||||||
public hasViewContainer: boolean, public children: TemplateAst[],
|
public hasViewContainer: boolean, public children: TemplateAst[],
|
||||||
public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {}
|
public ngContentIndex: number, public sourceSpan: ParseSourceSpan,
|
||||||
|
public endSourceSpan: ParseSourceSpan) {}
|
||||||
|
|
||||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
return visitor.visitElement(this, context);
|
return visitor.visitElement(this, context);
|
||||||
@ -241,6 +241,11 @@ export enum PropertyBindingType {
|
|||||||
* A visitor for {@link TemplateAst} trees that will process each node.
|
* A visitor for {@link TemplateAst} trees that will process each node.
|
||||||
*/
|
*/
|
||||||
export interface TemplateAstVisitor {
|
export interface TemplateAstVisitor {
|
||||||
|
// Returning a truthy value from `visit()` will prevent `templateVisitAll()` from the call to
|
||||||
|
// the typed method and result returned will become the result included in `visitAll()`s
|
||||||
|
// result array.
|
||||||
|
visit?(ast: TemplateAst, context: any): any;
|
||||||
|
|
||||||
visitNgContent(ast: NgContentAst, context: any): any;
|
visitNgContent(ast: NgContentAst, context: any): any;
|
||||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any;
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any;
|
||||||
visitElement(ast: ElementAst, context: any): any;
|
visitElement(ast: ElementAst, context: any): any;
|
||||||
@ -260,10 +265,13 @@ export interface TemplateAstVisitor {
|
|||||||
*/
|
*/
|
||||||
export function templateVisitAll(
|
export function templateVisitAll(
|
||||||
visitor: TemplateAstVisitor, asts: TemplateAst[], context: any = null): any[] {
|
visitor: TemplateAstVisitor, asts: TemplateAst[], context: any = null): any[] {
|
||||||
var result: any[] = [];
|
const result: any[] = [];
|
||||||
|
const visit = visitor.visit ?
|
||||||
|
(ast: TemplateAst) => visitor.visit(ast, context) || ast.visit(visitor, context) :
|
||||||
|
(ast: TemplateAst) => ast.visit(visitor, context);
|
||||||
asts.forEach(ast => {
|
asts.forEach(ast => {
|
||||||
var astResult = ast.visit(visitor, context);
|
const astResult = visit(ast);
|
||||||
if (isPresent(astResult)) {
|
if (astResult) {
|
||||||
result.push(astResult);
|
result.push(astResult);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -118,22 +118,18 @@ export class TemplateParser {
|
|||||||
component: CompileDirectiveMetadata, template: string, directives: CompileDirectiveMetadata[],
|
component: CompileDirectiveMetadata, template: string, directives: CompileDirectiveMetadata[],
|
||||||
pipes: CompilePipeMetadata[], schemas: SchemaMetadata[],
|
pipes: CompilePipeMetadata[], schemas: SchemaMetadata[],
|
||||||
templateUrl: string): TemplateParseResult {
|
templateUrl: string): TemplateParseResult {
|
||||||
let interpolationConfig: any;
|
return this.tryParseHtml(
|
||||||
if (component.template) {
|
this.expandHtml(this._htmlParser.parse(
|
||||||
interpolationConfig = InterpolationConfig.fromArray(component.template.interpolation);
|
template, templateUrl, true, this.getInterpolationConfig(component))),
|
||||||
}
|
component, template, directives, pipes, schemas, templateUrl);
|
||||||
let htmlAstWithErrors =
|
}
|
||||||
this._htmlParser.parse(template, templateUrl, true, interpolationConfig);
|
|
||||||
const errors: ParseError[] = htmlAstWithErrors.errors;
|
|
||||||
let result: TemplateAst[];
|
|
||||||
|
|
||||||
if (errors.length == 0) {
|
|
||||||
// Transform ICU messages to angular directives
|
|
||||||
const expandedHtmlAst = expandNodes(htmlAstWithErrors.rootNodes);
|
|
||||||
errors.push(...expandedHtmlAst.errors);
|
|
||||||
htmlAstWithErrors = new ParseTreeResult(expandedHtmlAst.nodes, errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
tryParseHtml(
|
||||||
|
htmlAstWithErrors: ParseTreeResult, component: CompileDirectiveMetadata, template: string,
|
||||||
|
directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||||
|
schemas: SchemaMetadata[], templateUrl: string): TemplateParseResult {
|
||||||
|
var result: TemplateAst[];
|
||||||
|
var errors = htmlAstWithErrors.errors;
|
||||||
if (htmlAstWithErrors.rootNodes.length > 0) {
|
if (htmlAstWithErrors.rootNodes.length > 0) {
|
||||||
const uniqDirectives = removeIdentifierDuplicates(directives);
|
const uniqDirectives = removeIdentifierDuplicates(directives);
|
||||||
const uniqPipes = removeIdentifierDuplicates(pipes);
|
const uniqPipes = removeIdentifierDuplicates(pipes);
|
||||||
@ -147,7 +143,6 @@ export class TemplateParser {
|
|||||||
} else {
|
} else {
|
||||||
result = [];
|
result = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this._assertNoReferenceDuplicationOnTemplate(result, errors);
|
this._assertNoReferenceDuplicationOnTemplate(result, errors);
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
@ -162,6 +157,24 @@ export class TemplateParser {
|
|||||||
return new TemplateParseResult(result, errors);
|
return new TemplateParseResult(result, errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expandHtml(htmlAstWithErrors: ParseTreeResult, forced: boolean = false): ParseTreeResult {
|
||||||
|
const errors: ParseError[] = htmlAstWithErrors.errors;
|
||||||
|
|
||||||
|
if (errors.length == 0 || forced) {
|
||||||
|
// Transform ICU messages to angular directives
|
||||||
|
const expandedHtmlAst = expandNodes(htmlAstWithErrors.rootNodes);
|
||||||
|
errors.push(...expandedHtmlAst.errors);
|
||||||
|
htmlAstWithErrors = new ParseTreeResult(expandedHtmlAst.nodes, errors);
|
||||||
|
}
|
||||||
|
return htmlAstWithErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInterpolationConfig(component: CompileDirectiveMetadata): InterpolationConfig {
|
||||||
|
if (component.template) {
|
||||||
|
return InterpolationConfig.fromArray(component.template.interpolation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_assertNoReferenceDuplicationOnTemplate(result: TemplateAst[], errors: TemplateParseError[]):
|
_assertNoReferenceDuplicationOnTemplate(result: TemplateAst[], errors: TemplateParseError[]):
|
||||||
void {
|
void {
|
||||||
@ -415,10 +428,8 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
let parsedElement: TemplateAst;
|
let parsedElement: TemplateAst;
|
||||||
|
|
||||||
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
||||||
if (isPresent(element.children) && element.children.length > 0) {
|
if (element.children && !element.children.every(_isEmptyTextNode)) {
|
||||||
this._reportError(
|
this._reportError(`<ng-content> element cannot have content.`, element.sourceSpan);
|
||||||
`<ng-content> element cannot have content. <ng-content> must be immediately followed by </ng-content>`,
|
|
||||||
element.sourceSpan);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedElement = new NgContentAst(
|
parsedElement = new NgContentAst(
|
||||||
@ -442,7 +453,7 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
nodeName, attrs, elementProps, events, references,
|
nodeName, attrs, elementProps, events, references,
|
||||||
providerContext.transformedDirectiveAsts, providerContext.transformProviders,
|
providerContext.transformedDirectiveAsts, providerContext.transformProviders,
|
||||||
providerContext.transformedHasViewContainer, children,
|
providerContext.transformedHasViewContainer, children,
|
||||||
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan, element.endSourceSpan);
|
||||||
|
|
||||||
this._findComponentDirectives(directiveAsts)
|
this._findComponentDirectives(directiveAsts)
|
||||||
.forEach(
|
.forEach(
|
||||||
@ -1083,7 +1094,7 @@ class NonBindableVisitor implements html.Visitor {
|
|||||||
const children = html.visitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT);
|
const children = html.visitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT);
|
||||||
return new ElementAst(
|
return new ElementAst(
|
||||||
ast.name, html.visitAll(this, ast.attrs), [], [], [], [], [], false, children,
|
ast.name, html.visitAll(this, ast.attrs), [], [], [], [], [], false, children,
|
||||||
ngContentIndex, ast.sourceSpan);
|
ngContentIndex, ast.sourceSpan, ast.endSourceSpan);
|
||||||
}
|
}
|
||||||
visitComment(comment: html.Comment, context: any): any { return null; }
|
visitComment(comment: html.Comment, context: any): any { return null; }
|
||||||
|
|
||||||
@ -1188,3 +1199,7 @@ export class PipeCollector extends RecursiveAstVisitor {
|
|||||||
function _isAnimationLabel(name: string): boolean {
|
function _isAnimationLabel(name: string): boolean {
|
||||||
return name[0] == '@';
|
return name[0] == '@';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _isEmptyTextNode(node: html.Node): boolean {
|
||||||
|
return node instanceof html.Text && node.value.trim().length == 0;
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {Inject, Injectable, PACKAGE_ROOT_URL} from '@angular/core';
|
import {Inject, Injectable, PACKAGE_ROOT_URL} from '@angular/core';
|
||||||
|
|
||||||
import {StringWrapper, isBlank, isPresent} from './facade/lang';
|
import {isBlank, isPresent} from './facade/lang';
|
||||||
|
|
||||||
|
|
||||||
const _ASSET_SCHEME = 'asset:';
|
const _ASSET_SCHEME = 'asset:';
|
||||||
@ -74,8 +74,8 @@ export class UrlResolver {
|
|||||||
var pathSegements = path.split(/\//);
|
var pathSegements = path.split(/\//);
|
||||||
resolvedUrl = `asset:${pathSegements[0]}/lib/${pathSegements.slice(1).join('/')}`;
|
resolvedUrl = `asset:${pathSegements[0]}/lib/${pathSegements.slice(1).join('/')}`;
|
||||||
} else {
|
} else {
|
||||||
prefix = StringWrapper.stripRight(prefix, '/');
|
prefix = prefix.replace(/\/+$/, '');
|
||||||
path = StringWrapper.stripLeft(path, '/');
|
path = path.replace(/^\/+/, '');
|
||||||
return `${prefix}/${path}`;
|
return `${prefix}/${path}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileTokenMetadata} from './compile_metadata';
|
import {CompileTokenMetadata} from './compile_metadata';
|
||||||
import {StringWrapper, isArray, isBlank, isPresent, isPrimitive, isStrictStringMap} from './facade/lang';
|
import {isArray, isBlank, isPresent, isPrimitive, isStrictStringMap} from './facade/lang';
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
|
|
||||||
export const MODULE_SUFFIX = '';
|
export const MODULE_SUFFIX = '';
|
||||||
@ -15,8 +15,7 @@ export const MODULE_SUFFIX = '';
|
|||||||
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||||
|
|
||||||
export function camelCaseToDashCase(input: string): string {
|
export function camelCaseToDashCase(input: string): string {
|
||||||
return StringWrapper.replaceAllMapped(
|
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
|
||||||
input, CAMEL_CASE_REGEXP, (m: string[]) => '-' + m[1].toLowerCase());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
||||||
@ -34,7 +33,7 @@ function _splitAt(input: string, character: string, defaultValues: string[]): st
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function sanitizeIdentifier(name: string): string {
|
export function sanitizeIdentifier(name: string): string {
|
||||||
return StringWrapper.replaceAll(name, /\W/g, '_');
|
return name.replace(/\W/g, '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function visitValue(value: any, visitor: ValueVisitor, context: any): any {
|
export function visitValue(value: any, visitor: ValueVisitor, context: any): any {
|
||||||
@ -61,9 +60,8 @@ export class ValueTransformer implements ValueVisitor {
|
|||||||
return arr.map(value => visitValue(value, this, context));
|
return arr.map(value => visitValue(value, this, context));
|
||||||
}
|
}
|
||||||
visitStringMap(map: {[key: string]: any}, context: any): any {
|
visitStringMap(map: {[key: string]: any}, context: any): any {
|
||||||
var result = {};
|
var result: {[key: string]: any} = {};
|
||||||
Object.keys(map).forEach(
|
Object.keys(map).forEach(key => { result[key] = visitValue(map[key], this, context); });
|
||||||
key => { (result as any /** TODO #9100 */)[key] = visitValue(map[key], this, context); });
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
visitPrimitive(value: any, context: any): any { return value; }
|
visitPrimitive(value: any, context: any): any { return value; }
|
||||||
|
@ -161,11 +161,11 @@ export class CompileElement extends CompileNode {
|
|||||||
resolvedProvider.providerType,
|
resolvedProvider.providerType,
|
||||||
new CompileDiDependencyMetadata({token: provider.useExisting}));
|
new CompileDiDependencyMetadata({token: provider.useExisting}));
|
||||||
} else if (isPresent(provider.useFactory)) {
|
} else if (isPresent(provider.useFactory)) {
|
||||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
|
var deps = provider.deps || provider.useFactory.diDeps;
|
||||||
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
||||||
return o.importExpr(provider.useFactory).callFn(depsExpr);
|
return o.importExpr(provider.useFactory).callFn(depsExpr);
|
||||||
} else if (isPresent(provider.useClass)) {
|
} else if (isPresent(provider.useClass)) {
|
||||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
|
var deps = provider.deps || provider.useClass.diDeps;
|
||||||
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
||||||
return o.importExpr(provider.useClass)
|
return o.importExpr(provider.useClass)
|
||||||
.instantiate(depsExpr, o.importType(provider.useClass));
|
.instantiate(depsExpr, o.importType(provider.useClass));
|
||||||
@ -435,6 +435,6 @@ function createProviderProperty(
|
|||||||
class _QueryWithRead {
|
class _QueryWithRead {
|
||||||
public read: CompileTokenMetadata;
|
public read: CompileTokenMetadata;
|
||||||
constructor(public query: CompileQuery, match: CompileTokenMetadata) {
|
constructor(public query: CompileQuery, match: CompileTokenMetadata) {
|
||||||
this.read = isPresent(query.meta.read) ? query.meta.read : match;
|
this.read = query.meta.read || match;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ export class CompileMethod {
|
|||||||
|
|
||||||
resetDebugInfoExpr(nodeIndex: number, templateAst: TemplateAst): o.Expression {
|
resetDebugInfoExpr(nodeIndex: number, templateAst: TemplateAst): o.Expression {
|
||||||
var res = this._updateDebugContext(new _DebugState(nodeIndex, templateAst));
|
var res = this._updateDebugContext(new _DebugState(nodeIndex, templateAst));
|
||||||
return isPresent(res) ? res : o.NULL_EXPR;
|
return res || o.NULL_EXPR;
|
||||||
}
|
}
|
||||||
|
|
||||||
resetDebugInfo(nodeIndex: number, templateAst: TemplateAst) {
|
resetDebugInfo(nodeIndex: number, templateAst: TemplateAst) {
|
||||||
|
@ -64,7 +64,7 @@ export class CompileQuery {
|
|||||||
return !this._values.values.some(value => value instanceof ViewQueryValues);
|
return !this._values.values.some(value => value instanceof ViewQueryValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
afterChildren(targetStaticMethod: any /** TODO #9100 */, targetDynamicMethod: CompileMethod) {
|
afterChildren(targetStaticMethod: CompileMethod, targetDynamicMethod: CompileMethod) {
|
||||||
var values = createQueryValues(this._values);
|
var values = createQueryValues(this._values);
|
||||||
var updateStmts = [this.queryList.callMethod('reset', [o.literalArr(values)]).toStmt()];
|
var updateStmts = [this.queryList.callMethod('reset', [o.literalArr(values)]).toStmt()];
|
||||||
if (isPresent(this.ownerDirectiveExpression)) {
|
if (isPresent(this.ownerDirectiveExpression)) {
|
||||||
|
@ -169,16 +169,16 @@ export class CompileView implements NameResolver {
|
|||||||
return proxyExpr.callFn(values);
|
return proxyExpr.callFn(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
createLiteralMap(entries: Array<Array<string|o.Expression>>): o.Expression {
|
createLiteralMap(entries: [string, o.Expression][]): o.Expression {
|
||||||
if (entries.length === 0) {
|
if (entries.length === 0) {
|
||||||
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_MAP));
|
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_MAP));
|
||||||
}
|
}
|
||||||
var proxyExpr = o.THIS_EXPR.prop(`_map_${this.literalMapCount++}`);
|
const proxyExpr = o.THIS_EXPR.prop(`_map_${this.literalMapCount++}`);
|
||||||
var proxyParams: o.FnParam[] = [];
|
const proxyParams: o.FnParam[] = [];
|
||||||
var proxyReturnEntries: Array<Array<string|o.Expression>> = [];
|
const proxyReturnEntries: [string, o.Expression][] = [];
|
||||||
var values: o.Expression[] = [];
|
const values: o.Expression[] = [];
|
||||||
for (var i = 0; i < entries.length; i++) {
|
for (var i = 0; i < entries.length; i++) {
|
||||||
var paramName = `p${i}`;
|
const paramName = `p${i}`;
|
||||||
proxyParams.push(new o.FnParam(paramName));
|
proxyParams.push(new o.FnParam(paramName));
|
||||||
proxyReturnEntries.push([entries[i][0], o.variable(paramName)]);
|
proxyReturnEntries.push([entries[i][0], o.variable(paramName)]);
|
||||||
values.push(<o.Expression>entries[i][1]);
|
values.push(<o.Expression>entries[i][1]);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileDirectiveMetadata} from '../compile_metadata';
|
import {CompileDirectiveMetadata} from '../compile_metadata';
|
||||||
import {StringWrapper, isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {identifierToken} from '../identifiers';
|
import {identifierToken} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
|
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
|
||||||
@ -46,7 +46,7 @@ export class CompileEventListener {
|
|||||||
public eventPhase: string, listenerIndex: number) {
|
public eventPhase: string, listenerIndex: number) {
|
||||||
this._method = new CompileMethod(compileElement.view);
|
this._method = new CompileMethod(compileElement.view);
|
||||||
this._methodName =
|
this._methodName =
|
||||||
`_handle_${santitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`;
|
`_handle_${sanitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`;
|
||||||
this._eventParam = new o.FnParam(
|
this._eventParam = new o.FnParam(
|
||||||
EventHandlerVars.event.name,
|
EventHandlerVars.event.name,
|
||||||
o.importType(this.compileElement.view.genConfig.renderTypes.renderEvent));
|
o.importType(this.compileElement.view.genConfig.renderTypes.renderEvent));
|
||||||
@ -59,8 +59,7 @@ export class CompileEventListener {
|
|||||||
this._hasComponentHostListener = true;
|
this._hasComponentHostListener = true;
|
||||||
}
|
}
|
||||||
this._method.resetDebugInfo(this.compileElement.nodeIndex, hostEvent);
|
this._method.resetDebugInfo(this.compileElement.nodeIndex, hostEvent);
|
||||||
var context = isPresent(directiveInstance) ? directiveInstance :
|
var context = directiveInstance || this.compileElement.view.componentContext;
|
||||||
this.compileElement.view.componentContext;
|
|
||||||
var actionStmts = convertCdStatementToIr(
|
var actionStmts = convertCdStatementToIr(
|
||||||
this.compileElement.view, context, hostEvent.handler, this.compileElement.nodeIndex);
|
this.compileElement.view, context, hostEvent.handler, this.compileElement.nodeIndex);
|
||||||
var lastIndex = actionStmts.length - 1;
|
var lastIndex = actionStmts.length - 1;
|
||||||
@ -96,7 +95,7 @@ export class CompileEventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
listenToRenderer() {
|
listenToRenderer() {
|
||||||
var listenExpr: any /** TODO #9100 */;
|
var listenExpr: o.Expression;
|
||||||
var eventListener = o.THIS_EXPR.callMethod(
|
var eventListener = o.THIS_EXPR.callMethod(
|
||||||
'eventHandler',
|
'eventHandler',
|
||||||
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]);
|
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]);
|
||||||
@ -148,13 +147,15 @@ export class CompileEventListener {
|
|||||||
export function collectEventListeners(
|
export function collectEventListeners(
|
||||||
hostEvents: BoundEventAst[], dirs: DirectiveAst[],
|
hostEvents: BoundEventAst[], dirs: DirectiveAst[],
|
||||||
compileElement: CompileElement): CompileEventListener[] {
|
compileElement: CompileElement): CompileEventListener[] {
|
||||||
var eventListeners: CompileEventListener[] = [];
|
const eventListeners: CompileEventListener[] = [];
|
||||||
|
|
||||||
hostEvents.forEach((hostEvent) => {
|
hostEvents.forEach((hostEvent) => {
|
||||||
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
|
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
|
||||||
var listener = CompileEventListener.getOrCreate(
|
var listener = CompileEventListener.getOrCreate(
|
||||||
compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
|
compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
|
||||||
listener.addAction(hostEvent, null, null);
|
listener.addAction(hostEvent, null, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
dirs.forEach((directiveAst) => {
|
dirs.forEach((directiveAst) => {
|
||||||
var directiveInstance =
|
var directiveInstance =
|
||||||
compileElement.instances.get(identifierToken(directiveAst.directive.type).reference);
|
compileElement.instances.get(identifierToken(directiveAst.directive.type).reference);
|
||||||
@ -165,6 +166,7 @@ export function collectEventListeners(
|
|||||||
listener.addAction(hostEvent, directiveAst.directive, directiveInstance);
|
listener.addAction(hostEvent, directiveAst.directive, directiveInstance);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
eventListeners.forEach((listener) => listener.finishMethod());
|
eventListeners.forEach((listener) => listener.finishMethod());
|
||||||
return eventListeners;
|
return eventListeners;
|
||||||
}
|
}
|
||||||
@ -174,6 +176,7 @@ export function bindDirectiveOutputs(
|
|||||||
eventListeners: CompileEventListener[]) {
|
eventListeners: CompileEventListener[]) {
|
||||||
Object.keys(directiveAst.directive.outputs).forEach(observablePropName => {
|
Object.keys(directiveAst.directive.outputs).forEach(observablePropName => {
|
||||||
const eventName = directiveAst.directive.outputs[observablePropName];
|
const eventName = directiveAst.directive.outputs[observablePropName];
|
||||||
|
|
||||||
eventListeners.filter(listener => listener.eventName == eventName).forEach((listener) => {
|
eventListeners.filter(listener => listener.eventName == eventName).forEach((listener) => {
|
||||||
listener.listenToDirective(directiveInstance, observablePropName);
|
listener.listenToDirective(directiveInstance, observablePropName);
|
||||||
});
|
});
|
||||||
@ -199,6 +202,6 @@ function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function santitizeEventName(name: string): string {
|
function sanitizeEventName(name: string): string {
|
||||||
return StringWrapper.replaceAll(name, /[^a-zA-Z_]/g, '_');
|
return name.replace(/[^a-zA-Z_]/g, '_');
|
||||||
}
|
}
|
||||||
|
@ -99,10 +99,9 @@ function bindAndWriteToRenderer(
|
|||||||
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
|
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
|
||||||
var fieldExpr = createBindFieldExpr(bindingIndex);
|
var fieldExpr = createBindFieldExpr(bindingIndex);
|
||||||
var currValExpr = createCurrValueExpr(bindingIndex);
|
var currValExpr = createCurrValueExpr(bindingIndex);
|
||||||
var renderMethod: string;
|
|
||||||
var oldRenderValue: o.Expression = sanitizedValue(boundProp, fieldExpr);
|
var oldRenderValue: o.Expression = sanitizedValue(boundProp, fieldExpr);
|
||||||
var renderValue: o.Expression = sanitizedValue(boundProp, currValExpr);
|
var renderValue: o.Expression = sanitizedValue(boundProp, currValExpr);
|
||||||
var updateStmts: any[] /** TODO #9100 */ = [];
|
var updateStmts: o.Statement[] = [];
|
||||||
var compileMethod = view.detectChangesRenderPropertiesMethod;
|
var compileMethod = view.detectChangesRenderPropertiesMethod;
|
||||||
switch (boundProp.type) {
|
switch (boundProp.type) {
|
||||||
case PropertyBindingType.Property:
|
case PropertyBindingType.Property:
|
||||||
|
@ -57,7 +57,7 @@ export function getViewFactoryName(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createFlatArray(expressions: o.Expression[]): o.Expression {
|
export function createFlatArray(expressions: o.Expression[]): o.Expression {
|
||||||
var lastNonArrayExpressions: any[] /** TODO #9100 */ = [];
|
var lastNonArrayExpressions: o.Expression[] = [];
|
||||||
var result: o.Expression = o.literalArr([]);
|
var result: o.Expression = o.literalArr([]);
|
||||||
for (var i = 0; i < expressions.length; i++) {
|
for (var i = 0; i < expressions.length; i++) {
|
||||||
var expr = expressions[i];
|
var expr = expressions[i];
|
||||||
|
@ -10,7 +10,7 @@ import {ViewEncapsulation} from '@angular/core';
|
|||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||||
import {ListWrapper} from '../facade/collection';
|
import {ListWrapper} from '../facade/collection';
|
||||||
import {StringWrapper, isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
|
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {ChangeDetectorStatus, ViewType, isDefaultChangeDetectionStrategy} from '../private_import_core';
|
import {ChangeDetectorStatus, ViewType, isDefaultChangeDetectionStrategy} from '../private_import_core';
|
||||||
@ -79,10 +79,10 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
|||||||
if (this._isRootNode(parent)) {
|
if (this._isRootNode(parent)) {
|
||||||
// store appElement as root node only for ViewContainers
|
// store appElement as root node only for ViewContainers
|
||||||
if (this.view.viewType !== ViewType.COMPONENT) {
|
if (this.view.viewType !== ViewType.COMPONENT) {
|
||||||
this.view.rootNodesOrAppElements.push(isPresent(vcAppEl) ? vcAppEl : node.renderNode);
|
this.view.rootNodesOrAppElements.push(vcAppEl || node.renderNode);
|
||||||
}
|
}
|
||||||
} else if (isPresent(parent.component) && isPresent(ngContentIndex)) {
|
} else if (isPresent(parent.component) && isPresent(ngContentIndex)) {
|
||||||
parent.addContentNode(ngContentIndex, isPresent(vcAppEl) ? vcAppEl : node.renderNode);
|
parent.addContentNode(ngContentIndex, vcAppEl || node.renderNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,7 +376,7 @@ function mapToKeyValueArray(data: {[key: string]: string}): string[][] {
|
|||||||
Object.keys(data).forEach(name => { entryArray.push([name, data[name]]); });
|
Object.keys(data).forEach(name => { entryArray.push([name, data[name]]); });
|
||||||
// We need to sort to get a defined output order
|
// We need to sort to get a defined output order
|
||||||
// for tests and for caching generated artifacts...
|
// for tests and for caching generated artifacts...
|
||||||
ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
|
ListWrapper.sort(entryArray);
|
||||||
return entryArray;
|
return entryArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,10 +511,13 @@ function createViewFactory(
|
|||||||
templateUrlInfo = view.component.template.templateUrl;
|
templateUrlInfo = view.component.template.templateUrl;
|
||||||
}
|
}
|
||||||
if (view.viewIndex === 0) {
|
if (view.viewIndex === 0) {
|
||||||
var animationsExpr = o.literalMap(view.animations.map(entry => [entry.name, entry.fnExp]));
|
var animationsExpr = o.literalMap(
|
||||||
initRenderCompTypeStmts = [new o.IfStmt(
|
view.animations.map((entry): [string, o.Expression] => [entry.name, entry.fnExp]));
|
||||||
|
initRenderCompTypeStmts = [
|
||||||
|
new o.IfStmt(
|
||||||
renderCompTypeVar.identical(o.NULL_EXPR),
|
renderCompTypeVar.identical(o.NULL_EXPR),
|
||||||
[renderCompTypeVar
|
[
|
||||||
|
renderCompTypeVar
|
||||||
.set(ViewConstructorVars.viewUtils.callMethod(
|
.set(ViewConstructorVars.viewUtils.callMethod(
|
||||||
'createRenderComponentType',
|
'createRenderComponentType',
|
||||||
[
|
[
|
||||||
@ -524,13 +527,16 @@ function createViewFactory(
|
|||||||
view.styles,
|
view.styles,
|
||||||
animationsExpr,
|
animationsExpr,
|
||||||
]))
|
]))
|
||||||
.toStmt()])];
|
.toStmt(),
|
||||||
|
]),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return o
|
return o
|
||||||
.fn(viewFactoryArgs, initRenderCompTypeStmts.concat([new o.ReturnStatement(
|
.fn(viewFactoryArgs, initRenderCompTypeStmts.concat([
|
||||||
o.variable(viewClass.name)
|
new o.ReturnStatement(o.variable(viewClass.name)
|
||||||
.instantiate(viewClass.constructorMethod.params.map(
|
.instantiate(viewClass.constructorMethod.params.map(
|
||||||
(param) => o.variable(param.name))))]),
|
(param) => o.variable(param.name)))),
|
||||||
|
]),
|
||||||
o.importType(resolveIdentifier(Identifiers.AppView), [getContextType(view)]))
|
o.importType(resolveIdentifier(Identifiers.AppView), [getContextType(view)]))
|
||||||
.toDeclStmt(view.viewFactory.name, [o.StmtModifier.Final]);
|
.toDeclStmt(view.viewFactory.name, [o.StmtModifier.Final]);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import {CompileMetadataResolver} from '../../src/metadata_resolver';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('RuntimeAnimationCompiler', () => {
|
describe('RuntimeAnimationCompiler', () => {
|
||||||
var resolver: any /** TODO #9100 */;
|
var resolver: CompileMetadataResolver;
|
||||||
beforeEach(
|
beforeEach(
|
||||||
inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; }));
|
inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; }));
|
||||||
|
|
||||||
|
@ -28,9 +28,9 @@ export function main() {
|
|||||||
(keyframe: AnimationKeyframeAst): {[key: string]: string | number} =>
|
(keyframe: AnimationKeyframeAst): {[key: string]: string | number} =>
|
||||||
combineStyles(keyframe.styles);
|
combineStyles(keyframe.styles);
|
||||||
|
|
||||||
var collectStepStyles = (step: AnimationStepAst): Array<{[key: string]: string | number}> => {
|
var collectStepStyles = (step: AnimationStepAst): {[key: string]: string | number}[] => {
|
||||||
var keyframes = step.keyframes;
|
var keyframes = step.keyframes;
|
||||||
var styles: any[] /** TODO #9100 */ = [];
|
var styles: {[key: string]: string | number}[] = [];
|
||||||
if (step.startingStyles.styles.length > 0) {
|
if (step.startingStyles.styles.length > 0) {
|
||||||
styles.push(combineStyles(step.startingStyles));
|
styles.push(combineStyles(step.startingStyles));
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ export function main() {
|
|||||||
return styles;
|
return styles;
|
||||||
};
|
};
|
||||||
|
|
||||||
var resolver: any /** TODO #9100 */;
|
var resolver: CompileMetadataResolver;
|
||||||
beforeEach(
|
beforeEach(
|
||||||
inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; }));
|
inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; }));
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ export function main() {
|
|||||||
it('should throw an error if a selector is being parsed while in the wrong mode', () => {
|
it('should throw an error if a selector is being parsed while in the wrong mode', () => {
|
||||||
var cssCode = '.class > tag';
|
var cssCode = '.class > tag';
|
||||||
|
|
||||||
var capturedMessage: any /** TODO #9100 */;
|
var capturedMessage: string;
|
||||||
try {
|
try {
|
||||||
tokenize(cssCode, false, CssLexerMode.STYLE_BLOCK);
|
tokenize(cssCode, false, CssLexerMode.STYLE_BLOCK);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -298,7 +298,7 @@ export function main() {
|
|||||||
describe('Attribute Mode', () => {
|
describe('Attribute Mode', () => {
|
||||||
it('should consider attribute selectors as valid input and throw when an invalid modifier is used',
|
it('should consider attribute selectors as valid input and throw when an invalid modifier is used',
|
||||||
() => {
|
() => {
|
||||||
function tokenizeAttr(modifier: any /** TODO #9100 */) {
|
function tokenizeAttr(modifier: string) {
|
||||||
var cssCode = 'value' + modifier + '=\'something\'';
|
var cssCode = 'value' + modifier + '=\'something\'';
|
||||||
return tokenize(cssCode, false, CssLexerMode.ATTRIBUTE_SELECTOR);
|
return tokenize(cssCode, false, CssLexerMode.ATTRIBUTE_SELECTOR);
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ class MyVisitor implements CssAstVisitor {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
_capture(method: string, ast: CssAst, context: any) {
|
_capture(method: string, ast: CssAst, context: any) {
|
||||||
this.captures[method] = isPresent(this.captures[method]) ? this.captures[method] : [];
|
this.captures[method] = this.captures[method] || [];
|
||||||
this.captures[method].push([ast, context]);
|
this.captures[method].push([ast, context]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {hasLifecycleHook} from '@angular/compiler/src/lifecycle_reflector';
|
import {hasLifecycleHook} from '@angular/compiler/src/lifecycle_reflector';
|
||||||
import {LifecycleHooks} from '@angular/core/src/metadata/lifecycle_hooks';
|
import {SimpleChanges} from '@angular/core';
|
||||||
import {describe, expect, it} from '@angular/core/testing/testing_internal';
|
import {LifecycleHooks as Hooks} from '@angular/core/src/metadata/lifecycle_hooks';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('Create Directive', () => {
|
describe('Create Directive', () => {
|
||||||
@ -16,92 +16,81 @@ export function main() {
|
|||||||
|
|
||||||
describe('ngOnChanges', () => {
|
describe('ngOnChanges', () => {
|
||||||
it('should be true when the directive has the ngOnChanges method', () => {
|
it('should be true when the directive has the ngOnChanges method', () => {
|
||||||
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveWithOnChangesMethod))
|
expect(hasLifecycleHook(Hooks.OnChanges, DirectiveWithOnChangesMethod)).toBe(true);
|
||||||
.toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be false otherwise', () => {
|
it('should be false otherwise',
|
||||||
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveNoHooks)).toBe(false);
|
() => { expect(hasLifecycleHook(Hooks.OnChanges, DirectiveNoHooks)).toBe(false); });
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngOnDestroy', () => {
|
describe('ngOnDestroy', () => {
|
||||||
it('should be true when the directive has the ngOnDestroy method', () => {
|
it('should be true when the directive has the ngOnDestroy method', () => {
|
||||||
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveWithOnDestroyMethod))
|
expect(hasLifecycleHook(Hooks.OnDestroy, DirectiveWithOnDestroyMethod)).toBe(true);
|
||||||
.toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be false otherwise', () => {
|
it('should be false otherwise',
|
||||||
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveNoHooks)).toBe(false);
|
() => { expect(hasLifecycleHook(Hooks.OnDestroy, DirectiveNoHooks)).toBe(false); });
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngOnInit', () => {
|
describe('ngOnInit', () => {
|
||||||
it('should be true when the directive has the ngOnInit method', () => {
|
it('should be true when the directive has the ngOnInit method',
|
||||||
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveWithOnInitMethod)).toBe(true);
|
() => { expect(hasLifecycleHook(Hooks.OnInit, DirectiveWithOnInitMethod)).toBe(true); });
|
||||||
});
|
|
||||||
|
|
||||||
it('should be false otherwise', () => {
|
it('should be false otherwise',
|
||||||
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveNoHooks)).toBe(false);
|
() => { expect(hasLifecycleHook(Hooks.OnInit, DirectiveNoHooks)).toBe(false); });
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngDoCheck', () => {
|
describe('ngDoCheck', () => {
|
||||||
it('should be true when the directive has the ngDoCheck method', () => {
|
it('should be true when the directive has the ngDoCheck method', () => {
|
||||||
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveWithOnCheckMethod)).toBe(true);
|
expect(hasLifecycleHook(Hooks.DoCheck, DirectiveWithOnCheckMethod)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be false otherwise', () => {
|
it('should be false otherwise',
|
||||||
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveNoHooks)).toBe(false);
|
() => { expect(hasLifecycleHook(Hooks.DoCheck, DirectiveNoHooks)).toBe(false); });
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngAfterContentInit', () => {
|
describe('ngAfterContentInit', () => {
|
||||||
it('should be true when the directive has the ngAfterContentInit method', () => {
|
it('should be true when the directive has the ngAfterContentInit method', () => {
|
||||||
expect(hasLifecycleHook(
|
expect(hasLifecycleHook(Hooks.AfterContentInit, DirectiveWithAfterContentInitMethod))
|
||||||
LifecycleHooks.AfterContentInit, DirectiveWithAfterContentInitMethod))
|
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be false otherwise', () => {
|
it('should be false otherwise', () => {
|
||||||
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit, DirectiveNoHooks)).toBe(false);
|
expect(hasLifecycleHook(Hooks.AfterContentInit, DirectiveNoHooks)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngAfterContentChecked', () => {
|
describe('ngAfterContentChecked', () => {
|
||||||
it('should be true when the directive has the ngAfterContentChecked method', () => {
|
it('should be true when the directive has the ngAfterContentChecked method', () => {
|
||||||
expect(hasLifecycleHook(
|
expect(
|
||||||
LifecycleHooks.AfterContentChecked, DirectiveWithAfterContentCheckedMethod))
|
hasLifecycleHook(Hooks.AfterContentChecked, DirectiveWithAfterContentCheckedMethod))
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be false otherwise', () => {
|
it('should be false otherwise', () => {
|
||||||
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked, DirectiveNoHooks))
|
expect(hasLifecycleHook(Hooks.AfterContentChecked, DirectiveNoHooks)).toBe(false);
|
||||||
.toBe(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('ngAfterViewInit', () => {
|
describe('ngAfterViewInit', () => {
|
||||||
it('should be true when the directive has the ngAfterViewInit method', () => {
|
it('should be true when the directive has the ngAfterViewInit method', () => {
|
||||||
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveWithAfterViewInitMethod))
|
expect(hasLifecycleHook(Hooks.AfterViewInit, DirectiveWithAfterViewInitMethod))
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be false otherwise', () => {
|
it('should be false otherwise',
|
||||||
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveNoHooks)).toBe(false);
|
() => { expect(hasLifecycleHook(Hooks.AfterViewInit, DirectiveNoHooks)).toBe(false); });
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngAfterViewChecked', () => {
|
describe('ngAfterViewChecked', () => {
|
||||||
it('should be true when the directive has the ngAfterViewChecked method', () => {
|
it('should be true when the directive has the ngAfterViewChecked method', () => {
|
||||||
expect(hasLifecycleHook(
|
expect(hasLifecycleHook(Hooks.AfterViewChecked, DirectiveWithAfterViewCheckedMethod))
|
||||||
LifecycleHooks.AfterViewChecked, DirectiveWithAfterViewCheckedMethod))
|
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be false otherwise', () => {
|
it('should be false otherwise', () => {
|
||||||
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked, DirectiveNoHooks)).toBe(false);
|
expect(hasLifecycleHook(Hooks.AfterViewChecked, DirectiveNoHooks)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -111,7 +100,7 @@ export function main() {
|
|||||||
class DirectiveNoHooks {}
|
class DirectiveNoHooks {}
|
||||||
|
|
||||||
class DirectiveWithOnChangesMethod {
|
class DirectiveWithOnChangesMethod {
|
||||||
ngOnChanges(_: any /** TODO #9100 */) {}
|
ngOnChanges(_: SimpleChanges) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DirectiveWithOnInitMethod {
|
class DirectiveWithOnInitMethod {
|
||||||
|
@ -435,7 +435,7 @@ export function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function programResourceLoaderSpy(spy: SpyResourceLoader, results: {[key: string]: string}) {
|
function programResourceLoaderSpy(spy: SpyResourceLoader, results: {[key: string]: string}) {
|
||||||
spy.spy('get').andCallFake((url: string): Promise<any> => {
|
spy.spy('get').and.callFake((url: string): Promise<any> => {
|
||||||
var result = results[url];
|
var result = results[url];
|
||||||
if (result) {
|
if (result) {
|
||||||
return Promise.resolve(result);
|
return Promise.resolve(result);
|
||||||
|
@ -117,6 +117,18 @@ class SomeDirectiveWithSameHostBindingAndInput {
|
|||||||
@Input() @HostBinding() prop: any;
|
@Input() @HostBinding() prop: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Directive({selector: 'someDirective'})
|
||||||
|
class SomeDirectiveWithMalformedHostBinding1 {
|
||||||
|
@HostBinding('(a)')
|
||||||
|
onA() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: 'someDirective'})
|
||||||
|
class SomeDirectiveWithMalformedHostBinding2 {
|
||||||
|
@HostBinding('[a]')
|
||||||
|
onA() {}
|
||||||
|
}
|
||||||
|
|
||||||
class SomeDirectiveWithoutMetadata {}
|
class SomeDirectiveWithoutMetadata {}
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
@ -210,6 +222,17 @@ export function main() {
|
|||||||
expect(directiveMetadata.host)
|
expect(directiveMetadata.host)
|
||||||
.toEqual({'(c)': 'onC()', '(a)': 'onA()', '(b)': 'onB($event.value)'});
|
.toEqual({'(c)': 'onC()', '(a)': 'onA()', '(b)': 'onB($event.value)'});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw when @HostBinding name starts with "("', () => {
|
||||||
|
expect(() => resolver.resolve(SomeDirectiveWithMalformedHostBinding1))
|
||||||
|
.toThrowError('@HostBinding can not bind to events. Use @HostListener instead.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when @HostBinding name starts with "["', () => {
|
||||||
|
expect(() => resolver.resolve(SomeDirectiveWithMalformedHostBinding2))
|
||||||
|
.toThrowError(
|
||||||
|
`@HostBinding parameter should be a property name, 'class.<name>', or 'attr.<name>'.`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('queries', () => {
|
describe('queries', () => {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {ASTWithSource, BindingPipe, Interpolation, ParserError, TemplateBinding} from '@angular/compiler/src/expression_parser/ast';
|
import {ASTWithSource, BindingPipe, Interpolation, ParserError, TemplateBinding} from '@angular/compiler/src/expression_parser/ast';
|
||||||
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
|
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
|
||||||
import {Parser, TemplateBindingParseResult} from '@angular/compiler/src/expression_parser/parser';
|
import {Parser, SplitInterpolation, TemplateBindingParseResult} from '@angular/compiler/src/expression_parser/parser';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
import {isBlank, isPresent} from '../../src/facade/lang';
|
import {isBlank, isPresent} from '../../src/facade/lang';
|
||||||
@ -39,6 +39,10 @@ export function main() {
|
|||||||
return createParser().parseInterpolation(text, location);
|
return createParser().parseInterpolation(text, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function splitInterpolation(text: string, location: any = null): SplitInterpolation {
|
||||||
|
return createParser().splitInterpolation(text, location);
|
||||||
|
}
|
||||||
|
|
||||||
function parseSimpleBinding(text: string, location: any = null): ASTWithSource {
|
function parseSimpleBinding(text: string, location: any = null): ASTWithSource {
|
||||||
return createParser().parseSimpleBinding(text, location);
|
return createParser().parseSimpleBinding(text, location);
|
||||||
}
|
}
|
||||||
@ -539,5 +543,18 @@ export function main() {
|
|||||||
it('should be able to recover from a missing selector in a array literal',
|
it('should be able to recover from a missing selector in a array literal',
|
||||||
() => recover('[[a.], b, c]'));
|
() => recover('[[a.], b, c]'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('offsets', () => {
|
||||||
|
it('should retain the offsets of an interpolation', () => {
|
||||||
|
const interpolations = splitInterpolation('{{a}} {{b}} {{c}}');
|
||||||
|
expect(interpolations.offsets).toEqual([2, 9, 16]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should retain the offsets into the expression AST of interpolations', () => {
|
||||||
|
const source = parseInterpolation('{{a}} {{b}} {{c}}');
|
||||||
|
const interpolation = source.ast as Interpolation;
|
||||||
|
expect(interpolation.expressions.map(e => e.span.start)).toEqual([2, 9, 16]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {AST, AstVisitor, Binary, BindingPipe, Chain, Conditional, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '../../src/expression_parser/ast';
|
import {AST, AstVisitor, Binary, BindingPipe, Chain, Conditional, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '../../src/expression_parser/ast';
|
||||||
import {StringWrapper, isString} from '../../src/facade/lang';
|
import {isString} from '../../src/facade/lang';
|
||||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../src/ml_parser/interpolation_config';
|
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../src/ml_parser/interpolation_config';
|
||||||
|
|
||||||
class Unparser implements AstVisitor {
|
class Unparser implements AstVisitor {
|
||||||
@ -134,7 +134,7 @@ class Unparser implements AstVisitor {
|
|||||||
|
|
||||||
visitLiteralPrimitive(ast: LiteralPrimitive, context: any) {
|
visitLiteralPrimitive(ast: LiteralPrimitive, context: any) {
|
||||||
if (isString(ast.value)) {
|
if (isString(ast.value)) {
|
||||||
this._expression += `"${StringWrapper.replaceAll(ast.value, Unparser._quoteRegExp, '\"')}"`;
|
this._expression += `"${ast.value.replace( Unparser._quoteRegExp, '\"')}"`;
|
||||||
} else {
|
} else {
|
||||||
this._expression += `${ast.value}`;
|
this._expression += `${ast.value}`;
|
||||||
}
|
}
|
||||||
|
@ -376,6 +376,82 @@ export function main() {
|
|||||||
[html.ExpansionCase, '=0', 2, '=0 {msg}'],
|
[html.ExpansionCase, '=0', 2, '=0 {msg}'],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not report a value span for an attribute without a value', () => {
|
||||||
|
const ast = parser.parse('<div bar></div>', 'TestComp');
|
||||||
|
expect((ast.rootNodes[0] as html.Element).attrs[0].valueSpan).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should report a value span for an attibute with a value', () => {
|
||||||
|
const ast = parser.parse('<div bar="12"></div>', 'TestComp');
|
||||||
|
const attr = (ast.rootNodes[0] as html.Element).attrs[0];
|
||||||
|
expect(attr.valueSpan.start.offset).toEqual(9);
|
||||||
|
expect(attr.valueSpan.end.offset).toEqual(13);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('visitor', () => {
|
||||||
|
it('should visit text nodes', () => {
|
||||||
|
const result = humanizeDom(parser.parse('text', 'TestComp'));
|
||||||
|
expect(result).toEqual([[html.Text, 'text', 0]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit element nodes', () => {
|
||||||
|
const result = humanizeDom(parser.parse('<div></div>', 'TestComp'));
|
||||||
|
expect(result).toEqual([[html.Element, 'div', 0]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit attribute nodes', () => {
|
||||||
|
const result = humanizeDom(parser.parse('<div id="foo"></div>', 'TestComp'));
|
||||||
|
expect(result).toContain([html.Attribute, 'id', 'foo']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit all nodes', () => {
|
||||||
|
const result =
|
||||||
|
parser.parse('<div id="foo"><span id="bar">a</span><span>b</span></div>', 'TestComp');
|
||||||
|
const accumulator: html.Node[] = [];
|
||||||
|
const visitor = new class {
|
||||||
|
visit(node: html.Node, context: any) { accumulator.push(node); }
|
||||||
|
visitElement(element: html.Element, context: any): any {
|
||||||
|
html.visitAll(this, element.attrs);
|
||||||
|
html.visitAll(this, element.children);
|
||||||
|
}
|
||||||
|
visitAttribute(attribute: html.Attribute, context: any): any {}
|
||||||
|
visitText(text: html.Text, context: any): any {}
|
||||||
|
visitComment(comment: html.Comment, context: any): any {}
|
||||||
|
visitExpansion(expansion: html.Expansion, context: any): any {
|
||||||
|
html.visitAll(this, expansion.cases);
|
||||||
|
}
|
||||||
|
visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any {}
|
||||||
|
};
|
||||||
|
|
||||||
|
html.visitAll(visitor, result.rootNodes);
|
||||||
|
expect(accumulator.map(n => n.constructor)).toEqual([
|
||||||
|
html.Element, html.Attribute, html.Element, html.Attribute, html.Text, html.Element,
|
||||||
|
html.Text
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip typed visit if visit() returns a truthy value', () => {
|
||||||
|
const visitor = new class {
|
||||||
|
visit(node: html.Node, context: any) { return true; }
|
||||||
|
visitElement(element: html.Element, context: any): any { throw Error('Unexpected'); }
|
||||||
|
visitAttribute(attribute: html.Attribute, context: any): any {
|
||||||
|
throw Error('Unexpected');
|
||||||
|
}
|
||||||
|
visitText(text: html.Text, context: any): any { throw Error('Unexpected'); }
|
||||||
|
visitComment(comment: html.Comment, context: any): any { throw Error('Unexpected'); }
|
||||||
|
visitExpansion(expansion: html.Expansion, context: any): any {
|
||||||
|
throw Error('Unexpected');
|
||||||
|
}
|
||||||
|
visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any {
|
||||||
|
throw Error('Unexpected');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = parser.parse('<div id="foo"></div><div id="bar"></div>', 'TestComp');
|
||||||
|
const traversal = html.visitAll(visitor, result.rootNodes);
|
||||||
|
expect(traversal).toEqual([true, true]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('errors', () => {
|
describe('errors', () => {
|
||||||
|
@ -43,7 +43,7 @@ export function main() {
|
|||||||
describe('output emitter', () => {
|
describe('output emitter', () => {
|
||||||
outputDefs.forEach((outputDef) => {
|
outputDefs.forEach((outputDef) => {
|
||||||
describe(`${outputDef['name']}`, () => {
|
describe(`${outputDef['name']}`, () => {
|
||||||
var expressions: any /** TODO #9100 */;
|
var expressions: {[k: string]: any};
|
||||||
beforeEach(() => { expressions = outputDef['getExpressions']()(); });
|
beforeEach(() => { expressions = outputDef['getExpressions']()(); });
|
||||||
|
|
||||||
it('should support literals', () => {
|
it('should support literals', () => {
|
||||||
@ -109,13 +109,16 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('operators', () => {
|
describe('operators', () => {
|
||||||
var ops: any /** TODO #9100 */;
|
var ops: {[k: string]: Function};
|
||||||
var aObj: any /** TODO #9100 */, bObj: any /** TODO #9100 */;
|
var aObj: any;
|
||||||
|
var bObj: any;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ops = expressions['operators'];
|
ops = expressions['operators'];
|
||||||
aObj = new Object();
|
aObj = {};
|
||||||
bObj = new Object();
|
bObj = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support ==', () => {
|
it('should support ==', () => {
|
||||||
expect(ops['=='](aObj, aObj)).toBe(true);
|
expect(ops['=='](aObj, aObj)).toBe(true);
|
||||||
expect(ops['=='](aObj, bObj)).toBe(false);
|
expect(ops['=='](aObj, bObj)).toBe(false);
|
||||||
|
@ -53,7 +53,7 @@ export function main() {
|
|||||||
it('should return an error from the definitions',
|
it('should return an error from the definitions',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var url = '/foo';
|
var url = '/foo';
|
||||||
var response: any /** TODO #9100 */ = null;
|
var response: string = null;
|
||||||
resourceLoader.when(url, response);
|
resourceLoader.when(url, response);
|
||||||
expectResponse(resourceLoader.get(url), url, response, () => async.done());
|
expectResponse(resourceLoader.get(url), url, response, () => async.done());
|
||||||
resourceLoader.flush();
|
resourceLoader.flush();
|
||||||
@ -71,7 +71,7 @@ export function main() {
|
|||||||
it('should return an error from the expectations',
|
it('should return an error from the expectations',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var url = '/foo';
|
var url = '/foo';
|
||||||
var response: any /** TODO #9100 */ = null;
|
var response: string = null;
|
||||||
resourceLoader.expect(url, response);
|
resourceLoader.expect(url, response);
|
||||||
expectResponse(resourceLoader.get(url), url, response, () => async.done());
|
expectResponse(resourceLoader.get(url), url, response, () => async.done());
|
||||||
resourceLoader.flush();
|
resourceLoader.flush();
|
||||||
|
@ -117,7 +117,7 @@ export function main() {
|
|||||||
class SomeModule {
|
class SomeModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceLoader.spy('get').andCallFake(() => Promise.resolve('hello'));
|
resourceLoader.spy('get').and.callFake(() => Promise.resolve('hello'));
|
||||||
let ngModuleFactory: NgModuleFactory<any>;
|
let ngModuleFactory: NgModuleFactory<any>;
|
||||||
compiler.compileModuleAsync(SomeModule).then((f) => ngModuleFactory = f);
|
compiler.compileModuleAsync(SomeModule).then((f) => ngModuleFactory = f);
|
||||||
tick();
|
tick();
|
||||||
@ -132,7 +132,7 @@ export function main() {
|
|||||||
class SomeModule {
|
class SomeModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceLoader.spy('get').andCallFake(() => Promise.resolve(''));
|
resourceLoader.spy('get').and.callFake(() => Promise.resolve(''));
|
||||||
expect(() => compiler.compileModuleSync(SomeModule))
|
expect(() => compiler.compileModuleSync(SomeModule))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Can't compile synchronously as ${stringify(SomeCompWithUrlTemplate)} is still being loaded!`);
|
`Can't compile synchronously as ${stringify(SomeCompWithUrlTemplate)} is still being loaded!`);
|
||||||
@ -144,7 +144,7 @@ export function main() {
|
|||||||
class SomeModule {
|
class SomeModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceLoader.spy('get').andCallFake(() => Promise.resolve(''));
|
resourceLoader.spy('get').and.callFake(() => Promise.resolve(''));
|
||||||
dirResolver.setView(SomeComp, new ViewMetadata({template: ''}));
|
dirResolver.setView(SomeComp, new ViewMetadata({template: ''}));
|
||||||
dirResolver.setView(ChildComp, new ViewMetadata({templateUrl: '/someTpl.html'}));
|
dirResolver.setView(ChildComp, new ViewMetadata({templateUrl: '/someTpl.html'}));
|
||||||
expect(() => compiler.compileModuleSync(SomeModule))
|
expect(() => compiler.compileModuleSync(SomeModule))
|
||||||
@ -161,7 +161,7 @@ export function main() {
|
|||||||
class SomeModule {
|
class SomeModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceLoader.spy('get').andCallFake(() => Promise.resolve('hello'));
|
resourceLoader.spy('get').and.callFake(() => Promise.resolve('hello'));
|
||||||
compiler.compileModuleAsync(SomeModule);
|
compiler.compileModuleAsync(SomeModule);
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
|
@ -112,10 +112,8 @@ export function main() {
|
|||||||
it('should handle no context',
|
it('should handle no context',
|
||||||
() => { expect(s(':host {}', 'a', 'a-host')).toEqual('[a-host] {}'); });
|
() => { expect(s(':host {}', 'a', 'a-host')).toEqual('[a-host] {}'); });
|
||||||
|
|
||||||
it('should handle tag selector', () => {
|
it('should handle tag selector',
|
||||||
expect(s(':host(ul) {}', 'a', 'a-host')).toEqual('ul[a-host] {}');
|
() => { expect(s(':host(ul) {}', 'a', 'a-host')).toEqual('ul[a-host] {}'); });
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle class selector',
|
it('should handle class selector',
|
||||||
() => { expect(s(':host(.x) {}', 'a', 'a-host')).toEqual('.x[a-host] {}'); });
|
() => { expect(s(':host(.x) {}', 'a', 'a-host')).toEqual('.x[a-host] {}'); });
|
||||||
@ -141,6 +139,11 @@ export function main() {
|
|||||||
expect(s(':host([a="b"],[c=d]) {}', 'a', 'a-host'))
|
expect(s(':host([a="b"],[c=d]) {}', 'a', 'a-host'))
|
||||||
.toEqual('[a="b"][a-host], [c="d"][a-host] {}');
|
.toEqual('[a="b"][a-host], [c="d"][a-host] {}');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle pseudo selector', () => {
|
||||||
|
expect(s(':host(:before) {}', 'a', 'a-host')).toEqual('[a-host]:before {}');
|
||||||
|
expect(s(':host:before {}', 'a', 'a-host')).toEqual('[a-host]:before {}');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe((':host-context'), () => {
|
describe((':host-context'), () => {
|
||||||
|
@ -11,7 +11,7 @@ import {UrlResolver} from '@angular/compiler/src/url_resolver';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('extractStyleUrls', () => {
|
describe('extractStyleUrls', () => {
|
||||||
var urlResolver: any /** TODO #9100 */;
|
var urlResolver: UrlResolver;
|
||||||
|
|
||||||
beforeEach(() => { urlResolver = new UrlResolver(); });
|
beforeEach(() => { urlResolver = new UrlResolver(); });
|
||||||
|
|
||||||
|
@ -71,6 +71,116 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('TemplateAstVisitor', () => {
|
||||||
|
function expectVisitedNode(visitor: TemplateAstVisitor, node: TemplateAst) {
|
||||||
|
expect(node.visit(visitor, null)).toEqual(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should visit NgContentAst', () => {
|
||||||
|
expectVisitedNode(
|
||||||
|
new class extends
|
||||||
|
NullVisitor{visitNgContent(ast: NgContentAst, context: any): any{return ast;}},
|
||||||
|
new NgContentAst(0, 0, null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit EmbeddedTemplateAst', () => {
|
||||||
|
expectVisitedNode(
|
||||||
|
new class extends NullVisitor{
|
||||||
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any) { return ast; }
|
||||||
|
},
|
||||||
|
new EmbeddedTemplateAst([], [], [], [], [], [], false, [], 0, null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit ElementAst', () => {
|
||||||
|
expectVisitedNode(
|
||||||
|
new class extends
|
||||||
|
NullVisitor{visitElement(ast: ElementAst, context: any) { return ast; }},
|
||||||
|
new ElementAst('foo', [], [], [], [], [], [], false, [], 0, null, null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit RefererenceAst', () => {
|
||||||
|
expectVisitedNode(
|
||||||
|
new class extends
|
||||||
|
NullVisitor{visitReference(ast: ReferenceAst, context: any): any{return ast;}},
|
||||||
|
new ReferenceAst('foo', null, null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit VariableAst', () => {
|
||||||
|
expectVisitedNode(
|
||||||
|
new class extends
|
||||||
|
NullVisitor{visitVariable(ast: VariableAst, context: any): any{return ast;}},
|
||||||
|
new VariableAst('foo', 'bar', null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit BoundEventAst', () => {
|
||||||
|
expectVisitedNode(
|
||||||
|
new class extends
|
||||||
|
NullVisitor{visitEvent(ast: BoundEventAst, context: any): any{return ast;}},
|
||||||
|
new BoundEventAst('foo', 'bar', 'goo', null, null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit BoundElementPropertyAst', () => {
|
||||||
|
expectVisitedNode(
|
||||||
|
new class extends NullVisitor{
|
||||||
|
visitElementProperty(ast: BoundElementPropertyAst, context: any): any{return ast;}
|
||||||
|
},
|
||||||
|
new BoundElementPropertyAst('foo', null, null, null, 'bar', null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit AttrAst', () => {
|
||||||
|
expectVisitedNode(
|
||||||
|
new class extends NullVisitor{visitAttr(ast: AttrAst, context: any): any{return ast;}},
|
||||||
|
new AttrAst('foo', 'bar', null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit BoundTextAst', () => {
|
||||||
|
expectVisitedNode(
|
||||||
|
new class extends
|
||||||
|
NullVisitor{visitBoundText(ast: BoundTextAst, context: any): any{return ast;}},
|
||||||
|
new BoundTextAst(null, 0, null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit TextAst', () => {
|
||||||
|
expectVisitedNode(
|
||||||
|
new class extends NullVisitor{visitText(ast: TextAst, context: any): any{return ast;}},
|
||||||
|
new TextAst('foo', 0, null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit DirectiveAst', () => {
|
||||||
|
expectVisitedNode(
|
||||||
|
new class extends
|
||||||
|
NullVisitor{visitDirective(ast: DirectiveAst, context: any): any{return ast;}},
|
||||||
|
new DirectiveAst(null, [], [], [], null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should visit DirectiveAst', () => {
|
||||||
|
expectVisitedNode(
|
||||||
|
new class extends NullVisitor{
|
||||||
|
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any{return ast;}
|
||||||
|
},
|
||||||
|
new BoundDirectivePropertyAst('foo', 'bar', null, null));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip the typed call of a visitor if visit() returns a truthy value', () => {
|
||||||
|
const visitor = new class extends ThrowingVisitor {
|
||||||
|
visit(ast: TemplateAst, context: any): any { return true; }
|
||||||
|
};
|
||||||
|
const nodes: TemplateAst[] = [
|
||||||
|
new NgContentAst(0, 0, null),
|
||||||
|
new EmbeddedTemplateAst([], [], [], [], [], [], false, [], 0, null),
|
||||||
|
new ElementAst('foo', [], [], [], [], [], [], false, [], 0, null, null),
|
||||||
|
new ReferenceAst('foo', null, null), new VariableAst('foo', 'bar', null),
|
||||||
|
new BoundEventAst('foo', 'bar', 'goo', null, null),
|
||||||
|
new BoundElementPropertyAst('foo', null, null, null, 'bar', null),
|
||||||
|
new AttrAst('foo', 'bar', null), new BoundTextAst(null, 0, null),
|
||||||
|
new TextAst('foo', 0, null), new DirectiveAst(null, [], [], [], null),
|
||||||
|
new BoundDirectivePropertyAst('foo', 'bar', null, null)
|
||||||
|
];
|
||||||
|
const result = templateVisitAll(visitor, nodes, null);
|
||||||
|
expect(result).toEqual(new Array(nodes.length).fill(true));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('TemplateParser template transform', () => {
|
describe('TemplateParser template transform', () => {
|
||||||
beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
|
beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
|
||||||
|
|
||||||
@ -164,12 +274,17 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should parse ngContent', () => {
|
it('should parse ngContent', () => {
|
||||||
var parsed = parse('<ng-content select="a">', []);
|
const parsed = parse('<ng-content select="a"></ng-content>', []);
|
||||||
|
expect(humanizeTplAst(parsed)).toEqual([[NgContentAst]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse ngContent when it contains WS only', () => {
|
||||||
|
const parsed = parse('<ng-content select="a"> \n </ng-content>', []);
|
||||||
expect(humanizeTplAst(parsed)).toEqual([[NgContentAst]]);
|
expect(humanizeTplAst(parsed)).toEqual([[NgContentAst]]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse ngContent regardless the namespace', () => {
|
it('should parse ngContent regardless the namespace', () => {
|
||||||
var parsed = parse('<svg><ng-content></ng-content></svg>', []);
|
const parsed = parse('<svg><ng-content></ng-content></svg>', []);
|
||||||
expect(humanizeTplAst(parsed)).toEqual([
|
expect(humanizeTplAst(parsed)).toEqual([
|
||||||
[ElementAst, ':svg:svg'],
|
[ElementAst, ':svg:svg'],
|
||||||
[NgContentAst],
|
[NgContentAst],
|
||||||
@ -1146,7 +1261,7 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('content projection', () => {
|
describe('content projection', () => {
|
||||||
var compCounter: any /** TODO #9100 */;
|
var compCounter: number;
|
||||||
beforeEach(() => { compCounter = 0; });
|
beforeEach(() => { compCounter = 0; });
|
||||||
|
|
||||||
function createComp(
|
function createComp(
|
||||||
@ -1308,10 +1423,11 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('error cases', () => {
|
describe('error cases', () => {
|
||||||
it('should report when ng-content has content', () => {
|
it('should report when ng-content has non WS content', () => {
|
||||||
expect(() => parse('<ng-content>content</ng-content>', []))
|
expect(() => parse('<ng-content>content</ng-content>', []))
|
||||||
.toThrowError(`Template parse errors:
|
.toThrowError(
|
||||||
<ng-content> element cannot have content. <ng-content> must be immediately followed by </ng-content> ("[ERROR ->]<ng-content>content</ng-content>"): TestComp@0:0`);
|
`Template parse errors:\n` +
|
||||||
|
`<ng-content> element cannot have content. ("[ERROR ->]<ng-content>content</ng-content>"): TestComp@0:0`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should treat *attr on a template element as valid',
|
it('should treat *attr on a template element as valid',
|
||||||
@ -1614,6 +1730,35 @@ Property binding a not used by any directive on an embedded template. Make sure
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support endSourceSpan for elements', () => {
|
||||||
|
const tagSel = CompileDirectiveMetadata.create({
|
||||||
|
selector: 'circle',
|
||||||
|
type: new CompileTypeMetadata(
|
||||||
|
{moduleUrl: someModuleUrl, name: 'elDir', reference: {} as Type<any>})
|
||||||
|
});
|
||||||
|
const result = parse('<circle></circle>', [tagSel]);
|
||||||
|
const circle = result[0] as ElementAst;
|
||||||
|
expect(circle.endSourceSpan).toBeDefined();
|
||||||
|
expect(circle.endSourceSpan.start.offset).toBe(8);
|
||||||
|
expect(circle.endSourceSpan.end.offset).toBe(17);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should report undefined for endSourceSpan for elements without an end-tag', () => {
|
||||||
|
const ulSel = CompileDirectiveMetadata.create({
|
||||||
|
selector: 'ul',
|
||||||
|
type: new CompileTypeMetadata(
|
||||||
|
{moduleUrl: someModuleUrl, name: 'ulDir', reference: {} as Type<any>})
|
||||||
|
});
|
||||||
|
const liSel = CompileDirectiveMetadata.create({
|
||||||
|
selector: 'li',
|
||||||
|
type: new CompileTypeMetadata(
|
||||||
|
{moduleUrl: someModuleUrl, name: 'liDir', reference: {} as Type<any>})
|
||||||
|
});
|
||||||
|
const result = parse('<ul><li><li></ul>', [ulSel, liSel]);
|
||||||
|
const ul = result[0] as ElementAst;
|
||||||
|
const li = ul.children[0] as ElementAst;
|
||||||
|
expect(li.endSourceSpan).toBe(null);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('pipes', () => {
|
describe('pipes', () => {
|
||||||
@ -1819,14 +1964,10 @@ class TemplateContentProjectionHumanizer implements TemplateAstVisitor {
|
|||||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
class FooAstTransformer implements TemplateAstVisitor {
|
class ThrowingVisitor implements TemplateAstVisitor {
|
||||||
visitNgContent(ast: NgContentAst, context: any): any { throw 'not implemented'; }
|
visitNgContent(ast: NgContentAst, context: any): any { throw 'not implemented'; }
|
||||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { throw 'not implemented'; }
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { throw 'not implemented'; }
|
||||||
visitElement(ast: ElementAst, context: any): any {
|
visitElement(ast: ElementAst, context: any): any { throw 'not implemented'; }
|
||||||
if (ast.name != 'div') return ast;
|
|
||||||
return new ElementAst(
|
|
||||||
'foo', [], [], [], [], [], [], false, [], ast.ngContentIndex, ast.sourceSpan);
|
|
||||||
}
|
|
||||||
visitReference(ast: ReferenceAst, context: any): any { throw 'not implemented'; }
|
visitReference(ast: ReferenceAst, context: any): any { throw 'not implemented'; }
|
||||||
visitVariable(ast: VariableAst, context: any): any { throw 'not implemented'; }
|
visitVariable(ast: VariableAst, context: any): any { throw 'not implemented'; }
|
||||||
visitEvent(ast: BoundEventAst, context: any): any { throw 'not implemented'; }
|
visitEvent(ast: BoundEventAst, context: any): any { throw 'not implemented'; }
|
||||||
@ -1840,14 +1981,39 @@ class FooAstTransformer implements TemplateAstVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FooAstTransformer extends ThrowingVisitor {
|
||||||
|
visitElement(ast: ElementAst, context: any): any {
|
||||||
|
if (ast.name != 'div') return ast;
|
||||||
|
return new ElementAst(
|
||||||
|
'foo', [], [], [], [], [], [], false, [], ast.ngContentIndex, ast.sourceSpan,
|
||||||
|
ast.endSourceSpan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class BarAstTransformer extends FooAstTransformer {
|
class BarAstTransformer extends FooAstTransformer {
|
||||||
visitElement(ast: ElementAst, context: any): any {
|
visitElement(ast: ElementAst, context: any): any {
|
||||||
if (ast.name != 'foo') return ast;
|
if (ast.name != 'foo') return ast;
|
||||||
return new ElementAst(
|
return new ElementAst(
|
||||||
'bar', [], [], [], [], [], [], false, [], ast.ngContentIndex, ast.sourceSpan);
|
'bar', [], [], [], [], [], [], false, [], ast.ngContentIndex, ast.sourceSpan,
|
||||||
|
ast.endSourceSpan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NullVisitor implements TemplateAstVisitor {
|
||||||
|
visitNgContent(ast: NgContentAst, context: any): any {}
|
||||||
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {}
|
||||||
|
visitElement(ast: ElementAst, context: any): any {}
|
||||||
|
visitReference(ast: ReferenceAst, context: any): any {}
|
||||||
|
visitVariable(ast: VariableAst, context: any): any {}
|
||||||
|
visitEvent(ast: BoundEventAst, context: any): any {}
|
||||||
|
visitElementProperty(ast: BoundElementPropertyAst, context: any): any {}
|
||||||
|
visitAttr(ast: AttrAst, context: any): any {}
|
||||||
|
visitBoundText(ast: BoundTextAst, context: any): any {}
|
||||||
|
visitText(ast: TextAst, context: any): any {}
|
||||||
|
visitDirective(ast: DirectiveAst, context: any): any {}
|
||||||
|
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {}
|
||||||
|
}
|
||||||
|
|
||||||
class ArrayConsole implements Console {
|
class ArrayConsole implements Console {
|
||||||
logs: string[] = [];
|
logs: string[] = [];
|
||||||
warnings: string[] = [];
|
warnings: string[] = [];
|
||||||
|
@ -46,16 +46,14 @@ export class MockDirectiveResolver extends DirectiveResolver {
|
|||||||
|
|
||||||
let providers = metadata.providers;
|
let providers = metadata.providers;
|
||||||
if (isPresent(providerOverrides)) {
|
if (isPresent(providerOverrides)) {
|
||||||
const originalViewProviders: Provider[] =
|
const originalViewProviders: Provider[] = metadata.providers || [];
|
||||||
isPresent(metadata.providers) ? metadata.providers : [];
|
|
||||||
providers = originalViewProviders.concat(providerOverrides);
|
providers = originalViewProviders.concat(providerOverrides);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadata instanceof Component) {
|
if (metadata instanceof Component) {
|
||||||
let viewProviders = metadata.viewProviders;
|
let viewProviders = metadata.viewProviders;
|
||||||
if (isPresent(viewProviderOverrides)) {
|
if (isPresent(viewProviderOverrides)) {
|
||||||
const originalViewProviders: Provider[] =
|
const originalViewProviders: Provider[] = metadata.viewProviders || [];
|
||||||
isPresent(metadata.viewProviders) ? metadata.viewProviders : [];
|
|
||||||
viewProviders = originalViewProviders.concat(viewProviderOverrides);
|
viewProviders = originalViewProviders.concat(viewProviderOverrides);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ export class MockResourceLoader extends ResourceLoader {
|
|||||||
verifyNoOutstandingExpectations() {
|
verifyNoOutstandingExpectations() {
|
||||||
if (this._expectations.length === 0) return;
|
if (this._expectations.length === 0) return;
|
||||||
|
|
||||||
var urls: any[] /** TODO #9100 */ = [];
|
var urls: string[] = [];
|
||||||
for (var i = 0; i < this._expectations.length; i++) {
|
for (var i = 0; i < this._expectations.length; i++) {
|
||||||
var expectation = this._expectations[i];
|
var expectation = this._expectations[i];
|
||||||
urls.push(expectation.url);
|
urls.push(expectation.url);
|
||||||
|
@ -540,6 +540,22 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
|
|||||||
* ])
|
* ])
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
* ### Transition Aliases (`:enter` and `:leave`)
|
||||||
|
*
|
||||||
|
* Given that enter (insertion) and leave (removal) animations are so common,
|
||||||
|
* the `transition` function accepts both `:enter` and `:leave` values which
|
||||||
|
* are aliases for the `void => *` and `* => void` state changes.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* transition(":enter", [
|
||||||
|
* style({ opacity: 0 }),
|
||||||
|
* animate(500, style({ opacity: 1 }))
|
||||||
|
* ])
|
||||||
|
* transition(":leave", [
|
||||||
|
* animate(500, style({ opacity: 0 }))
|
||||||
|
* ])
|
||||||
|
* ```
|
||||||
|
*
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/Kez8XGWBxWue7qP7nNvF?p=preview))
|
* ### Example ([live demo](http://plnkr.co/edit/Kez8XGWBxWue7qP7nNvF?p=preview))
|
||||||
*
|
*
|
||||||
* {@example core/animation/ts/dsl/animation_example.ts region='Component'}
|
* {@example core/animation/ts/dsl/animation_example.ts region='Component'}
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {StringWrapper} from '../src/facade/lang';
|
|
||||||
|
|
||||||
import {OpaqueToken} from './di';
|
import {OpaqueToken} from './di';
|
||||||
|
|
||||||
|
|
||||||
@ -38,7 +36,7 @@ export const APP_ID_RANDOM_PROVIDER = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function _randomChar(): string {
|
function _randomChar(): string {
|
||||||
return StringWrapper.fromCharCode(97 + Math.floor(Math.random() * 25));
|
return String.fromCharCode(97 + Math.floor(Math.random() * 25));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +47,7 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
|||||||
private _identityChangesTail: CollectionChangeRecord = null;
|
private _identityChangesTail: CollectionChangeRecord = null;
|
||||||
|
|
||||||
constructor(private _trackByFn?: TrackByFn) {
|
constructor(private _trackByFn?: TrackByFn) {
|
||||||
this._trackByFn = isPresent(this._trackByFn) ? this._trackByFn : trackByIdentity;
|
this._trackByFn = this._trackByFn || trackByIdentity;
|
||||||
}
|
}
|
||||||
|
|
||||||
get collection() { return this._collection; }
|
get collection() { return this._collection; }
|
||||||
|
@ -159,7 +159,7 @@ export class ViewContainerRef_ implements ViewContainerRef {
|
|||||||
componentFactory: ComponentFactory<C>, index: number = -1, injector: Injector = null,
|
componentFactory: ComponentFactory<C>, index: number = -1, injector: Injector = null,
|
||||||
projectableNodes: any[][] = null): ComponentRef<C> {
|
projectableNodes: any[][] = null): ComponentRef<C> {
|
||||||
var s = this._createComponentInContainerScope();
|
var s = this._createComponentInContainerScope();
|
||||||
var contextInjector = isPresent(injector) ? injector : this._element.parentInjector;
|
var contextInjector = injector || this._element.parentInjector;
|
||||||
var componentRef = componentFactory.create(contextInjector, projectableNodes);
|
var componentRef = componentFactory.create(contextInjector, projectableNodes);
|
||||||
this.insert(componentRef.hostView, index);
|
this.insert(componentRef.hostView, index);
|
||||||
return wtfLeave(s, componentRef);
|
return wtfLeave(s, componentRef);
|
||||||
|
@ -16,4 +16,4 @@ export {ReflectionInfo, Reflector} from './reflector';
|
|||||||
* The {@link Reflector} used internally in Angular to access metadata
|
* The {@link Reflector} used internally in Angular to access metadata
|
||||||
* about symbols.
|
* about symbols.
|
||||||
*/
|
*/
|
||||||
export var reflector = new Reflector(new ReflectionCapabilities());
|
export const reflector = new Reflector(new ReflectionCapabilities());
|
||||||
|
@ -19,19 +19,11 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
|||||||
|
|
||||||
isReflectionEnabled(): boolean { return true; }
|
isReflectionEnabled(): boolean { return true; }
|
||||||
|
|
||||||
factory(t: Type<any>): Function {
|
factory<T>(t: Type<T>): (args: any[]) => T { return (...args: any[]) => new t(...args); }
|
||||||
var prototype = t.prototype;
|
|
||||||
return function(...args: any[]) {
|
|
||||||
var instance = Object.create(prototype);
|
|
||||||
t.apply(instance, args);
|
|
||||||
return instance;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_zipTypesAndAnnotations(
|
_zipTypesAndAnnotations(paramTypes: any[], paramAnnotations: any[]): any[][] {
|
||||||
paramTypes: any /** TODO #9100 */, paramAnnotations: any /** TODO #9100 */): any[][] {
|
var result: any[][];
|
||||||
var result: any /** TODO #9100 */;
|
|
||||||
|
|
||||||
if (typeof paramTypes === 'undefined') {
|
if (typeof paramTypes === 'undefined') {
|
||||||
result = new Array(paramAnnotations.length);
|
result = new Array(paramAnnotations.length);
|
||||||
@ -50,48 +42,45 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
|||||||
} else {
|
} else {
|
||||||
result[i] = [];
|
result[i] = [];
|
||||||
}
|
}
|
||||||
if (isPresent(paramAnnotations) && isPresent(paramAnnotations[i])) {
|
if (paramAnnotations && isPresent(paramAnnotations[i])) {
|
||||||
result[i] = result[i].concat(paramAnnotations[i]);
|
result[i] = result[i].concat(paramAnnotations[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters(typeOrFunc: Type<any>): any[][] {
|
parameters(type: Type<any>): any[][] {
|
||||||
// Prefer the direct API.
|
// Prefer the direct API.
|
||||||
if (isPresent((<any>typeOrFunc).parameters)) {
|
if ((<any>type).parameters) {
|
||||||
return (<any>typeOrFunc).parameters;
|
return (<any>type).parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
// API of tsickle for lowering decorators to properties on the class.
|
// API of tsickle for lowering decorators to properties on the class.
|
||||||
if (isPresent((<any>typeOrFunc).ctorParameters)) {
|
if ((<any>type).ctorParameters) {
|
||||||
let ctorParameters = (<any>typeOrFunc).ctorParameters;
|
const ctorParameters = (<any>type).ctorParameters;
|
||||||
let paramTypes =
|
const paramTypes = ctorParameters.map((ctorParam: any) => ctorParam && ctorParam.type);
|
||||||
ctorParameters.map((ctorParam: any /** TODO #9100 */) => ctorParam && ctorParam.type);
|
const paramAnnotations = ctorParameters.map(
|
||||||
let paramAnnotations = ctorParameters.map(
|
(ctorParam: any) =>
|
||||||
(ctorParam: any /** TODO #9100 */) =>
|
|
||||||
ctorParam && convertTsickleDecoratorIntoMetadata(ctorParam.decorators));
|
ctorParam && convertTsickleDecoratorIntoMetadata(ctorParam.decorators));
|
||||||
return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
|
return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
// API for metadata created by invoking the decorators.
|
// API for metadata created by invoking the decorators.
|
||||||
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
|
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
|
||||||
var paramAnnotations = this._reflect.getMetadata('parameters', typeOrFunc);
|
const paramAnnotations = this._reflect.getMetadata('parameters', type);
|
||||||
var paramTypes = this._reflect.getMetadata('design:paramtypes', typeOrFunc);
|
const paramTypes = this._reflect.getMetadata('design:paramtypes', type);
|
||||||
if (isPresent(paramTypes) || isPresent(paramAnnotations)) {
|
if (paramTypes || paramAnnotations) {
|
||||||
return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
|
return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The array has to be filled with `undefined` because holes would be skipped by `some`
|
// The array has to be filled with `undefined` because holes would be skipped by `some`
|
||||||
let parameters = new Array((<any>typeOrFunc.length));
|
return new Array((<any>type.length)).fill(undefined);
|
||||||
parameters.fill(undefined);
|
|
||||||
return parameters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
annotations(typeOrFunc: Type<any>): any[] {
|
annotations(typeOrFunc: Type<any>): any[] {
|
||||||
// Prefer the direct API.
|
// Prefer the direct API.
|
||||||
if (isPresent((<any>typeOrFunc).annotations)) {
|
if ((<any>typeOrFunc).annotations) {
|
||||||
var annotations = (<any>typeOrFunc).annotations;
|
let annotations = (<any>typeOrFunc).annotations;
|
||||||
if (isFunction(annotations) && annotations.annotations) {
|
if (isFunction(annotations) && annotations.annotations) {
|
||||||
annotations = annotations.annotations;
|
annotations = annotations.annotations;
|
||||||
}
|
}
|
||||||
@ -99,22 +88,22 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// API of tsickle for lowering decorators to properties on the class.
|
// API of tsickle for lowering decorators to properties on the class.
|
||||||
if (isPresent((<any>typeOrFunc).decorators)) {
|
if ((<any>typeOrFunc).decorators) {
|
||||||
return convertTsickleDecoratorIntoMetadata((<any>typeOrFunc).decorators);
|
return convertTsickleDecoratorIntoMetadata((<any>typeOrFunc).decorators);
|
||||||
}
|
}
|
||||||
|
|
||||||
// API for metadata created by invoking the decorators.
|
// API for metadata created by invoking the decorators.
|
||||||
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
|
if (this._reflect && this._reflect.getMetadata) {
|
||||||
var annotations = this._reflect.getMetadata('annotations', typeOrFunc);
|
const annotations = this._reflect.getMetadata('annotations', typeOrFunc);
|
||||||
if (isPresent(annotations)) return annotations;
|
if (annotations) return annotations;
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
propMetadata(typeOrFunc: any): {[key: string]: any[]} {
|
propMetadata(typeOrFunc: any): {[key: string]: any[]} {
|
||||||
// Prefer the direct API.
|
// Prefer the direct API.
|
||||||
if (isPresent((<any>typeOrFunc).propMetadata)) {
|
if ((<any>typeOrFunc).propMetadata) {
|
||||||
var propMetadata = (<any>typeOrFunc).propMetadata;
|
let propMetadata = (<any>typeOrFunc).propMetadata;
|
||||||
if (isFunction(propMetadata) && propMetadata.propMetadata) {
|
if (isFunction(propMetadata) && propMetadata.propMetadata) {
|
||||||
propMetadata = propMetadata.propMetadata;
|
propMetadata = propMetadata.propMetadata;
|
||||||
}
|
}
|
||||||
@ -122,9 +111,9 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// API of tsickle for lowering decorators to properties on the class.
|
// API of tsickle for lowering decorators to properties on the class.
|
||||||
if (isPresent((<any>typeOrFunc).propDecorators)) {
|
if ((<any>typeOrFunc).propDecorators) {
|
||||||
let propDecorators = (<any>typeOrFunc).propDecorators;
|
const propDecorators = (<any>typeOrFunc).propDecorators;
|
||||||
let propMetadata = <{[key: string]: any[]}>{};
|
const propMetadata = <{[key: string]: any[]}>{};
|
||||||
Object.keys(propDecorators).forEach(prop => {
|
Object.keys(propDecorators).forEach(prop => {
|
||||||
propMetadata[prop] = convertTsickleDecoratorIntoMetadata(propDecorators[prop]);
|
propMetadata[prop] = convertTsickleDecoratorIntoMetadata(propDecorators[prop]);
|
||||||
});
|
});
|
||||||
@ -132,9 +121,9 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// API for metadata created by invoking the decorators.
|
// API for metadata created by invoking the decorators.
|
||||||
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
|
if (this._reflect && this._reflect.getMetadata) {
|
||||||
var propMetadata = this._reflect.getMetadata('propMetadata', typeOrFunc);
|
const propMetadata = this._reflect.getMetadata('propMetadata', typeOrFunc);
|
||||||
if (isPresent(propMetadata)) return propMetadata;
|
if (propMetadata) return propMetadata;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -147,7 +136,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
|||||||
hasLifecycleHook(type: any, lcInterface: Type<any>, lcProperty: string): boolean {
|
hasLifecycleHook(type: any, lcInterface: Type<any>, lcProperty: string): boolean {
|
||||||
if (!(type instanceof Type)) return false;
|
if (!(type instanceof Type)) return false;
|
||||||
|
|
||||||
var proto = (<any>type).prototype;
|
const proto = (<any>type).prototype;
|
||||||
return !!proto[lcProperty];
|
return !!proto[lcProperty];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +147,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
method(name: string): MethodFn {
|
method(name: string): MethodFn {
|
||||||
let functionBody = `if (!o.${name}) throw new Error('"${name}" is undefined');
|
const functionBody = `if (!o.${name}) throw new Error('"${name}" is undefined');
|
||||||
return o.${name}.apply(o, args);`;
|
return o.${name}.apply(o, args);`;
|
||||||
return <MethodFn>new Function('o', 'args', functionBody);
|
return <MethodFn>new Function('o', 'args', functionBody);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {MapWrapper} from '../facade/collection';
|
import {MapWrapper} from '../facade/collection';
|
||||||
import {isPresent} from '../facade/lang';
|
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
|
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
|
||||||
import {ReflectorReader} from './reflector_reader';
|
import {ReflectorReader} from './reflector_reader';
|
||||||
@ -60,10 +59,10 @@ export class Reflector extends ReflectorReader {
|
|||||||
* potential dead code.
|
* potential dead code.
|
||||||
*/
|
*/
|
||||||
listUnusedKeys(): any[] {
|
listUnusedKeys(): any[] {
|
||||||
if (this._usedKeys == null) {
|
if (!this._usedKeys) {
|
||||||
throw new Error('Usage tracking is disabled');
|
throw new Error('Usage tracking is disabled');
|
||||||
}
|
}
|
||||||
var allTypes = MapWrapper.keys(this._injectableInfo);
|
const allTypes = MapWrapper.keys(this._injectableInfo);
|
||||||
return allTypes.filter(key => !this._usedKeys.has(key));
|
return allTypes.filter(key => !this._usedKeys.has(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,87 +82,73 @@ export class Reflector extends ReflectorReader {
|
|||||||
|
|
||||||
factory(type: Type<any>): Function {
|
factory(type: Type<any>): Function {
|
||||||
if (this._containsReflectionInfo(type)) {
|
if (this._containsReflectionInfo(type)) {
|
||||||
var res = this._getReflectionInfo(type).factory;
|
return this._getReflectionInfo(type).factory || null;
|
||||||
return isPresent(res) ? res : null;
|
|
||||||
} else {
|
|
||||||
return this.reflectionCapabilities.factory(type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.reflectionCapabilities.factory(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters(typeOrFunc: Type<any>): any[][] {
|
parameters(typeOrFunc: Type<any>): any[][] {
|
||||||
if (this._injectableInfo.has(typeOrFunc)) {
|
if (this._injectableInfo.has(typeOrFunc)) {
|
||||||
var res = this._getReflectionInfo(typeOrFunc).parameters;
|
return this._getReflectionInfo(typeOrFunc).parameters || [];
|
||||||
return isPresent(res) ? res : [];
|
|
||||||
} else {
|
|
||||||
return this.reflectionCapabilities.parameters(typeOrFunc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.reflectionCapabilities.parameters(typeOrFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
annotations(typeOrFunc: Type<any>): any[] {
|
annotations(typeOrFunc: Type<any>): any[] {
|
||||||
if (this._injectableInfo.has(typeOrFunc)) {
|
if (this._injectableInfo.has(typeOrFunc)) {
|
||||||
var res = this._getReflectionInfo(typeOrFunc).annotations;
|
return this._getReflectionInfo(typeOrFunc).annotations || [];
|
||||||
return isPresent(res) ? res : [];
|
|
||||||
} else {
|
|
||||||
return this.reflectionCapabilities.annotations(typeOrFunc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.reflectionCapabilities.annotations(typeOrFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
propMetadata(typeOrFunc: Type<any>): {[key: string]: any[]} {
|
propMetadata(typeOrFunc: Type<any>): {[key: string]: any[]} {
|
||||||
if (this._injectableInfo.has(typeOrFunc)) {
|
if (this._injectableInfo.has(typeOrFunc)) {
|
||||||
var res = this._getReflectionInfo(typeOrFunc).propMetadata;
|
return this._getReflectionInfo(typeOrFunc).propMetadata || {};
|
||||||
return isPresent(res) ? res : {};
|
|
||||||
} else {
|
|
||||||
return this.reflectionCapabilities.propMetadata(typeOrFunc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.reflectionCapabilities.propMetadata(typeOrFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
interfaces(type: Type<any>): any[] {
|
interfaces(type: Type<any>): any[] {
|
||||||
if (this._injectableInfo.has(type)) {
|
if (this._injectableInfo.has(type)) {
|
||||||
var res = this._getReflectionInfo(type).interfaces;
|
return this._getReflectionInfo(type).interfaces || [];
|
||||||
return isPresent(res) ? res : [];
|
|
||||||
} else {
|
|
||||||
return this.reflectionCapabilities.interfaces(type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.reflectionCapabilities.interfaces(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasLifecycleHook(type: any, lcInterface: Type<any>, lcProperty: string): boolean {
|
hasLifecycleHook(type: any, lcInterface: Type<any>, lcProperty: string): boolean {
|
||||||
var interfaces = this.interfaces(type);
|
if (this.interfaces(type).indexOf(lcInterface) !== -1) {
|
||||||
if (interfaces.indexOf(lcInterface) !== -1) {
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return this.reflectionCapabilities.hasLifecycleHook(type, lcInterface, lcProperty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.reflectionCapabilities.hasLifecycleHook(type, lcInterface, lcProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
getter(name: string): GetterFn {
|
getter(name: string): GetterFn {
|
||||||
if (this._getters.has(name)) {
|
return this._getters.has(name) ? this._getters.get(name) :
|
||||||
return this._getters.get(name);
|
this.reflectionCapabilities.getter(name);
|
||||||
} else {
|
|
||||||
return this.reflectionCapabilities.getter(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setter(name: string): SetterFn {
|
setter(name: string): SetterFn {
|
||||||
if (this._setters.has(name)) {
|
return this._setters.has(name) ? this._setters.get(name) :
|
||||||
return this._setters.get(name);
|
this.reflectionCapabilities.setter(name);
|
||||||
} else {
|
|
||||||
return this.reflectionCapabilities.setter(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
method(name: string): MethodFn {
|
method(name: string): MethodFn {
|
||||||
if (this._methods.has(name)) {
|
return this._methods.has(name) ? this._methods.get(name) :
|
||||||
return this._methods.get(name);
|
this.reflectionCapabilities.method(name);
|
||||||
} else {
|
|
||||||
return this.reflectionCapabilities.method(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_getReflectionInfo(typeOrFunc: any): ReflectionInfo {
|
_getReflectionInfo(typeOrFunc: any): ReflectionInfo {
|
||||||
if (isPresent(this._usedKeys)) {
|
if (this._usedKeys) {
|
||||||
this._usedKeys.add(typeOrFunc);
|
this._usedKeys.add(typeOrFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._injectableInfo.get(typeOrFunc);
|
return this._injectableInfo.get(typeOrFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,10 +6,11 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {global, isFunction, stringify} from '../facade/lang';
|
import {global, stringify} from '../facade/lang';
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
|
|
||||||
var _nextClassId = 0;
|
let _nextClassId = 0;
|
||||||
|
const Reflect = global.Reflect;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declares the interface to be used with {@link Class}.
|
* Declares the interface to be used with {@link Class}.
|
||||||
@ -87,7 +88,7 @@ export interface TypeDecorator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function extractAnnotation(annotation: any): any {
|
function extractAnnotation(annotation: any): any {
|
||||||
if (isFunction(annotation) && annotation.hasOwnProperty('annotation')) {
|
if (typeof annotation === 'function' && annotation.hasOwnProperty('annotation')) {
|
||||||
// it is a decorator, extract annotation
|
// it is a decorator, extract annotation
|
||||||
annotation = annotation.annotation;
|
annotation = annotation.annotation;
|
||||||
}
|
}
|
||||||
@ -99,13 +100,16 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
|
|||||||
fnOrArray === Number || fnOrArray === Array) {
|
fnOrArray === Number || fnOrArray === Array) {
|
||||||
throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`);
|
throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`);
|
||||||
}
|
}
|
||||||
if (isFunction(fnOrArray)) {
|
|
||||||
return <Function>fnOrArray;
|
if (typeof fnOrArray === 'function') {
|
||||||
} else if (fnOrArray instanceof Array) {
|
return fnOrArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(fnOrArray)) {
|
||||||
const annotations: any[] = fnOrArray;
|
const annotations: any[] = fnOrArray;
|
||||||
const annoLength = annotations.length - 1;
|
const annoLength = annotations.length - 1;
|
||||||
const fn: Function = fnOrArray[annoLength];
|
const fn: Function = fnOrArray[annoLength];
|
||||||
if (!isFunction(fn)) {
|
if (typeof fn !== 'function') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Last position of Class method array must be Function in key ${key} was '${stringify(fn)}'`);
|
`Last position of Class method array must be Function in key ${key} was '${stringify(fn)}'`);
|
||||||
}
|
}
|
||||||
@ -118,11 +122,11 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
|
|||||||
const paramAnnotations: any[] = [];
|
const paramAnnotations: any[] = [];
|
||||||
paramsAnnotations.push(paramAnnotations);
|
paramsAnnotations.push(paramAnnotations);
|
||||||
const annotation = annotations[i];
|
const annotation = annotations[i];
|
||||||
if (annotation instanceof Array) {
|
if (Array.isArray(annotation)) {
|
||||||
for (let j = 0; j < annotation.length; j++) {
|
for (let j = 0; j < annotation.length; j++) {
|
||||||
paramAnnotations.push(extractAnnotation(annotation[j]));
|
paramAnnotations.push(extractAnnotation(annotation[j]));
|
||||||
}
|
}
|
||||||
} else if (isFunction(annotation)) {
|
} else if (typeof annotation === 'function') {
|
||||||
paramAnnotations.push(extractAnnotation(annotation));
|
paramAnnotations.push(extractAnnotation(annotation));
|
||||||
} else {
|
} else {
|
||||||
paramAnnotations.push(annotation);
|
paramAnnotations.push(annotation);
|
||||||
@ -130,10 +134,10 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
|
|||||||
}
|
}
|
||||||
Reflect.defineMetadata('parameters', paramsAnnotations, fn);
|
Reflect.defineMetadata('parameters', paramsAnnotations, fn);
|
||||||
return fn;
|
return fn;
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
`Only Function or Array is supported in Class definition for key '${key}' is '${stringify(fnOrArray)}'`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Only Function or Array is supported in Class definition for key '${key}' is '${stringify(fnOrArray)}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -183,7 +187,7 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
|
|||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* var MyService = ng.Class({
|
* var MyService = ng.Class({
|
||||||
* constructor: [String, [new Query(), QueryList], function(name, queryList) {
|
* constructor: [String, [new Optional(), Service], function(name, myService) {
|
||||||
* ...
|
* ...
|
||||||
* }]
|
* }]
|
||||||
* });
|
* });
|
||||||
@ -193,7 +197,7 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
|
|||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* class MyService {
|
* class MyService {
|
||||||
* constructor(name: string, @Query() queryList: QueryList) {
|
* constructor(name: string, @Optional() myService: Service) {
|
||||||
* ...
|
* ...
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
@ -221,9 +225,11 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
|
|||||||
export function Class(clsDef: ClassDefinition): Type<any> {
|
export function Class(clsDef: ClassDefinition): Type<any> {
|
||||||
const constructor = applyParams(
|
const constructor = applyParams(
|
||||||
clsDef.hasOwnProperty('constructor') ? clsDef.constructor : undefined, 'constructor');
|
clsDef.hasOwnProperty('constructor') ? clsDef.constructor : undefined, 'constructor');
|
||||||
|
|
||||||
let proto = constructor.prototype;
|
let proto = constructor.prototype;
|
||||||
|
|
||||||
if (clsDef.hasOwnProperty('extends')) {
|
if (clsDef.hasOwnProperty('extends')) {
|
||||||
if (isFunction(clsDef.extends)) {
|
if (typeof clsDef.extends === 'function') {
|
||||||
(<Function>constructor).prototype = proto =
|
(<Function>constructor).prototype = proto =
|
||||||
Object.create((<Function>clsDef.extends).prototype);
|
Object.create((<Function>clsDef.extends).prototype);
|
||||||
} else {
|
} else {
|
||||||
@ -231,8 +237,9 @@ export function Class(clsDef: ClassDefinition): Type<any> {
|
|||||||
`Class definition 'extends' property must be a constructor function was: ${stringify(clsDef.extends)}`);
|
`Class definition 'extends' property must be a constructor function was: ${stringify(clsDef.extends)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let key in clsDef) {
|
for (let key in clsDef) {
|
||||||
if (key != 'extends' && key != 'prototype' && clsDef.hasOwnProperty(key)) {
|
if (key !== 'extends' && key !== 'prototype' && clsDef.hasOwnProperty(key)) {
|
||||||
proto[key] = applyParams(<any>clsDef[key], key);
|
proto[key] = applyParams(<any>clsDef[key], key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,10 +256,8 @@ export function Class(clsDef: ClassDefinition): Type<any> {
|
|||||||
return <Type<any>>constructor;
|
return <Type<any>>constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
var Reflect = global.Reflect;
|
|
||||||
|
|
||||||
export function makeDecorator(
|
export function makeDecorator(
|
||||||
name: string, props: {[key: string]: any}, parentClass?: any,
|
name: string, props: {[name: string]: any}, parentClass?: any,
|
||||||
chainFn: (fn: Function) => void = null): (...args: any[]) => (cls: any) => any {
|
chainFn: (fn: Function) => void = null): (...args: any[]) => (cls: any) => any {
|
||||||
const metaCtor = makeMetadataCtor([props]);
|
const metaCtor = makeMetadataCtor([props]);
|
||||||
|
|
||||||
@ -264,26 +269,28 @@ export function makeDecorator(
|
|||||||
if (this instanceof DecoratorFactory) {
|
if (this instanceof DecoratorFactory) {
|
||||||
metaCtor.call(this, objOrType);
|
metaCtor.call(this, objOrType);
|
||||||
return this;
|
return this;
|
||||||
} else {
|
|
||||||
const annotationInstance = new (<any>DecoratorFactory)(objOrType);
|
|
||||||
const chainAnnotation =
|
|
||||||
isFunction(this) && this.annotations instanceof Array ? this.annotations : [];
|
|
||||||
chainAnnotation.push(annotationInstance);
|
|
||||||
const TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls: Type<any>) {
|
|
||||||
const annotations = Reflect.getOwnMetadata('annotations', cls) || [];
|
|
||||||
annotations.push(annotationInstance);
|
|
||||||
Reflect.defineMetadata('annotations', annotations, cls);
|
|
||||||
return cls;
|
|
||||||
};
|
|
||||||
TypeDecorator.annotations = chainAnnotation;
|
|
||||||
TypeDecorator.Class = Class;
|
|
||||||
if (chainFn) chainFn(TypeDecorator);
|
|
||||||
return TypeDecorator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const annotationInstance = new (<any>DecoratorFactory)(objOrType);
|
||||||
|
const chainAnnotation =
|
||||||
|
typeof this === 'function' && Array.isArray(this.annotations) ? this.annotations : [];
|
||||||
|
chainAnnotation.push(annotationInstance);
|
||||||
|
const TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls: Type<any>) {
|
||||||
|
const annotations = Reflect.getOwnMetadata('annotations', cls) || [];
|
||||||
|
annotations.push(annotationInstance);
|
||||||
|
Reflect.defineMetadata('annotations', annotations, cls);
|
||||||
|
return cls;
|
||||||
|
};
|
||||||
|
TypeDecorator.annotations = chainAnnotation;
|
||||||
|
TypeDecorator.Class = Class;
|
||||||
|
if (chainFn) chainFn(TypeDecorator);
|
||||||
|
return TypeDecorator;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentClass) {
|
if (parentClass) {
|
||||||
DecoratorFactory.prototype = Object.create(parentClass.prototype);
|
DecoratorFactory.prototype = Object.create(parentClass.prototype);
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoratorFactory.prototype.toString = () => `@${name}`;
|
DecoratorFactory.prototype.toString = () => `@${name}`;
|
||||||
(<any>DecoratorFactory).annotationCls = DecoratorFactory;
|
(<any>DecoratorFactory).annotationCls = DecoratorFactory;
|
||||||
return DecoratorFactory;
|
return DecoratorFactory;
|
||||||
@ -295,12 +302,11 @@ function makeMetadataCtor(props: ([string, any] | {[key: string]: any})[]): any
|
|||||||
const argVal = args[i];
|
const argVal = args[i];
|
||||||
if (Array.isArray(prop)) {
|
if (Array.isArray(prop)) {
|
||||||
// plain parameter
|
// plain parameter
|
||||||
const val = !argVal || argVal === undefined ? prop[1] : argVal;
|
this[prop[0]] = !argVal || argVal === undefined ? prop[1] : argVal;
|
||||||
this[prop[0]] = val;
|
|
||||||
} else {
|
} else {
|
||||||
for (let propName in prop) {
|
for (let propName in prop) {
|
||||||
const val = !argVal || argVal[propName] === undefined ? prop[propName] : argVal[propName];
|
this[propName] =
|
||||||
this[propName] = val;
|
!argVal || argVal[propName] === undefined ? prop[propName] : argVal[propName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -309,7 +315,7 @@ function makeMetadataCtor(props: ([string, any] | {[key: string]: any})[]): any
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function makeParamDecorator(
|
export function makeParamDecorator(
|
||||||
name: string, props: ([string, any] | {[key: string]: any})[], parentClass?: any): any {
|
name: string, props: ([string, any] | {[name: string]: any})[], parentClass?: any): any {
|
||||||
const metaCtor = makeMetadataCtor(props);
|
const metaCtor = makeMetadataCtor(props);
|
||||||
function ParamDecoratorFactory(...args: any[]): any {
|
function ParamDecoratorFactory(...args: any[]): any {
|
||||||
if (this instanceof ParamDecoratorFactory) {
|
if (this instanceof ParamDecoratorFactory) {
|
||||||
@ -331,8 +337,7 @@ export function makeParamDecorator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
parameters[index] = parameters[index] || [];
|
parameters[index] = parameters[index] || [];
|
||||||
var annotationsForParam: any[] = parameters[index];
|
parameters[index].push(annotationInstance);
|
||||||
annotationsForParam.push(annotationInstance);
|
|
||||||
|
|
||||||
Reflect.defineMetadata('parameters', parameters, cls);
|
Reflect.defineMetadata('parameters', parameters, cls);
|
||||||
return cls;
|
return cls;
|
||||||
@ -353,15 +358,15 @@ export function makePropDecorator(
|
|||||||
if (this instanceof PropDecoratorFactory) {
|
if (this instanceof PropDecoratorFactory) {
|
||||||
metaCtor.apply(this, args);
|
metaCtor.apply(this, args);
|
||||||
return this;
|
return this;
|
||||||
} else {
|
|
||||||
var decoratorInstance = new (<any>PropDecoratorFactory)(...args);
|
|
||||||
return function PropDecorator(target: any, name: string) {
|
|
||||||
const meta = Reflect.getOwnMetadata('propMetadata', target.constructor) || {};
|
|
||||||
meta[name] = meta[name] || [];
|
|
||||||
meta[name].unshift(decoratorInstance);
|
|
||||||
Reflect.defineMetadata('propMetadata', meta, target.constructor);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const decoratorInstance = new (<any>PropDecoratorFactory)(...args);
|
||||||
|
return function PropDecorator(target: any, name: string) {
|
||||||
|
const meta = Reflect.getOwnMetadata('propMetadata', target.constructor) || {};
|
||||||
|
meta[name] = meta[name] || [];
|
||||||
|
meta[name].unshift(decoratorInstance);
|
||||||
|
Reflect.defineMetadata('propMetadata', meta, target.constructor);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (parentClass) {
|
if (parentClass) {
|
||||||
PropDecoratorFactory.prototype = Object.create(parentClass.prototype);
|
PropDecoratorFactory.prototype = Object.create(parentClass.prototype);
|
||||||
|
@ -8,25 +8,22 @@
|
|||||||
|
|
||||||
import {EventEmitter} from '../facade/async';
|
import {EventEmitter} from '../facade/async';
|
||||||
|
|
||||||
import {NgZoneImpl} from './ng_zone_impl';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An injectable service for executing work inside or outside of the Angular zone.
|
* An injectable service for executing work inside or outside of the Angular zone.
|
||||||
*
|
*
|
||||||
* The most common use of this service is to optimize performance when starting a work consisting of
|
* The most common use of this service is to optimize performance when starting a work consisting of
|
||||||
* one or more asynchronous tasks that don't require UI updates or error handling to be handled by
|
* one or more asynchronous tasks that don't require UI updates or error handling to be handled by
|
||||||
* Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks
|
* Angular. Such tasks can be kicked off via {@link runOutsideAngular} and if needed, these tasks
|
||||||
* can reenter the Angular zone via {@link #run}.
|
* can reenter the Angular zone via {@link run}.
|
||||||
*
|
*
|
||||||
* <!-- TODO: add/fix links to:
|
* <!-- TODO: add/fix links to:
|
||||||
* - docs explaining zones and the use of zones in Angular and change-detection
|
* - docs explaining zones and the use of zones in Angular and change-detection
|
||||||
* - link to runOutsideAngular/run (throughout this file!)
|
* - link to runOutsideAngular/run (throughout this file!)
|
||||||
* -->
|
* -->
|
||||||
*
|
*
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/lY9m8HLy7z06vDoUaSN2?p=preview))
|
* ### Example
|
||||||
* ```
|
* ```
|
||||||
* import {Component, View, NgZone} from '@angular/core';
|
* import {Component, NgZone} from '@angular/core';
|
||||||
* import {NgIf} from '@angular/common';
|
* import {NgIf} from '@angular/common';
|
||||||
*
|
*
|
||||||
* @Component({
|
* @Component({
|
||||||
@ -67,7 +64,6 @@ import {NgZoneImpl} from './ng_zone_impl';
|
|||||||
* }}));
|
* }}));
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* _increaseProgress(doneCallback: () => void) {
|
* _increaseProgress(doneCallback: () => void) {
|
||||||
* this.progress += 1;
|
* this.progress += 1;
|
||||||
* console.log(`Current progress: ${this.progress}%`);
|
* console.log(`Current progress: ${this.progress}%`);
|
||||||
@ -83,82 +79,85 @@ import {NgZoneImpl} from './ng_zone_impl';
|
|||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export class NgZone {
|
export class NgZone {
|
||||||
static isInAngularZone(): boolean { return NgZoneImpl.isInAngularZone(); }
|
private outer: Zone;
|
||||||
static assertInAngularZone(): void {
|
private inner: Zone;
|
||||||
if (!NgZoneImpl.isInAngularZone()) {
|
|
||||||
throw new Error('Expected to be in Angular Zone, but it is not!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static assertNotInAngularZone(): void {
|
|
||||||
if (NgZoneImpl.isInAngularZone()) {
|
|
||||||
throw new Error('Expected to not be in Angular Zone, but it is!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _zoneImpl: NgZoneImpl;
|
|
||||||
|
|
||||||
private _hasPendingMicrotasks: boolean = false;
|
private _hasPendingMicrotasks: boolean = false;
|
||||||
private _hasPendingMacrotasks: boolean = false;
|
private _hasPendingMacrotasks: boolean = false;
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
private _isStable = true;
|
private _isStable = true;
|
||||||
/** @internal */
|
private _nesting: number = 0;
|
||||||
private _nesting = 0;
|
|
||||||
/** @internal */
|
|
||||||
private _onUnstable: EventEmitter<any> = new EventEmitter(false);
|
private _onUnstable: EventEmitter<any> = new EventEmitter(false);
|
||||||
/** @internal */
|
|
||||||
private _onMicrotaskEmpty: EventEmitter<any> = new EventEmitter(false);
|
private _onMicrotaskEmpty: EventEmitter<any> = new EventEmitter(false);
|
||||||
/** @internal */
|
|
||||||
private _onStable: EventEmitter<any> = new EventEmitter(false);
|
private _onStable: EventEmitter<any> = new EventEmitter(false);
|
||||||
/** @internal */
|
|
||||||
private _onErrorEvents: EventEmitter<any> = new EventEmitter(false);
|
private _onErrorEvents: EventEmitter<any> = new EventEmitter(false);
|
||||||
|
|
||||||
constructor({enableLongStackTrace = false}) {
|
constructor({enableLongStackTrace = false}) {
|
||||||
this._zoneImpl = new NgZoneImpl({
|
if (typeof Zone == 'undefined') {
|
||||||
trace: enableLongStackTrace,
|
throw new Error('Angular requires Zone.js prolyfill.');
|
||||||
onEnter: () => {
|
}
|
||||||
// console.log('ZONE.enter', this._nesting, this._isStable);
|
|
||||||
this._nesting++;
|
Zone.assertZonePatched();
|
||||||
if (this._isStable) {
|
|
||||||
this._isStable = false;
|
this.outer = this.inner = Zone.current;
|
||||||
this._onUnstable.emit(null);
|
|
||||||
}
|
if ((Zone as any)['wtfZoneSpec']) {
|
||||||
},
|
this.inner = this.inner.fork((Zone as any)['wtfZoneSpec']);
|
||||||
onLeave: () => {
|
}
|
||||||
this._nesting--;
|
|
||||||
// console.log('ZONE.leave', this._nesting, this._isStable);
|
if (enableLongStackTrace && (Zone as any)['longStackTraceZoneSpec']) {
|
||||||
this._checkStable();
|
this.inner = this.inner.fork((Zone as any)['longStackTraceZoneSpec']);
|
||||||
},
|
}
|
||||||
setMicrotask: (hasMicrotasks: boolean) => {
|
|
||||||
this._hasPendingMicrotasks = hasMicrotasks;
|
this.forkInnerZoneWithAngularBehavior();
|
||||||
this._checkStable();
|
|
||||||
},
|
|
||||||
setMacrotask: (hasMacrotasks: boolean) => { this._hasPendingMacrotasks = hasMacrotasks; },
|
|
||||||
onError: (error: any) => this._onErrorEvents.emit(error)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _checkStable() {
|
static isInAngularZone(): boolean { return Zone.current.get('isAngularZone') === true; }
|
||||||
if (this._nesting == 0) {
|
|
||||||
if (!this._hasPendingMicrotasks && !this._isStable) {
|
static assertInAngularZone(): void {
|
||||||
try {
|
if (!NgZone.isInAngularZone()) {
|
||||||
// console.log('ZONE.microtaskEmpty');
|
throw new Error('Expected to be in Angular Zone, but it is not!');
|
||||||
this._nesting++;
|
|
||||||
this._onMicrotaskEmpty.emit(null);
|
|
||||||
} finally {
|
|
||||||
this._nesting--;
|
|
||||||
if (!this._hasPendingMicrotasks) {
|
|
||||||
try {
|
|
||||||
// console.log('ZONE.stable', this._nesting, this._isStable);
|
|
||||||
this.runOutsideAngular(() => this._onStable.emit(null));
|
|
||||||
} finally {
|
|
||||||
this._isStable = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
static assertNotInAngularZone(): void {
|
||||||
|
if (NgZone.isInAngularZone()) {
|
||||||
|
throw new Error('Expected to not be in Angular Zone, but it is!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the `fn` function synchronously within the Angular zone and returns value returned by
|
||||||
|
* the function.
|
||||||
|
*
|
||||||
|
* Running functions via `run` allows you to reenter Angular zone from a task that was executed
|
||||||
|
* outside of the Angular zone (typically started via {@link runOutsideAngular}).
|
||||||
|
*
|
||||||
|
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
||||||
|
* within the Angular zone.
|
||||||
|
*
|
||||||
|
* If a synchronous error happens it will be rethrown and not reported via `onError`.
|
||||||
|
*/
|
||||||
|
run(fn: () => any): any { return this.inner.run(fn); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as `run`, except that synchronous errors are caught and forwarded via `onError` and not
|
||||||
|
* rethrown.
|
||||||
|
*/
|
||||||
|
runGuarded(fn: () => any): any { return this.inner.runGuarded(fn); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the `fn` function synchronously in Angular's parent zone and returns value returned by
|
||||||
|
* the function.
|
||||||
|
*
|
||||||
|
* Running functions via `runOutsideAngular` allows you to escape Angular's zone and do work that
|
||||||
|
* doesn't trigger Angular change-detection or is subject to Angular's error handling.
|
||||||
|
*
|
||||||
|
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
||||||
|
* outside of the Angular zone.
|
||||||
|
*
|
||||||
|
* Use {@link run} to reenter the Angular zone and do work that updates the application model.
|
||||||
|
*/
|
||||||
|
runOutsideAngular(fn: () => any): any { return this.outer.run(fn); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies when code enters Angular Zone. This gets fired first on VM Turn.
|
* Notifies when code enters Angular Zone. This gets fired first on VM Turn.
|
||||||
@ -185,51 +184,98 @@ export class NgZone {
|
|||||||
get onError(): EventEmitter<any> { return this._onErrorEvents; }
|
get onError(): EventEmitter<any> { return this._onErrorEvents; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether there are no outstanding microtasks or microtasks.
|
* Whether there are no outstanding microtasks or macrotasks.
|
||||||
*/
|
*/
|
||||||
get isStable(): boolean { return this._isStable; }
|
get isStable(): boolean { return this._isStable; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether there are any outstanding microtasks.
|
|
||||||
*/
|
|
||||||
get hasPendingMicrotasks(): boolean { return this._hasPendingMicrotasks; }
|
get hasPendingMicrotasks(): boolean { return this._hasPendingMicrotasks; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether there are any outstanding microtasks.
|
|
||||||
*/
|
|
||||||
get hasPendingMacrotasks(): boolean { return this._hasPendingMacrotasks; }
|
get hasPendingMacrotasks(): boolean { return this._hasPendingMacrotasks; }
|
||||||
|
|
||||||
/**
|
private checkStable() {
|
||||||
* Executes the `fn` function synchronously within the Angular zone and returns value returned by
|
if (this._nesting == 0 && !this._hasPendingMicrotasks && !this._isStable) {
|
||||||
* the function.
|
try {
|
||||||
*
|
this._nesting++;
|
||||||
* Running functions via `run` allows you to reenter Angular zone from a task that was executed
|
this._onMicrotaskEmpty.emit(null);
|
||||||
* outside of the Angular zone (typically started via {@link #runOutsideAngular}).
|
} finally {
|
||||||
*
|
this._nesting--;
|
||||||
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
if (!this._hasPendingMicrotasks) {
|
||||||
* within the Angular zone.
|
try {
|
||||||
*
|
this.runOutsideAngular(() => this._onStable.emit(null));
|
||||||
* If a synchronous error happens it will be rethrown and not reported via `onError`.
|
} finally {
|
||||||
*/
|
this._isStable = true;
|
||||||
run(fn: () => any): any { return this._zoneImpl.runInner(fn); }
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
private forkInnerZoneWithAngularBehavior() {
|
||||||
* Same as #run, except that synchronous errors are caught and forwarded
|
this.inner = this.inner.fork({
|
||||||
* via `onError` and not rethrown.
|
name: 'angular',
|
||||||
*/
|
properties: <any>{'isAngularZone': true},
|
||||||
runGuarded(fn: () => any): any { return this._zoneImpl.runInnerGuarded(fn); }
|
onInvokeTask: (delegate: ZoneDelegate, current: Zone, target: Zone, task: Task,
|
||||||
|
applyThis: any, applyArgs: any): any => {
|
||||||
|
try {
|
||||||
|
this.onEnter();
|
||||||
|
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
||||||
|
} finally {
|
||||||
|
this.onLeave();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the `fn` function synchronously in Angular's parent zone and returns value returned by
|
onInvoke: (delegate: ZoneDelegate, current: Zone, target: Zone, callback: Function,
|
||||||
* the function.
|
applyThis: any, applyArgs: any[], source: string): any => {
|
||||||
*
|
try {
|
||||||
* Running functions via `runOutsideAngular` allows you to escape Angular's zone and do work that
|
this.onEnter();
|
||||||
* doesn't trigger Angular change-detection or is subject to Angular's error handling.
|
return delegate.invoke(target, callback, applyThis, applyArgs, source);
|
||||||
*
|
} finally {
|
||||||
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
this.onLeave();
|
||||||
* outside of the Angular zone.
|
}
|
||||||
*
|
},
|
||||||
* Use {@link #run} to reenter the Angular zone and do work that updates the application model.
|
|
||||||
*/
|
onHasTask:
|
||||||
runOutsideAngular(fn: () => any): any { return this._zoneImpl.runOuter(fn); }
|
(delegate: ZoneDelegate, current: Zone, target: Zone, hasTaskState: HasTaskState) => {
|
||||||
|
delegate.hasTask(target, hasTaskState);
|
||||||
|
if (current === target) {
|
||||||
|
// We are only interested in hasTask events which originate from our zone
|
||||||
|
// (A child hasTask event is not interesting to us)
|
||||||
|
if (hasTaskState.change == 'microTask') {
|
||||||
|
this.setHasMicrotask(hasTaskState.microTask);
|
||||||
|
} else if (hasTaskState.change == 'macroTask') {
|
||||||
|
this.setHasMacrotask(hasTaskState.macroTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onHandleError: (delegate: ZoneDelegate, current: Zone, target: Zone, error: any): boolean => {
|
||||||
|
delegate.handleError(target, error);
|
||||||
|
this.triggerError(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private onEnter() {
|
||||||
|
this._nesting++;
|
||||||
|
if (this._isStable) {
|
||||||
|
this._isStable = false;
|
||||||
|
this._onUnstable.emit(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onLeave() {
|
||||||
|
this._nesting--;
|
||||||
|
this.checkStable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setHasMicrotask(hasMicrotasks: boolean) {
|
||||||
|
this._hasPendingMicrotasks = hasMicrotasks;
|
||||||
|
this.checkStable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setHasMacrotask(hasMacrotasks: boolean) { this._hasPendingMacrotasks = hasMacrotasks; }
|
||||||
|
|
||||||
|
private triggerError(error: any) { this._onErrorEvents.emit(error); }
|
||||||
}
|
}
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
/**
|
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
export class NgZoneImpl {
|
|
||||||
static isInAngularZone(): boolean { return Zone.current.get('isAngularZone') === true; }
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
private outer: Zone;
|
|
||||||
/** @internal */
|
|
||||||
private inner: Zone;
|
|
||||||
|
|
||||||
private onEnter: () => void;
|
|
||||||
private onLeave: () => void;
|
|
||||||
private setMicrotask: (hasMicrotasks: boolean) => void;
|
|
||||||
private setMacrotask: (hasMacrotasks: boolean) => void;
|
|
||||||
private onError: (error: any) => void;
|
|
||||||
|
|
||||||
constructor({trace, onEnter, onLeave, setMicrotask, setMacrotask, onError}: {
|
|
||||||
trace: boolean,
|
|
||||||
onEnter: () => void,
|
|
||||||
onLeave: () => void,
|
|
||||||
setMicrotask: (hasMicrotasks: boolean) => void,
|
|
||||||
setMacrotask: (hasMacrotasks: boolean) => void,
|
|
||||||
onError: (error: any) => void
|
|
||||||
}) {
|
|
||||||
this.onEnter = onEnter;
|
|
||||||
this.onLeave = onLeave;
|
|
||||||
this.setMicrotask = setMicrotask;
|
|
||||||
this.setMacrotask = setMacrotask;
|
|
||||||
this.onError = onError;
|
|
||||||
|
|
||||||
if (typeof Zone == 'undefined') {
|
|
||||||
throw new Error('Angular requires Zone.js prolyfill.');
|
|
||||||
}
|
|
||||||
Zone.assertZonePatched();
|
|
||||||
this.outer = this.inner = Zone.current;
|
|
||||||
if ((Zone as any)['wtfZoneSpec']) {
|
|
||||||
this.inner = this.inner.fork((Zone as any)['wtfZoneSpec']);
|
|
||||||
}
|
|
||||||
if (trace && (Zone as any)['longStackTraceZoneSpec']) {
|
|
||||||
this.inner = this.inner.fork((Zone as any)['longStackTraceZoneSpec']);
|
|
||||||
}
|
|
||||||
this.inner = this.inner.fork({
|
|
||||||
name: 'angular',
|
|
||||||
properties: <any>{'isAngularZone': true},
|
|
||||||
onInvokeTask: (delegate: ZoneDelegate, current: Zone, target: Zone, task: Task,
|
|
||||||
applyThis: any, applyArgs: any): any => {
|
|
||||||
try {
|
|
||||||
this.onEnter();
|
|
||||||
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
|
||||||
} finally {
|
|
||||||
this.onLeave();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
onInvoke: (delegate: ZoneDelegate, current: Zone, target: Zone, callback: Function,
|
|
||||||
applyThis: any, applyArgs: any[], source: string): any => {
|
|
||||||
try {
|
|
||||||
this.onEnter();
|
|
||||||
return delegate.invoke(target, callback, applyThis, applyArgs, source);
|
|
||||||
} finally {
|
|
||||||
this.onLeave();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onHasTask:
|
|
||||||
(delegate: ZoneDelegate, current: Zone, target: Zone, hasTaskState: HasTaskState) => {
|
|
||||||
delegate.hasTask(target, hasTaskState);
|
|
||||||
if (current === target) {
|
|
||||||
// We are only interested in hasTask events which originate from our zone
|
|
||||||
// (A child hasTask event is not interesting to us)
|
|
||||||
if (hasTaskState.change == 'microTask') {
|
|
||||||
this.setMicrotask(hasTaskState.microTask);
|
|
||||||
} else if (hasTaskState.change == 'macroTask') {
|
|
||||||
this.setMacrotask(hasTaskState.macroTask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onHandleError: (delegate: ZoneDelegate, current: Zone, target: Zone, error: any): boolean => {
|
|
||||||
delegate.handleError(target, error);
|
|
||||||
this.onError(error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
runInner(fn: () => any): any { return this.inner.run(fn); };
|
|
||||||
runInnerGuarded(fn: () => any): any { return this.inner.runGuarded(fn); };
|
|
||||||
runOuter(fn: () => any): any { return this.outer.run(fn); };
|
|
||||||
}
|
|
@ -146,6 +146,87 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||||||
expect(kf[1]).toEqual([1, {'background': 'blue'}]);
|
expect(kf[1]).toEqual([1, {'background': 'blue'}]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
describe('animation aliases', () => {
|
||||||
|
it('should animate the ":enter" animation alias as "void => *"', fakeAsync(() => {
|
||||||
|
TestBed.overrideComponent(DummyIfCmp, {
|
||||||
|
set: {
|
||||||
|
template: `
|
||||||
|
<div *ngIf="exp" [@myAnimation]="exp"></div>
|
||||||
|
`,
|
||||||
|
animations: [trigger(
|
||||||
|
'myAnimation',
|
||||||
|
[transition(
|
||||||
|
':enter',
|
||||||
|
[style({'opacity': 0}), animate('500ms', style({opacity: 1}))])])]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const driver = TestBed.get(AnimationDriver) as MockAnimationDriver;
|
||||||
|
let fixture = TestBed.createComponent(DummyIfCmp);
|
||||||
|
var cmp = fixture.componentInstance;
|
||||||
|
cmp.exp = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(driver.log.length).toEqual(1);
|
||||||
|
|
||||||
|
var animation = driver.log[0];
|
||||||
|
expect(animation['duration']).toEqual(500);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should animate the ":leave" animation alias as "* => void"', fakeAsync(() => {
|
||||||
|
TestBed.overrideComponent(DummyIfCmp, {
|
||||||
|
set: {
|
||||||
|
template: `
|
||||||
|
<div *ngIf="exp" [@myAnimation]="exp"></div>
|
||||||
|
`,
|
||||||
|
animations: [trigger(
|
||||||
|
'myAnimation',
|
||||||
|
[transition(':leave', [animate('999ms', style({opacity: 0}))])])]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const driver = TestBed.get(AnimationDriver) as MockAnimationDriver;
|
||||||
|
let fixture = TestBed.createComponent(DummyIfCmp);
|
||||||
|
var cmp = fixture.componentInstance;
|
||||||
|
cmp.exp = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(driver.log.length).toEqual(0);
|
||||||
|
|
||||||
|
cmp.exp = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(driver.log.length).toEqual(1);
|
||||||
|
|
||||||
|
var animation = driver.log[0];
|
||||||
|
expect(animation['duration']).toEqual(999);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should throw an error when an unsupported alias is detected which is prefixed a colon value',
|
||||||
|
fakeAsync(() => {
|
||||||
|
TestBed.overrideComponent(DummyIfCmp, {
|
||||||
|
set: {
|
||||||
|
template: `
|
||||||
|
<div *ngIf="exp" [@myAnimation]="exp"></div>
|
||||||
|
`,
|
||||||
|
animations: [trigger(
|
||||||
|
'myAnimation',
|
||||||
|
[transition(':dont_leave_me', [animate('444ms', style({opacity: 0}))])])]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var message = '';
|
||||||
|
try {
|
||||||
|
let fixture = TestBed.createComponent(DummyIfCmp);
|
||||||
|
} catch (e) {
|
||||||
|
message = e.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(message).toMatch(
|
||||||
|
/the transition alias value ":dont_leave_me" is not supported/);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
it('should animate between * and void and back even when no expression is assigned',
|
it('should animate between * and void and back even when no expression is assigned',
|
||||||
fakeAsync(() => {
|
fakeAsync(() => {
|
||||||
TestBed.overrideComponent(DummyIfCmp, {
|
TestBed.overrideComponent(DummyIfCmp, {
|
||||||
|
@ -74,7 +74,7 @@ export function main() {
|
|||||||
var cdRef = <any>new SpyChangeDetectorRef();
|
var cdRef = <any>new SpyChangeDetectorRef();
|
||||||
try {
|
try {
|
||||||
ref.registerChangeDetector(cdRef);
|
ref.registerChangeDetector(cdRef);
|
||||||
cdRef.spy('detectChanges').andCallFake(() => ref.tick());
|
cdRef.spy('detectChanges').and.callFake(() => ref.tick());
|
||||||
expect(() => ref.tick()).toThrowError('ApplicationRef.tick is called recursively');
|
expect(() => ref.tick()).toThrowError('ApplicationRef.tick is called recursively');
|
||||||
} finally {
|
} finally {
|
||||||
ref.unregisterChangeDetector(cdRef);
|
ref.unregisterChangeDetector(cdRef);
|
||||||
|
@ -31,17 +31,17 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return the first suitable implementation', () => {
|
it('should return the first suitable implementation', () => {
|
||||||
factory1.spy('supports').andReturn(false);
|
factory1.spy('supports').and.returnValue(false);
|
||||||
factory2.spy('supports').andReturn(true);
|
factory2.spy('supports').and.returnValue(true);
|
||||||
factory3.spy('supports').andReturn(true);
|
factory3.spy('supports').and.returnValue(true);
|
||||||
|
|
||||||
var differs = IterableDiffers.create(<any>[factory1, factory2, factory3]);
|
var differs = IterableDiffers.create(<any>[factory1, factory2, factory3]);
|
||||||
expect(differs.find('some object')).toBe(factory2);
|
expect(differs.find('some object')).toBe(factory2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should copy over differs from the parent repo', () => {
|
it('should copy over differs from the parent repo', () => {
|
||||||
factory1.spy('supports').andReturn(true);
|
factory1.spy('supports').and.returnValue(true);
|
||||||
factory2.spy('supports').andReturn(false);
|
factory2.spy('supports').and.returnValue(false);
|
||||||
|
|
||||||
var parent = IterableDiffers.create(<any>[factory1]);
|
var parent = IterableDiffers.create(<any>[factory1]);
|
||||||
var child = IterableDiffers.create(<any>[factory2], parent);
|
var child = IterableDiffers.create(<any>[factory2], parent);
|
||||||
|
@ -12,7 +12,6 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_in
|
|||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {iterateListLike} from '../../src/facade/collection';
|
import {iterateListLike} from '../../src/facade/collection';
|
||||||
import {StringWrapper} from '../../src/facade/lang';
|
|
||||||
|
|
||||||
interface _JsQueryList {
|
interface _JsQueryList {
|
||||||
filter(c: any): any;
|
filter(c: any): any;
|
||||||
@ -104,8 +103,8 @@ export function main() {
|
|||||||
it('should support toString', () => {
|
it('should support toString', () => {
|
||||||
queryList.reset(['one', 'two']);
|
queryList.reset(['one', 'two']);
|
||||||
var listString = queryList.toString();
|
var listString = queryList.toString();
|
||||||
expect(StringWrapper.contains(listString, 'one')).toBeTruthy();
|
expect(listString.indexOf('one') != -1).toBeTruthy();
|
||||||
expect(StringWrapper.contains(listString, 'two')).toBeTruthy();
|
expect(listString.indexOf('two') != -1).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support first and last', () => {
|
it('should support first and last', () => {
|
||||||
|
@ -66,7 +66,7 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('spy objects', () => {
|
describe('spy objects', () => {
|
||||||
var spyObj: any /** TODO #9100 */;
|
let spyObj: any;
|
||||||
|
|
||||||
beforeEach(() => { spyObj = <any>new SpyTestObj(); });
|
beforeEach(() => { spyObj = <any>new SpyTestObj(); });
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ export function main() {
|
|||||||
() => { expect(spyObj.spy('someFunc')).not.toHaveBeenCalled(); });
|
() => { expect(spyObj.spy('someFunc')).not.toHaveBeenCalled(); });
|
||||||
|
|
||||||
it('should record function calls', () => {
|
it('should record function calls', () => {
|
||||||
spyObj.spy('someFunc').andCallFake((a: any, b: any) => a + b);
|
spyObj.spy('someFunc').and.callFake((a: any, b: any) => a + b);
|
||||||
|
|
||||||
expect(spyObj.someFunc(1, 2)).toEqual(3);
|
expect(spyObj.someFunc(1, 2)).toEqual(3);
|
||||||
expect(spyObj.spy('someFunc')).toHaveBeenCalledWith(1, 2);
|
expect(spyObj.spy('someFunc')).toHaveBeenCalledWith(1, 2);
|
||||||
@ -106,12 +106,6 @@ export function main() {
|
|||||||
|
|
||||||
it('should create spys for all methods',
|
it('should create spys for all methods',
|
||||||
() => { expect(() => spyObj.someFunc()).not.toThrow(); });
|
() => { expect(() => spyObj.someFunc()).not.toThrow(); });
|
||||||
|
|
||||||
it('should create a default spy that does not fail for numbers', () => {
|
|
||||||
// Previously needed for rtts_assert. Revisit this behavior.
|
|
||||||
expect(spyObj.someFunc()).toBe(null);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ export {inject} from './test_bed';
|
|||||||
export * from './logger';
|
export * from './logger';
|
||||||
export * from './ng_zone_mock';
|
export * from './ng_zone_mock';
|
||||||
|
|
||||||
export var proxy: ClassDecorator = (t: any /** TODO #9100 */) => t;
|
export var proxy: ClassDecorator = (t: any) => t;
|
||||||
|
|
||||||
var _global = <any>(typeof window === 'undefined' ? global : window);
|
var _global = <any>(typeof window === 'undefined' ? global : window);
|
||||||
|
|
||||||
@ -35,7 +35,6 @@ var jsmIIt = _global.fit;
|
|||||||
var jsmXIt = _global.xit;
|
var jsmXIt = _global.xit;
|
||||||
|
|
||||||
var runnerStack: BeforeEachRunner[] = [];
|
var runnerStack: BeforeEachRunner[] = [];
|
||||||
var inIt = false;
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000;
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000;
|
||||||
var globalTimeOut = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
var globalTimeOut = jasmine.DEFAULT_TIMEOUT_INTERVAL;
|
||||||
|
|
||||||
@ -123,7 +122,7 @@ function _it(jsmFn: Function, name: string, testFn: Function, testTimeOut: numbe
|
|||||||
var runner = runnerStack[runnerStack.length - 1];
|
var runner = runnerStack[runnerStack.length - 1];
|
||||||
var timeOut = Math.max(globalTimeOut, testTimeOut);
|
var timeOut = Math.max(globalTimeOut, testTimeOut);
|
||||||
|
|
||||||
jsmFn(name, (done: any /** TODO #9100 */) => {
|
jsmFn(name, (done: any) => {
|
||||||
var completerProvider = {
|
var completerProvider = {
|
||||||
provide: AsyncTestCompleter,
|
provide: AsyncTestCompleter,
|
||||||
useFactory: () => {
|
useFactory: () => {
|
||||||
@ -134,7 +133,6 @@ function _it(jsmFn: Function, name: string, testFn: Function, testTimeOut: numbe
|
|||||||
testBed.configureTestingModule({providers: [completerProvider]});
|
testBed.configureTestingModule({providers: [completerProvider]});
|
||||||
runner.run();
|
runner.run();
|
||||||
|
|
||||||
inIt = true;
|
|
||||||
if (testFn.length == 0) {
|
if (testFn.length == 0) {
|
||||||
let retVal = testFn();
|
let retVal = testFn();
|
||||||
if (isPromise(retVal)) {
|
if (isPromise(retVal)) {
|
||||||
@ -148,44 +146,26 @@ function _it(jsmFn: Function, name: string, testFn: Function, testTimeOut: numbe
|
|||||||
// Asynchronous test function that takes in 'done' parameter.
|
// Asynchronous test function that takes in 'done' parameter.
|
||||||
testFn(done);
|
testFn(done);
|
||||||
}
|
}
|
||||||
inIt = false;
|
|
||||||
}, timeOut);
|
}, timeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function it(
|
export function it(name: any, fn: any, timeOut: any = null): void {
|
||||||
name: any /** TODO #9100 */, fn: any /** TODO #9100 */,
|
|
||||||
timeOut: any /** TODO #9100 */ = null): void {
|
|
||||||
return _it(jsmIt, name, fn, timeOut);
|
return _it(jsmIt, name, fn, timeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function xit(
|
export function xit(name: any, fn: any, timeOut: any = null): void {
|
||||||
name: any /** TODO #9100 */, fn: any /** TODO #9100 */,
|
|
||||||
timeOut: any /** TODO #9100 */ = null): void {
|
|
||||||
return _it(jsmXIt, name, fn, timeOut);
|
return _it(jsmXIt, name, fn, timeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function iit(
|
export function iit(name: any, fn: any, timeOut: any = null): void {
|
||||||
name: any /** TODO #9100 */, fn: any /** TODO #9100 */,
|
|
||||||
timeOut: any /** TODO #9100 */ = null): void {
|
|
||||||
return _it(jsmIIt, name, fn, timeOut);
|
return _it(jsmIIt, name, fn, timeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GuinessCompatibleSpy extends jasmine.Spy {
|
|
||||||
/** By chaining the spy with and.returnValue, all calls to the function will return a specific
|
|
||||||
* value. */
|
|
||||||
andReturn(val: any): void;
|
|
||||||
/** By chaining the spy with and.callFake, all calls to the spy will delegate to the supplied
|
|
||||||
* function. */
|
|
||||||
andCallFake(fn: Function): GuinessCompatibleSpy;
|
|
||||||
/** removes all recorded calls */
|
|
||||||
reset(): any /** TODO #9100 */;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SpyObject {
|
export class SpyObject {
|
||||||
constructor(type: any /** TODO #9100 */ = null) {
|
constructor(type?: any) {
|
||||||
if (type) {
|
if (type) {
|
||||||
for (var prop in type.prototype) {
|
for (let prop in type.prototype) {
|
||||||
var m: any /** TODO #9100 */ = null;
|
let m: any = null;
|
||||||
try {
|
try {
|
||||||
m = type.prototype[prop];
|
m = type.prototype[prop];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -200,23 +180,17 @@ export class SpyObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Noop so that SpyObject has the same interface as in Dart
|
|
||||||
noSuchMethod(args: any /** TODO #9100 */) {}
|
|
||||||
|
|
||||||
spy(name: any /** TODO #9100 */) {
|
spy(name: string) {
|
||||||
if (!(this as any /** TODO #9100 */)[name]) {
|
if (!(this as any)[name]) {
|
||||||
(this as any /** TODO #9100 */)[name] = this._createGuinnessCompatibleSpy(name);
|
(this as any)[name] = jasmine.createSpy(name);
|
||||||
}
|
}
|
||||||
return (this as any /** TODO #9100 */)[name];
|
return (this as any)[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
prop(name: any /** TODO #9100 */, value: any /** TODO #9100 */) {
|
prop(name: string, value: any) { (this as any)[name] = value; }
|
||||||
(this as any /** TODO #9100 */)[name] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static stub(
|
static stub(object: any = null, config: any = null, overrides: any = null) {
|
||||||
object: any /** TODO #9100 */ = null, config: any /** TODO #9100 */ = null,
|
|
||||||
overrides: any /** TODO #9100 */ = null) {
|
|
||||||
if (!(object instanceof SpyObject)) {
|
if (!(object instanceof SpyObject)) {
|
||||||
overrides = config;
|
overrides = config;
|
||||||
config = object;
|
config = object;
|
||||||
@ -224,18 +198,7 @@ export class SpyObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var m = StringMapWrapper.merge(config, overrides);
|
var m = StringMapWrapper.merge(config, overrides);
|
||||||
Object.keys(m).forEach(key => { object.spy(key).andReturn(m[key]); });
|
Object.keys(m).forEach(key => { object.spy(key).and.returnValue(m[key]); });
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_createGuinnessCompatibleSpy(name: any /** TODO #9100 */): GuinessCompatibleSpy {
|
|
||||||
var newSpy: GuinessCompatibleSpy = <any>jasmine.createSpy(name);
|
|
||||||
newSpy.andCallFake = <any>newSpy.and.callFake;
|
|
||||||
newSpy.andReturn = <any>newSpy.and.returnValue;
|
|
||||||
newSpy.reset = <any>newSpy.calls.reset;
|
|
||||||
// revisit return null here (previously needed for rtts_assert).
|
|
||||||
newSpy.and.returnValue(null);
|
|
||||||
return newSpy;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -78,14 +78,14 @@ export class EventEmitter<T> extends Subject<T> {
|
|||||||
emit(value?: T) { super.next(value); }
|
emit(value?: T) { super.next(value); }
|
||||||
|
|
||||||
subscribe(generatorOrNext?: any, error?: any, complete?: any): any {
|
subscribe(generatorOrNext?: any, error?: any, complete?: any): any {
|
||||||
let schedulerFn: any /** TODO #9100 */;
|
let schedulerFn: (t: any) => any;
|
||||||
let errorFn = (err: any): any /** TODO #9100 */ => null;
|
let errorFn = (err: any): any => null;
|
||||||
let completeFn = (): any /** TODO #9100 */ => null;
|
let completeFn = (): any => null;
|
||||||
|
|
||||||
if (generatorOrNext && typeof generatorOrNext === 'object') {
|
if (generatorOrNext && typeof generatorOrNext === 'object') {
|
||||||
schedulerFn = this.__isAsync ? (value: any /** TODO #9100 */) => {
|
schedulerFn = this.__isAsync ? (value: any) => {
|
||||||
setTimeout(() => generatorOrNext.next(value));
|
setTimeout(() => generatorOrNext.next(value));
|
||||||
} : (value: any /** TODO #9100 */) => { generatorOrNext.next(value); };
|
} : (value: any) => { generatorOrNext.next(value); };
|
||||||
|
|
||||||
if (generatorOrNext.error) {
|
if (generatorOrNext.error) {
|
||||||
errorFn = this.__isAsync ? (err) => { setTimeout(() => generatorOrNext.error(err)); } :
|
errorFn = this.__isAsync ? (err) => { setTimeout(() => generatorOrNext.error(err)); } :
|
||||||
@ -97,9 +97,8 @@ export class EventEmitter<T> extends Subject<T> {
|
|||||||
() => { generatorOrNext.complete(); };
|
() => { generatorOrNext.complete(); };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
schedulerFn = this.__isAsync ? (value: any /** TODO #9100 */) => {
|
schedulerFn = this.__isAsync ? (value: any) => { setTimeout(() => generatorOrNext(value)); } :
|
||||||
setTimeout(() => generatorOrNext(value));
|
(value: any) => { generatorOrNext(value); };
|
||||||
} : (value: any /** TODO #9100 */) => { generatorOrNext(value); };
|
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
errorFn =
|
errorFn =
|
||||||
|
@ -137,73 +137,6 @@ export function stringify(token: any): string {
|
|||||||
return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
|
return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StringWrapper {
|
|
||||||
static fromCharCode(code: number): string { return String.fromCharCode(code); }
|
|
||||||
|
|
||||||
static charCodeAt(s: string, index: number): number { return s.charCodeAt(index); }
|
|
||||||
|
|
||||||
static split(s: string, regExp: RegExp): string[] { return s.split(regExp); }
|
|
||||||
|
|
||||||
static equals(s: string, s2: string): boolean { return s === s2; }
|
|
||||||
|
|
||||||
static stripLeft(s: string, charVal: string): string {
|
|
||||||
if (s && s.length) {
|
|
||||||
var pos = 0;
|
|
||||||
for (var i = 0; i < s.length; i++) {
|
|
||||||
if (s[i] != charVal) break;
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
s = s.substring(pos);
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static stripRight(s: string, charVal: string): string {
|
|
||||||
if (s && s.length) {
|
|
||||||
var pos = s.length;
|
|
||||||
for (var i = s.length - 1; i >= 0; i--) {
|
|
||||||
if (s[i] != charVal) break;
|
|
||||||
pos--;
|
|
||||||
}
|
|
||||||
s = s.substring(0, pos);
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static replace(s: string, from: string, replace: string): string {
|
|
||||||
return s.replace(from, replace);
|
|
||||||
}
|
|
||||||
|
|
||||||
static replaceAll(s: string, from: RegExp, replace: string): string {
|
|
||||||
return s.replace(from, replace);
|
|
||||||
}
|
|
||||||
|
|
||||||
static slice<T>(s: string, from: number = 0, to: number = null): string {
|
|
||||||
return s.slice(from, to === null ? undefined : to);
|
|
||||||
}
|
|
||||||
|
|
||||||
static replaceAllMapped(s: string, from: RegExp, cb: (m: string[]) => string): string {
|
|
||||||
return s.replace(from, function(...matches: any[]) {
|
|
||||||
// Remove offset & string from the result array
|
|
||||||
matches.splice(-2, 2);
|
|
||||||
// The callback receives match, p1, ..., pn
|
|
||||||
return cb(matches);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static contains(s: string, substr: string): boolean { return s.indexOf(substr) != -1; }
|
|
||||||
|
|
||||||
static compare(a: string, b: string): number {
|
|
||||||
if (a < b) {
|
|
||||||
return -1;
|
|
||||||
} else if (a > b) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class StringJoiner {
|
export class StringJoiner {
|
||||||
constructor(public parts: string[] = []) {}
|
constructor(public parts: string[] = []) {}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {NumberWrapper, StringWrapper, escapeRegExp, hasConstructor} from '../src/lang';
|
import {NumberWrapper, escapeRegExp, hasConstructor} from '../src/lang';
|
||||||
|
|
||||||
class MySuperclass {}
|
class MySuperclass {}
|
||||||
class MySubclass extends MySuperclass {}
|
class MySubclass extends MySuperclass {}
|
||||||
@ -50,92 +50,4 @@ export function main() {
|
|||||||
() => { expect(NumberWrapper.isNumeric('2a')).toBe(false); });
|
() => { expect(NumberWrapper.isNumeric('2a')).toBe(false); });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('String', () => {
|
|
||||||
var s: string;
|
|
||||||
|
|
||||||
describe('slice', () => {
|
|
||||||
beforeEach(() => { s = 'abcdefghij'; });
|
|
||||||
|
|
||||||
it('should return the whole string if neither start nor end are specified',
|
|
||||||
() => { expect(StringWrapper.slice(s)).toEqual('abcdefghij'); });
|
|
||||||
|
|
||||||
it('should return up to the end if end is not specified',
|
|
||||||
() => { expect(StringWrapper.slice(s, 1)).toEqual('bcdefghij'); });
|
|
||||||
|
|
||||||
it('should support negative start',
|
|
||||||
() => { expect(StringWrapper.slice(s, -1)).toEqual('j'); });
|
|
||||||
|
|
||||||
it('should support negative end',
|
|
||||||
() => { expect(StringWrapper.slice(s, -3, -1)).toEqual('hi'); });
|
|
||||||
|
|
||||||
it('should return empty string if start is greater than end', () => {
|
|
||||||
expect(StringWrapper.slice(s, 4, 2)).toEqual('');
|
|
||||||
expect(StringWrapper.slice(s, -2, -4)).toEqual('');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('stripLeft', () => {
|
|
||||||
it('should strip the first character of the string if it matches the provided input', () => {
|
|
||||||
var input = '~angular2 is amazing';
|
|
||||||
var expectedOutput = 'angular2 is amazing';
|
|
||||||
|
|
||||||
expect(StringWrapper.stripLeft(input, '~')).toEqual(expectedOutput);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should keep stripping characters from the start until the first unmatched character',
|
|
||||||
() => {
|
|
||||||
var input = '#####hello';
|
|
||||||
var expectedOutput = 'hello';
|
|
||||||
expect(StringWrapper.stripLeft(input, '#')).toEqual(expectedOutput);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not alter the provided input if the first character does not match the provided input',
|
|
||||||
() => {
|
|
||||||
var input = '+angular2 is amazing';
|
|
||||||
expect(StringWrapper.stripLeft(input, '*')).toEqual(input);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not do any alterations when an empty string or null value is passed in', () => {
|
|
||||||
expect(StringWrapper.stripLeft('', 'S')).toEqual('');
|
|
||||||
expect(StringWrapper.stripLeft(null, 'S')).toEqual(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('stripRight', () => {
|
|
||||||
it('should strip the first character of the string if it matches the provided input', () => {
|
|
||||||
var input = 'angular2 is amazing!';
|
|
||||||
var expectedOutput = 'angular2 is amazing';
|
|
||||||
|
|
||||||
expect(StringWrapper.stripRight(input, '!')).toEqual(expectedOutput);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not alter the provided input if the first character does not match the provided input',
|
|
||||||
() => {
|
|
||||||
var input = 'angular2 is amazing+';
|
|
||||||
|
|
||||||
expect(StringWrapper.stripRight(input, '*')).toEqual(input);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should keep stripping characters from the end until the first unmatched character',
|
|
||||||
() => {
|
|
||||||
var input = 'hi&!&&&&&';
|
|
||||||
var expectedOutput = 'hi&!';
|
|
||||||
expect(StringWrapper.stripRight(input, '&')).toEqual(expectedOutput);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not do any alterations when an empty string or null value is passed in', () => {
|
|
||||||
expect(StringWrapper.stripRight('', 'S')).toEqual('');
|
|
||||||
expect(StringWrapper.stripRight(null, 'S')).toEqual(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('hasConstructor', () => {
|
|
||||||
it('should be true when the type matches',
|
|
||||||
() => { expect(hasConstructor(new MySuperclass(), MySuperclass)).toEqual(true); });
|
|
||||||
|
|
||||||
it('should be false for subtypes',
|
|
||||||
() => { expect(hasConstructor(new MySubclass(), MySuperclass)).toEqual(false); });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,8 @@ const resolvedPromise = Promise.resolve(null);
|
|||||||
* sub-groups within the form.
|
* sub-groups within the form.
|
||||||
*
|
*
|
||||||
* You can listen to the directive's `ngSubmit` event to be notified when the user has
|
* You can listen to the directive's `ngSubmit` event to be notified when the user has
|
||||||
* triggered a form submission.
|
* triggered a form submission. The `ngSubmit` event will be emitted with the original form
|
||||||
|
* submission event.
|
||||||
*
|
*
|
||||||
* {@example forms/ts/simpleForm/simple_form_example.ts region='Component'}
|
* {@example forms/ts/simpleForm/simple_form_example.ts region='Component'}
|
||||||
*
|
*
|
||||||
@ -61,7 +62,7 @@ const resolvedPromise = Promise.resolve(null);
|
|||||||
@Directive({
|
@Directive({
|
||||||
selector: 'form:not([ngNoForm]):not([formGroup]),ngForm,[ngForm]',
|
selector: 'form:not([ngNoForm]):not([formGroup]),ngForm,[ngForm]',
|
||||||
providers: [formDirectiveProvider],
|
providers: [formDirectiveProvider],
|
||||||
host: {'(submit)': 'onSubmit()', '(reset)': 'onReset()'},
|
host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'},
|
||||||
outputs: ['ngSubmit'],
|
outputs: ['ngSubmit'],
|
||||||
exportAs: 'ngForm'
|
exportAs: 'ngForm'
|
||||||
})
|
})
|
||||||
@ -102,7 +103,7 @@ export class NgForm extends ControlContainer implements Form {
|
|||||||
|
|
||||||
removeControl(dir: NgModel): void {
|
removeControl(dir: NgModel): void {
|
||||||
resolvedPromise.then(() => {
|
resolvedPromise.then(() => {
|
||||||
var container = this._findContainer(dir.path);
|
const container = this._findContainer(dir.path);
|
||||||
if (isPresent(container)) {
|
if (isPresent(container)) {
|
||||||
container.removeControl(dir.name);
|
container.removeControl(dir.name);
|
||||||
}
|
}
|
||||||
@ -111,8 +112,8 @@ export class NgForm extends ControlContainer implements Form {
|
|||||||
|
|
||||||
addFormGroup(dir: NgModelGroup): void {
|
addFormGroup(dir: NgModelGroup): void {
|
||||||
resolvedPromise.then(() => {
|
resolvedPromise.then(() => {
|
||||||
var container = this._findContainer(dir.path);
|
const container = this._findContainer(dir.path);
|
||||||
var group = new FormGroup({});
|
const group = new FormGroup({});
|
||||||
setUpFormContainer(group, dir);
|
setUpFormContainer(group, dir);
|
||||||
container.registerControl(dir.name, group);
|
container.registerControl(dir.name, group);
|
||||||
group.updateValueAndValidity({emitEvent: false});
|
group.updateValueAndValidity({emitEvent: false});
|
||||||
@ -121,7 +122,7 @@ export class NgForm extends ControlContainer implements Form {
|
|||||||
|
|
||||||
removeFormGroup(dir: NgModelGroup): void {
|
removeFormGroup(dir: NgModelGroup): void {
|
||||||
resolvedPromise.then(() => {
|
resolvedPromise.then(() => {
|
||||||
var container = this._findContainer(dir.path);
|
const container = this._findContainer(dir.path);
|
||||||
if (isPresent(container)) {
|
if (isPresent(container)) {
|
||||||
container.removeControl(dir.name);
|
container.removeControl(dir.name);
|
||||||
}
|
}
|
||||||
@ -132,16 +133,16 @@ export class NgForm extends ControlContainer implements Form {
|
|||||||
|
|
||||||
updateModel(dir: NgControl, value: any): void {
|
updateModel(dir: NgControl, value: any): void {
|
||||||
resolvedPromise.then(() => {
|
resolvedPromise.then(() => {
|
||||||
var ctrl = <FormControl>this.form.get(dir.path);
|
const ctrl = <FormControl>this.form.get(dir.path);
|
||||||
ctrl.setValue(value);
|
ctrl.setValue(value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue(value: {[key: string]: any}): void { this.control.setValue(value); }
|
setValue(value: {[key: string]: any}): void { this.control.setValue(value); }
|
||||||
|
|
||||||
onSubmit(): boolean {
|
onSubmit($event: Event): boolean {
|
||||||
this._submitted = true;
|
this._submitted = true;
|
||||||
this.ngSubmit.emit(null);
|
this.ngSubmit.emit($event);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,10 @@ export const formDirectiveProvider: any = {
|
|||||||
* its {@link AbstractControl.statusChanges} event to be notified when the validation status is
|
* its {@link AbstractControl.statusChanges} event to be notified when the validation status is
|
||||||
* re-calculated.
|
* re-calculated.
|
||||||
*
|
*
|
||||||
|
* Furthermore, you can listen to the directive's `ngSubmit` event to be notified when the user has
|
||||||
|
* triggered a form submission. The `ngSubmit` event will be emitted with the original form
|
||||||
|
* submission event.
|
||||||
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* In this example, we create form controls for first name and last name.
|
* In this example, we create form controls for first name and last name.
|
||||||
@ -59,7 +63,7 @@ export const formDirectiveProvider: any = {
|
|||||||
@Directive({
|
@Directive({
|
||||||
selector: '[formGroup]',
|
selector: '[formGroup]',
|
||||||
providers: [formDirectiveProvider],
|
providers: [formDirectiveProvider],
|
||||||
host: {'(submit)': 'onSubmit()', '(reset)': 'onReset()'},
|
host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'},
|
||||||
exportAs: 'ngForm'
|
exportAs: 'ngForm'
|
||||||
})
|
})
|
||||||
export class FormGroupDirective extends ControlContainer implements Form,
|
export class FormGroupDirective extends ControlContainer implements Form,
|
||||||
@ -107,7 +111,7 @@ export class FormGroupDirective extends ControlContainer implements Form,
|
|||||||
removeControl(dir: FormControlName): void { ListWrapper.remove(this.directives, dir); }
|
removeControl(dir: FormControlName): void { ListWrapper.remove(this.directives, dir); }
|
||||||
|
|
||||||
addFormGroup(dir: FormGroupName): void {
|
addFormGroup(dir: FormGroupName): void {
|
||||||
var ctrl: any = this.form.get(dir.path);
|
const ctrl: any = this.form.get(dir.path);
|
||||||
setUpFormContainer(ctrl, dir);
|
setUpFormContainer(ctrl, dir);
|
||||||
ctrl.updateValueAndValidity({emitEvent: false});
|
ctrl.updateValueAndValidity({emitEvent: false});
|
||||||
}
|
}
|
||||||
@ -117,7 +121,7 @@ export class FormGroupDirective extends ControlContainer implements Form,
|
|||||||
getFormGroup(dir: FormGroupName): FormGroup { return <FormGroup>this.form.get(dir.path); }
|
getFormGroup(dir: FormGroupName): FormGroup { return <FormGroup>this.form.get(dir.path); }
|
||||||
|
|
||||||
addFormArray(dir: FormArrayName): void {
|
addFormArray(dir: FormArrayName): void {
|
||||||
var ctrl: any = this.form.get(dir.path);
|
const ctrl: any = this.form.get(dir.path);
|
||||||
setUpFormContainer(ctrl, dir);
|
setUpFormContainer(ctrl, dir);
|
||||||
ctrl.updateValueAndValidity({emitEvent: false});
|
ctrl.updateValueAndValidity({emitEvent: false});
|
||||||
}
|
}
|
||||||
@ -127,13 +131,13 @@ export class FormGroupDirective extends ControlContainer implements Form,
|
|||||||
getFormArray(dir: FormArrayName): FormArray { return <FormArray>this.form.get(dir.path); }
|
getFormArray(dir: FormArrayName): FormArray { return <FormArray>this.form.get(dir.path); }
|
||||||
|
|
||||||
updateModel(dir: FormControlName, value: any): void {
|
updateModel(dir: FormControlName, value: any): void {
|
||||||
var ctrl = <FormControl>this.form.get(dir.path);
|
const ctrl = <FormControl>this.form.get(dir.path);
|
||||||
ctrl.setValue(value);
|
ctrl.setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit(): boolean {
|
onSubmit($event: Event): boolean {
|
||||||
this._submitted = true;
|
this._submitted = true;
|
||||||
this.ngSubmit.emit(null);
|
this.ngSubmit.emit($event);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core';
|
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core';
|
||||||
|
|
||||||
import {MapWrapper} from '../facade/collection';
|
import {MapWrapper} from '../facade/collection';
|
||||||
import {StringWrapper, isBlank, isPresent, isPrimitive, looseIdentical} from '../facade/lang';
|
import {isBlank, isPresent, isPrimitive, looseIdentical} from '../facade/lang';
|
||||||
|
|
||||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ export const SELECT_VALUE_ACCESSOR: any = {
|
|||||||
function _buildValueString(id: string, value: any): string {
|
function _buildValueString(id: string, value: any): string {
|
||||||
if (isBlank(id)) return `${value}`;
|
if (isBlank(id)) return `${value}`;
|
||||||
if (!isPrimitive(value)) value = 'Object';
|
if (!isPrimitive(value)) value = 'Object';
|
||||||
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
|
return `${id}: ${value}`.slice(0, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _extractId(valueString: string): string {
|
function _extractId(valueString: string): string {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import {Directive, ElementRef, Host, Input, OnDestroy, OpaqueToken, Optional, Renderer, Type, forwardRef} from '@angular/core';
|
import {Directive, ElementRef, Host, Input, OnDestroy, OpaqueToken, Optional, Renderer, Type, forwardRef} from '@angular/core';
|
||||||
|
|
||||||
import {MapWrapper} from '../facade/collection';
|
import {MapWrapper} from '../facade/collection';
|
||||||
import {StringWrapper, isBlank, isPresent, isPrimitive, isString, looseIdentical} from '../facade/lang';
|
import {isBlank, isPresent, isPrimitive, isString, looseIdentical} from '../facade/lang';
|
||||||
|
|
||||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ function _buildValueString(id: string, value: any): string {
|
|||||||
if (isBlank(id)) return `${value}`;
|
if (isBlank(id)) return `${value}`;
|
||||||
if (isString(value)) value = `'${value}'`;
|
if (isString(value)) value = `'${value}'`;
|
||||||
if (!isPrimitive(value)) value = 'Object';
|
if (!isPrimitive(value)) value = 'Object';
|
||||||
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
|
return `${id}: ${value}`.slice(0, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _extractId(valueString: string): string {
|
function _extractId(valueString: string): string {
|
||||||
|
@ -52,13 +52,14 @@ function _find(control: AbstractControl, path: Array<string|number>| string, del
|
|||||||
|
|
||||||
return (<Array<string|number>>path).reduce((v, name) => {
|
return (<Array<string|number>>path).reduce((v, name) => {
|
||||||
if (v instanceof FormGroup) {
|
if (v instanceof FormGroup) {
|
||||||
return isPresent(v.controls[name]) ? v.controls[name] : null;
|
return v.controls[name] || null;
|
||||||
} else if (v instanceof FormArray) {
|
|
||||||
var index = <number>name;
|
|
||||||
return isPresent(v.at(index)) ? v.at(index) : null;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (v instanceof FormArray) {
|
||||||
|
return v.at(<number>name) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}, control);
|
}, control);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,13 @@ import {toPromise} from 'rxjs/operator/toPromise';
|
|||||||
|
|
||||||
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
|
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
|
||||||
import {StringMapWrapper} from './facade/collection';
|
import {StringMapWrapper} from './facade/collection';
|
||||||
import {isBlank, isPresent, isString} from './facade/lang';
|
import {isPresent} from './facade/lang';
|
||||||
import {AbstractControl} from './model';
|
import {AbstractControl} from './model';
|
||||||
import {isPromise} from './private_import_core';
|
import {isPromise} from './private_import_core';
|
||||||
|
|
||||||
|
function isEmptyInputValue(value: any) {
|
||||||
|
return value == null || typeof value === 'string' && value.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Providers for validators to be used for {@link FormControl}s in a form.
|
* Providers for validators to be used for {@link FormControl}s in a form.
|
||||||
@ -60,9 +62,7 @@ export class Validators {
|
|||||||
* Validator that requires controls to have a non-empty value.
|
* Validator that requires controls to have a non-empty value.
|
||||||
*/
|
*/
|
||||||
static required(control: AbstractControl): {[key: string]: boolean} {
|
static required(control: AbstractControl): {[key: string]: boolean} {
|
||||||
return isBlank(control.value) || (isString(control.value) && control.value == '') ?
|
return isEmptyInputValue(control.value) ? {'required': true} : null;
|
||||||
{'required': true} :
|
|
||||||
null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,10 +70,12 @@ export class Validators {
|
|||||||
*/
|
*/
|
||||||
static minLength(minLength: number): ValidatorFn {
|
static minLength(minLength: number): ValidatorFn {
|
||||||
return (control: AbstractControl): {[key: string]: any} => {
|
return (control: AbstractControl): {[key: string]: any} => {
|
||||||
if (isPresent(Validators.required(control))) return null;
|
if (isEmptyInputValue(control.value)) {
|
||||||
var v: string = control.value;
|
return null; // don't validate empty values to allow optional controls
|
||||||
return v.length < minLength ?
|
}
|
||||||
{'minlength': {'requiredLength': minLength, 'actualLength': v.length}} :
|
const length = typeof control.value === 'string' ? control.value.length : 0;
|
||||||
|
return length < minLength ?
|
||||||
|
{'minlength': {'requiredLength': minLength, 'actualLength': length}} :
|
||||||
null;
|
null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -83,10 +85,9 @@ export class Validators {
|
|||||||
*/
|
*/
|
||||||
static maxLength(maxLength: number): ValidatorFn {
|
static maxLength(maxLength: number): ValidatorFn {
|
||||||
return (control: AbstractControl): {[key: string]: any} => {
|
return (control: AbstractControl): {[key: string]: any} => {
|
||||||
if (isPresent(Validators.required(control))) return null;
|
const length = typeof control.value === 'string' ? control.value.length : 0;
|
||||||
var v: string = control.value;
|
return length > maxLength ?
|
||||||
return v.length > maxLength ?
|
{'maxlength': {'requiredLength': maxLength, 'actualLength': length}} :
|
||||||
{'maxlength': {'requiredLength': maxLength, 'actualLength': v.length}} :
|
|
||||||
null;
|
null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -96,10 +97,14 @@ export class Validators {
|
|||||||
*/
|
*/
|
||||||
static pattern(pattern: string): ValidatorFn {
|
static pattern(pattern: string): ValidatorFn {
|
||||||
return (control: AbstractControl): {[key: string]: any} => {
|
return (control: AbstractControl): {[key: string]: any} => {
|
||||||
let regex = new RegExp(`^${pattern}$`);
|
if (isEmptyInputValue(control.value)) {
|
||||||
let v: string = control.value;
|
return null; // don't validate empty values to allow optional controls
|
||||||
return regex.test(v) ? null :
|
}
|
||||||
{'pattern': {'requiredPattern': `^${pattern}$`, 'actualValue': v}};
|
const regex = new RegExp(`^${pattern}$`);
|
||||||
|
const value: string = control.value;
|
||||||
|
return regex.test(value) ?
|
||||||
|
null :
|
||||||
|
{'pattern': {'requiredPattern': `^${pattern}$`, 'actualValue': value}};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,17 +529,17 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('submit and reset events', () => {
|
describe('submit and reset events', () => {
|
||||||
it('should emit ngSubmit event on submit', () => {
|
it('should emit ngSubmit event with the original submit event on submit', () => {
|
||||||
const fixture = TestBed.createComponent(FormGroupComp);
|
const fixture = TestBed.createComponent(FormGroupComp);
|
||||||
fixture.componentInstance.form = new FormGroup({'login': new FormControl('loginValue')});
|
fixture.componentInstance.form = new FormGroup({'login': new FormControl('loginValue')});
|
||||||
fixture.componentInstance.data = 'should be changed';
|
fixture.componentInstance.event = null;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const formEl = fixture.debugElement.query(By.css('form')).nativeElement;
|
const formEl = fixture.debugElement.query(By.css('form')).nativeElement;
|
||||||
dispatchEvent(formEl, 'submit');
|
dispatchEvent(formEl, 'submit');
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.componentInstance.data).toEqual('submitted');
|
expect(fixture.componentInstance.event.type).toEqual('submit');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should mark formGroup as submitted on submit event', () => {
|
it('should mark formGroup as submitted on submit event', () => {
|
||||||
@ -1760,7 +1760,7 @@ class FormControlComp {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'form-group-comp',
|
selector: 'form-group-comp',
|
||||||
template: `
|
template: `
|
||||||
<form [formGroup]="form" (ngSubmit)="data='submitted'">
|
<form [formGroup]="form" (ngSubmit)="event=$event">
|
||||||
<input type="text" formControlName="login">
|
<input type="text" formControlName="login">
|
||||||
</form>
|
</form>
|
||||||
`
|
`
|
||||||
@ -1769,7 +1769,7 @@ class FormGroupComp {
|
|||||||
control: FormControl;
|
control: FormControl;
|
||||||
form: FormGroup;
|
form: FormGroup;
|
||||||
myGroup: FormGroup;
|
myGroup: FormGroup;
|
||||||
data: string;
|
event: Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -22,7 +22,7 @@ export function main() {
|
|||||||
StandaloneNgModel, NgModelForm, NgModelGroupForm, NgModelValidBinding, NgModelNgIfForm,
|
StandaloneNgModel, NgModelForm, NgModelGroupForm, NgModelValidBinding, NgModelNgIfForm,
|
||||||
NgModelRadioForm, NgModelSelectForm, NgNoFormComp, InvalidNgModelNoName,
|
NgModelRadioForm, NgModelSelectForm, NgNoFormComp, InvalidNgModelNoName,
|
||||||
NgModelOptionsStandalone, NgModelCustomComp, NgModelCustomWrapper,
|
NgModelOptionsStandalone, NgModelCustomComp, NgModelCustomWrapper,
|
||||||
NgModelValidationBindings
|
NgModelValidationBindings, NgModelMultipleValidators
|
||||||
],
|
],
|
||||||
imports: [FormsModule]
|
imports: [FormsModule]
|
||||||
});
|
});
|
||||||
@ -237,15 +237,15 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('submit and reset events', () => {
|
describe('submit and reset events', () => {
|
||||||
it('should emit ngSubmit event on submit', fakeAsync(() => {
|
it('should emit ngSubmit event with the original submit event on submit', fakeAsync(() => {
|
||||||
const fixture = TestBed.createComponent(NgModelForm);
|
const fixture = TestBed.createComponent(NgModelForm);
|
||||||
fixture.componentInstance.name = 'old';
|
fixture.componentInstance.event = null;
|
||||||
|
|
||||||
const form = fixture.debugElement.query(By.css('form'));
|
const form = fixture.debugElement.query(By.css('form'));
|
||||||
dispatchEvent(form.nativeElement, 'submit');
|
dispatchEvent(form.nativeElement, 'submit');
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
expect(fixture.componentInstance.name).toEqual('submitted');
|
expect(fixture.componentInstance.event.type).toEqual('submit');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should mark NgForm as submitted on submit event', fakeAsync(() => {
|
it('should mark NgForm as submitted on submit event', fakeAsync(() => {
|
||||||
@ -728,6 +728,50 @@ export function main() {
|
|||||||
expect(form.valid).toEqual(true);
|
expect(form.valid).toEqual(true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should support optional fields with pattern validator', fakeAsync(() => {
|
||||||
|
const fixture = TestBed.createComponent(NgModelMultipleValidators);
|
||||||
|
fixture.componentInstance.required = false;
|
||||||
|
fixture.componentInstance.pattern = '[a-z]+';
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||||
|
const input = fixture.debugElement.query(By.css('input'));
|
||||||
|
|
||||||
|
input.nativeElement.value = '';
|
||||||
|
dispatchEvent(input.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toBeTruthy();
|
||||||
|
|
||||||
|
input.nativeElement.value = '1';
|
||||||
|
dispatchEvent(input.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toBeFalsy();
|
||||||
|
expect(form.control.hasError('pattern', ['tovalidate'])).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should support optional fields with minlength validator', fakeAsync(() => {
|
||||||
|
const fixture = TestBed.createComponent(NgModelMultipleValidators);
|
||||||
|
fixture.componentInstance.required = false;
|
||||||
|
fixture.componentInstance.minLen = 2;
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
const form = fixture.debugElement.children[0].injector.get(NgForm);
|
||||||
|
const input = fixture.debugElement.query(By.css('input'));
|
||||||
|
|
||||||
|
input.nativeElement.value = '';
|
||||||
|
dispatchEvent(input.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toBeTruthy();
|
||||||
|
|
||||||
|
input.nativeElement.value = '1';
|
||||||
|
dispatchEvent(input.nativeElement, 'input');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(form.valid).toBeFalsy();
|
||||||
|
expect(form.control.hasError('minlength', ['tovalidate'])).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
it('changes on bound properties should change the validation state of the form',
|
it('changes on bound properties should change the validation state of the form',
|
||||||
fakeAsync(() => {
|
fakeAsync(() => {
|
||||||
const fixture = TestBed.createComponent(NgModelValidationBindings);
|
const fixture = TestBed.createComponent(NgModelValidationBindings);
|
||||||
@ -854,13 +898,14 @@ class StandaloneNgModel {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'ng-model-form',
|
selector: 'ng-model-form',
|
||||||
template: `
|
template: `
|
||||||
<form (ngSubmit)="name='submitted'" (reset)="onReset()">
|
<form (ngSubmit)="event=$event" (reset)="onReset()">
|
||||||
<input name="name" [(ngModel)]="name" minlength="10" [ngModelOptions]="options">
|
<input name="name" [(ngModel)]="name" minlength="10" [ngModelOptions]="options">
|
||||||
</form>
|
</form>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
class NgModelForm {
|
class NgModelForm {
|
||||||
name: string;
|
name: string;
|
||||||
|
event: Event;
|
||||||
options = {};
|
options = {};
|
||||||
|
|
||||||
onReset() {}
|
onReset() {}
|
||||||
@ -1037,6 +1082,20 @@ class NgModelValidationBindings {
|
|||||||
pattern: string;
|
pattern: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ng-model-multiple-validators',
|
||||||
|
template: `
|
||||||
|
<form>
|
||||||
|
<input name="tovalidate" ngModel [required]="required" [minlength]="minLen" [pattern]="pattern">
|
||||||
|
</form>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class NgModelMultipleValidators {
|
||||||
|
required: boolean;
|
||||||
|
minLen: number;
|
||||||
|
pattern: string;
|
||||||
|
}
|
||||||
|
|
||||||
function sortedClassList(el: HTMLElement) {
|
function sortedClassList(el: HTMLElement) {
|
||||||
const l = getDOM().classList(el);
|
const l = getDOM().classList(el);
|
||||||
l.sort();
|
l.sort();
|
||||||
|
@ -44,21 +44,24 @@ export function main() {
|
|||||||
() => { expect(Validators.required(new FormControl(null))).toEqual({'required': true}); });
|
() => { expect(Validators.required(new FormControl(null))).toEqual({'required': true}); });
|
||||||
|
|
||||||
it('should not error on a non-empty string',
|
it('should not error on a non-empty string',
|
||||||
() => { expect(Validators.required(new FormControl('not empty'))).toEqual(null); });
|
() => { expect(Validators.required(new FormControl('not empty'))).toBeNull(); });
|
||||||
|
|
||||||
it('should accept zero as valid',
|
it('should accept zero as valid',
|
||||||
() => { expect(Validators.required(new FormControl(0))).toEqual(null); });
|
() => { expect(Validators.required(new FormControl(0))).toBeNull(); });
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('minLength', () => {
|
describe('minLength', () => {
|
||||||
it('should not error on an empty string',
|
it('should not error on an empty string',
|
||||||
() => { expect(Validators.minLength(2)(new FormControl(''))).toEqual(null); });
|
() => { expect(Validators.minLength(2)(new FormControl(''))).toBeNull(); });
|
||||||
|
|
||||||
it('should not error on null',
|
it('should not error on null',
|
||||||
() => { expect(Validators.minLength(2)(new FormControl(null))).toEqual(null); });
|
() => { expect(Validators.minLength(2)(new FormControl(null))).toBeNull(); });
|
||||||
|
|
||||||
|
it('should not error on undefined',
|
||||||
|
() => { expect(Validators.minLength(2)(new FormControl(null))).toBeNull(); });
|
||||||
|
|
||||||
it('should not error on valid strings',
|
it('should not error on valid strings',
|
||||||
() => { expect(Validators.minLength(2)(new FormControl('aa'))).toEqual(null); });
|
() => { expect(Validators.minLength(2)(new FormControl('aa'))).toBeNull(); });
|
||||||
|
|
||||||
it('should error on short strings', () => {
|
it('should error on short strings', () => {
|
||||||
expect(Validators.minLength(2)(new FormControl('a'))).toEqual({
|
expect(Validators.minLength(2)(new FormControl('a'))).toEqual({
|
||||||
@ -69,13 +72,13 @@ export function main() {
|
|||||||
|
|
||||||
describe('maxLength', () => {
|
describe('maxLength', () => {
|
||||||
it('should not error on an empty string',
|
it('should not error on an empty string',
|
||||||
() => { expect(Validators.maxLength(2)(new FormControl(''))).toEqual(null); });
|
() => { expect(Validators.maxLength(2)(new FormControl(''))).toBeNull(); });
|
||||||
|
|
||||||
it('should not error on null',
|
it('should not error on null',
|
||||||
() => { expect(Validators.maxLength(2)(new FormControl(null))).toEqual(null); });
|
() => { expect(Validators.maxLength(2)(new FormControl(null))).toBeNull(); });
|
||||||
|
|
||||||
it('should not error on valid strings',
|
it('should not error on valid strings',
|
||||||
() => { expect(Validators.maxLength(2)(new FormControl('aa'))).toEqual(null); });
|
() => { expect(Validators.maxLength(2)(new FormControl('aa'))).toBeNull(); });
|
||||||
|
|
||||||
it('should error on long strings', () => {
|
it('should error on long strings', () => {
|
||||||
expect(Validators.maxLength(2)(new FormControl('aaa'))).toEqual({
|
expect(Validators.maxLength(2)(new FormControl('aaa'))).toEqual({
|
||||||
@ -86,29 +89,25 @@ export function main() {
|
|||||||
|
|
||||||
describe('pattern', () => {
|
describe('pattern', () => {
|
||||||
it('should not error on an empty string',
|
it('should not error on an empty string',
|
||||||
() => { expect(Validators.pattern('[a-zA-Z ]*')(new FormControl(''))).toEqual(null); });
|
() => { expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(''))).toBeNull(); });
|
||||||
|
|
||||||
it('should not error on null',
|
it('should not error on null',
|
||||||
() => { expect(Validators.pattern('[a-zA-Z ]*')(new FormControl(null))).toEqual(null); });
|
() => { expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(null))).toBeNull(); });
|
||||||
|
|
||||||
|
it('should not error on undefined',
|
||||||
|
() => { expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(null))).toBeNull(); });
|
||||||
|
|
||||||
it('should not error on null value and "null" pattern',
|
it('should not error on null value and "null" pattern',
|
||||||
() => { expect(Validators.pattern('null')(new FormControl(null))).toEqual(null); });
|
() => { expect(Validators.pattern('null')(new FormControl(null))).toBeNull(); });
|
||||||
|
|
||||||
it('should not error on valid strings', () => {
|
it('should not error on valid strings',
|
||||||
expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toEqual(null);
|
() => { expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toBeNull(); });
|
||||||
});
|
|
||||||
|
|
||||||
it('should error on failure to match string', () => {
|
it('should error on failure to match string', () => {
|
||||||
expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaa0'))).toEqual({
|
expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaa0'))).toEqual({
|
||||||
'pattern': {'requiredPattern': '^[a-zA-Z ]*$', 'actualValue': 'aaa0'}
|
'pattern': {'requiredPattern': '^[a-zA-Z ]*$', 'actualValue': 'aaa0'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error on failure to match empty string', () => {
|
|
||||||
expect(Validators.pattern('[a-zA-Z]+')(new FormControl(''))).toEqual({
|
|
||||||
'pattern': {'requiredPattern': '^[a-zA-Z]+$', 'actualValue': ''}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('compose', () => {
|
describe('compose', () => {
|
||||||
@ -127,7 +126,7 @@ export function main() {
|
|||||||
|
|
||||||
it('should return null when no errors', () => {
|
it('should return null when no errors', () => {
|
||||||
var c = Validators.compose([Validators.nullValidator, Validators.nullValidator]);
|
var c = Validators.compose([Validators.nullValidator, Validators.nullValidator]);
|
||||||
expect(c(new FormControl(''))).toEqual(null);
|
expect(c(new FormControl(''))).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore nulls', () => {
|
it('should ignore nulls', () => {
|
||||||
@ -154,7 +153,7 @@ export function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it('should return null when given null',
|
it('should return null when given null',
|
||||||
() => { expect(Validators.composeAsync(null)).toEqual(null); });
|
() => { expect(Validators.composeAsync(null)).toBeNull(); });
|
||||||
|
|
||||||
it('should collect errors from all the validators', fakeAsync(() => {
|
it('should collect errors from all the validators', fakeAsync(() => {
|
||||||
var c = Validators.composeAsync([
|
var c = Validators.composeAsync([
|
||||||
@ -187,7 +186,7 @@ export function main() {
|
|||||||
(<Promise<any>>c(new FormControl('expected'))).then(v => value = v);
|
(<Promise<any>>c(new FormControl('expected'))).then(v => value = v);
|
||||||
tick(1);
|
tick(1);
|
||||||
|
|
||||||
expect(value).toEqual(null);
|
expect(value).toBeNull();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should ignore nulls', fakeAsync(() => {
|
it('should ignore nulls', fakeAsync(() => {
|
||||||
|
@ -12,7 +12,7 @@ import {Observer} from 'rxjs/Observer';
|
|||||||
|
|
||||||
import {ResponseOptions} from '../base_response_options';
|
import {ResponseOptions} from '../base_response_options';
|
||||||
import {ReadyState, RequestMethod, ResponseType} from '../enums';
|
import {ReadyState, RequestMethod, ResponseType} from '../enums';
|
||||||
import {StringWrapper, isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Connection, ConnectionBackend} from '../interfaces';
|
import {Connection, ConnectionBackend} from '../interfaces';
|
||||||
import {Request} from '../static_request';
|
import {Request} from '../static_request';
|
||||||
import {Response} from '../static_response';
|
import {Response} from '../static_response';
|
||||||
@ -75,7 +75,7 @@ export class JSONPConnection_ extends JSONPConnection {
|
|||||||
let callback = _dom.requestCallback(this._id);
|
let callback = _dom.requestCallback(this._id);
|
||||||
let url: string = req.url;
|
let url: string = req.url;
|
||||||
if (url.indexOf('=JSONP_CALLBACK&') > -1) {
|
if (url.indexOf('=JSONP_CALLBACK&') > -1) {
|
||||||
url = StringWrapper.replace(url, '=JSONP_CALLBACK&', `=${callback}&`);
|
url = url.replace('=JSONP_CALLBACK&', `=${callback}&`);
|
||||||
} else if (url.lastIndexOf('=JSONP_CALLBACK') === url.length - '=JSONP_CALLBACK'.length) {
|
} else if (url.lastIndexOf('=JSONP_CALLBACK') === url.length - '=JSONP_CALLBACK'.length) {
|
||||||
url = url.substring(0, url.length - '=JSONP_CALLBACK'.length) + `=${callback}`;
|
url = url.substring(0, url.length - '=JSONP_CALLBACK'.length) + `=${callback}`;
|
||||||
}
|
}
|
||||||
|
@ -49,19 +49,16 @@ export class Headers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (headers instanceof Headers) {
|
if (headers instanceof Headers) {
|
||||||
headers._headers.forEach((value: string[], name: string) => {
|
headers._headers.forEach((values: string[], name: string) => {
|
||||||
const lcName = name.toLowerCase();
|
values.forEach(value => this.append(name, value));
|
||||||
this._headers.set(lcName, value);
|
|
||||||
this.mayBeSetNormalizedName(name);
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(headers).forEach((name: string) => {
|
Object.keys(headers).forEach((name: string) => {
|
||||||
const value = headers[name];
|
const values: string[] = Array.isArray(headers[name]) ? headers[name] : [headers[name]];
|
||||||
const lcName = name.toLowerCase();
|
this.delete(name);
|
||||||
this._headers.set(lcName, Array.isArray(value) ? value : [value]);
|
values.forEach(value => this.append(name, value));
|
||||||
this.mayBeSetNormalizedName(name);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +85,12 @@ export class Headers {
|
|||||||
*/
|
*/
|
||||||
append(name: string, value: string): void {
|
append(name: string, value: string): void {
|
||||||
const values = this.getAll(name);
|
const values = this.getAll(name);
|
||||||
this.set(name, values === null ? [value] : [...values, value]);
|
|
||||||
|
if (values === null) {
|
||||||
|
this.set(name, value);
|
||||||
|
} else {
|
||||||
|
values.push(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,8 +134,13 @@ export class Headers {
|
|||||||
* Sets or overrides header value for given name.
|
* Sets or overrides header value for given name.
|
||||||
*/
|
*/
|
||||||
set(name: string, value: string|string[]): void {
|
set(name: string, value: string|string[]): void {
|
||||||
const strValue = Array.isArray(value) ? value.join(',') : value;
|
if (Array.isArray(value)) {
|
||||||
this._headers.set(name.toLowerCase(), [strValue]);
|
if (value.length) {
|
||||||
|
this._headers.set(name.toLowerCase(), [value.join(',')]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._headers.set(name.toLowerCase(), [value]);
|
||||||
|
}
|
||||||
this.mayBeSetNormalizedName(name);
|
this.mayBeSetNormalizedName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {StringWrapper, isPresent} from '../src/facade/lang';
|
import {isPresent} from '../src/facade/lang';
|
||||||
|
|
||||||
import {Body} from './body';
|
import {Body} from './body';
|
||||||
import {ContentType, RequestMethod, ResponseContentType} from './enums';
|
import {ContentType, RequestMethod, ResponseContentType} from './enums';
|
||||||
@ -82,7 +82,7 @@ export class Request extends Body {
|
|||||||
let search = requestOptions.search.toString();
|
let search = requestOptions.search.toString();
|
||||||
if (search.length > 0) {
|
if (search.length > 0) {
|
||||||
let prefix = '?';
|
let prefix = '?';
|
||||||
if (StringWrapper.contains(this.url, '?')) {
|
if (this.url.indexOf('?') != -1) {
|
||||||
prefix = (this.url[this.url.length - 1] == '&') ? '' : '&';
|
prefix = (this.url[this.url.length - 1] == '&') ? '' : '&';
|
||||||
}
|
}
|
||||||
// TODO: just delete search-query-looking string in url?
|
// TODO: just delete search-query-looking string in url?
|
||||||
|
@ -37,8 +37,25 @@ export function main() {
|
|||||||
secondHeaders.append('Content-Type', 'image/jpeg');
|
secondHeaders.append('Content-Type', 'image/jpeg');
|
||||||
expect(firstHeaders.has('Content-Type')).toEqual(false);
|
expect(firstHeaders.has('Content-Type')).toEqual(false);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
|
it('should preserve the list of values', () => {
|
||||||
|
const src = new Headers();
|
||||||
|
src.append('foo', 'a');
|
||||||
|
src.append('foo', 'b');
|
||||||
|
src.append('foo', 'c');
|
||||||
|
const dst = new Headers(src);
|
||||||
|
expect(dst.getAll('foo')).toEqual(src.getAll('foo'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep the last value when initialized from an object', () => {
|
||||||
|
const headers = new Headers({
|
||||||
|
'foo': 'first',
|
||||||
|
'fOo': 'second',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(headers.getAll('foo')).toEqual(['second']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('.set()', () => {
|
describe('.set()', () => {
|
||||||
it('should clear all values and re-set for the provided key', () => {
|
it('should clear all values and re-set for the provided key', () => {
|
||||||
@ -112,6 +129,14 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('.append', () => {
|
describe('.append', () => {
|
||||||
|
it('should append a value to the list', () => {
|
||||||
|
const headers = new Headers();
|
||||||
|
headers.append('foo', 'bar');
|
||||||
|
headers.append('foo', 'baz');
|
||||||
|
expect(headers.get('foo')).toEqual('bar');
|
||||||
|
expect(headers.getAll('foo')).toEqual(['bar', 'baz']);
|
||||||
|
});
|
||||||
|
|
||||||
it('should preserve the case of the first call', () => {
|
it('should preserve the case of the first call', () => {
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
|
|
||||||
@ -133,7 +158,6 @@ export function main() {
|
|||||||
ref = {'Accept': values};
|
ref = {'Accept': values};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should be serializable with toJSON',
|
it('should be serializable with toJSON',
|
||||||
() => { expect(JSON.stringify(headers)).toEqual(JSON.stringify(ref)); });
|
() => { expect(JSON.stringify(headers)).toEqual(JSON.stringify(ref)); });
|
||||||
|
|
||||||
@ -143,20 +167,19 @@ export function main() {
|
|||||||
expect(JSON.stringify(parsedHeaders)).toEqual(JSON.stringify(recreatedHeaders));
|
expect(JSON.stringify(parsedHeaders)).toEqual(JSON.stringify(recreatedHeaders));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('.fromResponseHeaderString()', () => {
|
describe('.fromResponseHeaderString()', () => {
|
||||||
|
it('should parse a response header string', () => {
|
||||||
it('should parse a response header string', () => {
|
const response = `Date: Fri, 20 Nov 2015 01:45:26 GMT\n` +
|
||||||
const response = `Date: Fri, 20 Nov 2015 01:45:26 GMT\n` +
|
`Content-Type: application/json; charset=utf-8\n` +
|
||||||
`Content-Type: application/json; charset=utf-8\n` +
|
`Transfer-Encoding: chunked\n` +
|
||||||
`Transfer-Encoding: chunked\n` +
|
`Connection: keep-alive`;
|
||||||
`Connection: keep-alive`;
|
const headers = Headers.fromResponseHeaderString(response);
|
||||||
const headers = Headers.fromResponseHeaderString(response);
|
expect(headers.get('Date')).toEqual('Fri, 20 Nov 2015 01:45:26 GMT');
|
||||||
expect(headers.get('Date')).toEqual('Fri, 20 Nov 2015 01:45:26 GMT');
|
expect(headers.get('Content-Type')).toEqual('application/json; charset=utf-8');
|
||||||
expect(headers.get('Content-Type')).toEqual('application/json; charset=utf-8');
|
expect(headers.get('Transfer-Encoding')).toEqual('chunked');
|
||||||
expect(headers.get('Transfer-Encoding')).toEqual('chunked');
|
expect(headers.get('Connection')).toEqual('keep-alive');
|
||||||
expect(headers.get('Connection')).toEqual('keep-alive');
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user