Compare commits

..

130 Commits
2.0.x ... 2.1.0

Author SHA1 Message Date
07bd4b0630 chore: bump version number and generate CHANGELOG (#12247) 2016-10-12 13:19:31 -07:00
df1718d624 fix(compiler): allow WS as <ng-content> content (#12225) 2016-10-12 07:58:06 -07:00
17e3410d98 Form submit event (#11989)
* feat(forms): ngSubmit event exposes $event from original submit event as local variable

Modify NgForm directive and FormGroup directive to expose the original submit event as $event in the ngSubmit event. Modify docs to reflect changes.

This resolves #10920.

* refactor: code cleanup
2016-10-11 15:49:36 -07:00
5effc330ed feat(upgrade): compilerOptions in bootstrap (#10575) 2016-10-11 15:48:08 -07:00
3df00828d7 docs(router): fix CanActivateChild API docs (#12128)
fixes #12127
2016-10-11 15:47:57 -07:00
8c477b2f45 fix(compiler-cli): don't clone static symbols when simplifying annotation metadata (#12158) 2016-10-11 15:47:44 -07:00
7787771aba refactor(core): cleanup decorators.ts (#12161) 2016-10-11 15:47:20 -07:00
7275e1beb3 refactor(compiler): add optional visit() to TemplateAstVisitor (#12209) 2016-10-11 15:46:11 -07:00
12ba62e5e2 refactor(compiler): expose template parser phases (#12210) 2016-10-11 15:45:27 -07:00
e6e007e2f1 refactor(core): cleanup SpyObject (#12221) 2016-10-11 15:44:48 -07:00
91dd138fa5 docs(animations): document :enter and :leave transition aliases (#12222) 2016-10-11 15:44:38 -07:00
d972d82354 refactor: simplify isPresent(x) ? x : y to x || y (#12166)
Closes #12166
2016-10-10 09:20:58 -07:00
bdcf46f82e refactor(compiler): improve types, misc 2016-10-10 09:20:58 -07:00
79e1c7b807 refactor(upgrade): unify spec code (#12190)
- replace all variable declarations using 'var' by 'const' or 'let'
- replace es5 function declaration by arrow function where applicable
2016-10-10 09:18:33 -07:00
d22eeb70b8 fix(forms): allow optional fields with pattern and minlength validators (#12147) 2016-10-10 09:17:45 -07:00
aa92512ac6 fix(compiler): properly shim :host:before and :host(:before) (#12171)
fixes #12165
2016-10-10 09:15:15 -07:00
f782b08f58 docs: Minor typo fix (#12151) 2016-10-10 09:14:35 -07:00
4202936bbf refactor(compiler): add optional visit() to html AST Visitor (#12135) 2016-10-10 09:13:50 -07:00
e1faca6386 refactor(compiler): template element ast has endSourceSpan (#12138) 2016-10-10 09:12:05 -07:00
f5b0e22d35 docs(public_api): fix missing backtick 2016-10-07 17:23:08 -07:00
00693d70a2 docs: add PUBLIC_API.md 2016-10-07 14:29:16 -07:00
bcef5efffe fix(platform-browser-dynamic): mark platformBrowserDynamic as stable API (#12154)
Everyone building Angular apps need to use this api to bootstrap or AoT compile, so it can't be experimental.
2016-10-07 13:54:06 -07:00
13ecc140e8 fix(compiler): validate @HostBinding name (#12139)
relates to #7500
2016-10-07 13:53:53 -07:00
709a6dea06 refactor(compiler): attribute ast records span of the value (#12132) 2016-10-07 13:53:29 -07:00
16cfb88c00 refactor(compiler): refactor analyzeModules() out of OfflineCompiler (#12137) 2016-10-07 13:52:53 -07:00
efee6f5199 docs(saved-replies): order the replies as shown in github (#12153)
github sorts the replies by reply name.
2016-10-07 13:52:18 -07:00
2aa8aae76d docs(saved-replies): add document with saved replies for our issue tracker 2016-10-07 09:21:20 -07:00
afb4bd9ef6 refactor(NgZone): merge NgZoneImpl and NgZone (#12104) 2016-10-06 15:23:37 -07:00
d641c36a45 fix(compiler): interpolation expressions report the correct offset (#12125) 2016-10-06 15:22:10 -07:00
f4566f8128 fix(http): fix Headers initialization from Headers and Object (#12106) 2016-10-06 15:21:27 -07:00
a67c06708d fix(http): Headers.append should append to the list 2016-10-06 15:21:03 -07:00
d9d57d71dd refactor(http): move one Headers test inside the main describe 2016-10-06 15:21:03 -07:00
e06303a987 fix(router): improve error message (#12102) 2016-10-06 15:19:22 -07:00
40b92ddf21 fix(router): wildcards routes should support lazy loading
Closes #12024
2016-10-06 15:19:09 -07:00
1681e4f57f fix(router): parent resolve should complete before merging resolved data
Closes #12032
2016-10-06 15:19:09 -07:00
71b7654660 fix(compiler-cli): remove peerDependency on @angular/platform-server (#12122)
There is no runtime dependency from the compiler-cli on @angular/platform-server - it was most likely added to package.json by mistake.
2016-10-06 15:16:41 -07:00
eaaec6979c fix(compiler-cli): remove unused parse5 dependency from package.json
This was added in error or is an obsolete dependency. We don't need parse5 for the compiler-cli's runtime.
2016-10-06 15:16:22 -07:00
c587c63591 docs(changelog): fix mentions, backticks and a misplaced line ending (#12109)
* docs(changelog): fix mentions and backticks

`@page` and `@document` were both incorrectly linking to GitHub repositories.
`:enter` and `:leave` in the *Features* section were missing backticks.

* Fix misplaced line ending.
2016-10-06 15:12:41 -07:00
f50c1da4e2 fix(forms): properly validate blank strings with minlength (#12091) 2016-10-06 15:12:09 -07:00
0254ce1f6c refactor(core): simplify Reflector code, add types (#12099) 2016-10-06 15:11:16 -07:00
c9b765f5c0 refactor(compiler): module collector is reusable (#12095) 2016-10-06 15:10:44 -07:00
8c975ed156 refactor(facade): inline StringWrapper (#12051) 2016-10-06 15:10:27 -07:00
bb35fcb562 docs(contributing): remove references to Angular 2
It's just Angular.
2016-10-06 15:07:46 -07:00
57230b70a9 docs(contributing): update the "Submitting an Issue" section 2016-10-06 15:06:37 -07:00
43dc60ce4f docs(contributing): update "got a question or a problem" section
Inspired by: https://github.com/ng-bootstrap/ng-bootstrap#getting-help
2016-10-06 13:58:14 -07:00
230b3b73d8 chore(benchpress): fix the license (#12090)
It's not Apache MIT 2.0, that's a mishmash of Apache 2.0 and MIT
2016-10-06 10:24:01 -07:00
0b7dc2f9ff docs(RouterTestingModule) change modules to imports in example (#12118) 2016-10-06 10:22:39 -07:00
de1f44f51f fix(benchmarks): allow ng2_switch benchmark to be used with AoT. (#12124) 2016-10-06 10:22:08 -07:00
f1cfddf6d6 refactor(benchmarks): add index_aot to support AoT bootstrap. (#12105)
Note: This only make sure it can compile the AoT version, but does not yet use it in e2e tests.
2016-10-06 08:37:37 -07:00
ef621a2f00 docs(changelog): tweak changelog 2016-10-06 06:43:17 -07:00
df9761951b docs(changelog): added changelog for 2.1.0-rc.0 2016-10-05 16:48:22 -07:00
f786c560f1 doc(triage): add info about user pain, frequency and severity labels 2016-10-05 15:58:45 -07:00
c5557de3e7 doc(triage): correct existing incorrect info 2016-10-05 15:58:45 -07:00
ec3a5b54de docs(readme): remove incorrect download count badge
npm doesn't currently support download counts for scoped packages, so there is no replacement right now.
2016-10-05 11:37:28 -07:00
cf269d9ff4 refactor: add license header to JS files & format files (#12081) 2016-10-04 20:39:20 -07:00
5fa5ffb82a refactor(benchmarks): refactor to support AOT bootstrap in G3 (#12075) 2016-10-04 16:27:45 -07:00
4a57dcfd8d fix(forms): properly validate empty strings with patterns (#11450) 2016-10-04 16:14:23 -07:00
43923ffcf5 docs(traige): update triaging doc 2016-10-04 16:13:32 -07:00
50c37d45dc refactor: simplify arrow functions (#12057) 2016-10-04 15:57:37 -07:00
a63359689f fix(ShadowCss): fix attribute selectors in :host and :host-context (#12056)
Fix a regression introduced in #11917 while fixing #6249
2016-10-04 15:40:31 -07:00
43d3a84df3 Revert "refactor: add license header to JS files & format files (#12035)"
This reverts commit 8310c91823.
2016-10-04 14:06:41 -07:00
8310c91823 refactor: add license header to JS files & format files (#12035) 2016-10-04 13:15:49 -07:00
b64b5ece65 refactor(facade): Remove most of StringMapWrapper facade. (#12022)
This change mostly automated by
12012b07a2
with some manual fixes.
2016-10-03 16:46:05 -07:00
ed9c2b6281 fix(Header): preserve case of the first init, set() or append() (#12023)
fixes #11624
2016-10-03 15:27:56 -07:00
1cf5f5fa38 docs(NgModule): Fixed docs for NgModule.entryComponents (#12006)
* docs(NgModule): Corrected the wording of the documentation of `entryComponents`, fixed some minor grammar issues

* docs(NgModule): Remove redundant ComponentFactory mentions

* docs(NgModule): Restore ComponentFactory/ComponentResolver links
2016-10-03 10:19:03 -07:00
a32078f85e docs(DEVELOPER.md): fix typos on "Tests" section (#12029) 2016-10-02 14:19:47 -07:00
decd129a4d refactor(facade): remove DateWrapper (#12027) 2016-10-02 14:12:14 -07:00
c3c9ecb302 text(offline compiler): fix expected output 2016-09-30 17:59:43 -07:00
af520947aa test(AstSerializer): fix serializing void tags 2016-09-30 17:59:43 -07:00
040bf57966 fix(xlif): fix <x> ctype names
fixes #12000
see http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#ctype
2016-09-30 17:59:43 -07:00
65a60b7456 style(I18N): Carriage returns in serialized files 2016-09-30 17:59:43 -07:00
756ef09d12 docs(gh): try to improve the issue template (#11891) 2016-09-30 16:40:56 -07:00
9316f95467 fix(ShadowCss): support @page and @document CSS rules (#11878)
fixes #11860
2016-09-30 16:26:24 -07:00
83d94b7504 fix(ShadowCss): support quoted attribute values
fixes #6085
2016-09-30 14:37:41 -07:00
a121136fae refactor(ShadowCss): add missing types 2016-09-30 14:37:41 -07:00
a6bb84e02b fix(ShadowCss): fix :host(tag) and :host-context(tag)
fixes #11972
2016-09-30 14:37:41 -07:00
3898dc488e fix(BrowserAdapter): correctly removes styles on IE
fixes #7916
2016-09-30 11:18:17 -07:00
ca3f9926f9 refactor(BrowserAdapter): cleanup 2016-09-30 11:18:17 -07:00
1c012a035f refactor(CssSelector): misc cleanup 2016-09-30 11:06:24 -07:00
38c5304b7f docs(CssSelector): [name*=value] is not supported
fixes #6042
2016-09-30 11:06:24 -07:00
9a049be67f feat(Parse5): update to the latest version 2.2.1
fixes #6237
2016-09-30 10:46:49 -07:00
2045c9e8ee docs: update docs for ng2_ftl benchmark 2016-09-30 10:42:21 -07:00
6c4ec05a4a fix(ShadowCss): support [attr="value with space"]
fixes #6249
2016-09-30 10:27:35 -07:00
f7bfda31ff refactor(ShadowCss): cleanup 2016-09-30 10:27:35 -07:00
a92b573309 test(DirectiveResolver): test that a prop can have both @Input and @HostBinding 2016-09-30 10:08:52 -07:00
4fd13d71c8 refactor(DirectiveResolver): cleanup 2016-09-30 10:08:52 -07:00
bf7b82b658 fix(UrlSearchParams): change a behavior when a param value is null or undefined (#11990) 2016-09-30 09:57:26 -07:00
c143fee849 refactor(routerLinkActive): optimised routerLinkActive active check code (#11968)
Modify routerLinkActive to optimise performance by removing unnecessary iteration. By replacing Array.reduce with Array.some, the loop will break when it finds an active link. Useful if used on the parent of a large group of routerLinks. Furthermore, if a RouterLink is active it will not check the RouterLinkWithHrefs.
2016-09-30 09:42:54 -07:00
0286956107 refactor(facade): Inline isBlank called with object-type argument (#11992) 2016-09-30 09:26:53 -07:00
e884f4854d feat(animations): provide aliases for :enter and :leave transitions (#11991) 2016-09-30 09:15:56 -07:00
df1822fc2a benchmarks: add ng2_ftl and ng2_switch_ftl benchmarks (#11963)
These benchmarks take the output of AoT
and manually tweaks it to explore possible
future changes to the compiler to produce
this output directly.
2016-09-30 09:09:31 -07:00
42b4b6d21b fix(upgrade): bind optional properties when upgrading from ng1 (#11411)
Previously, optional properties of a directive/component would be wrongly mapped and thus ignored.

Closes #10181
2016-09-29 09:45:28 -07:00
36bc2ff269 docs(forms): Added FormControl initialization information (#11948) 2016-09-28 13:59:08 -07:00
1564042fe8 fix(ngc): allow ReflectorHost passed as argument to CodeGenerator#create (#11951) 2016-09-27 17:12:57 -07:00
41c8c30973 chore(lint): remove unused imports (#11923)
This was done automatically by tslint, which can now fix issues it finds.
The fixer is still pending in PR https://github.com/palantir/tslint/pull/1568
Also I have a local bugfix for https://github.com/palantir/tslint/issues/1569
which causes too many imports to be deleted.
2016-09-27 17:12:25 -07:00
61129fa12d fix(compiler): move detection of unsafe properties for binding to ElementSchemaRegistry (#11378) 2016-09-27 17:10:02 -07:00
3a5b4882bc fix(compiler): Do not embed templateUrl in view factories in non-debug mode. (#11818)
Fixes #11117.
2016-09-27 17:09:44 -07:00
425c1e6042 refactor: remove dead code 2016-09-27 16:13:09 -07:00
58605cf350 refactor(facade): remove useless facades 2016-09-27 16:13:09 -07:00
34b31dea7c docs(upgrade): rename undeclared Ng2 to Ng2Component (#11950) 2016-09-27 16:11:41 -07:00
a241ab7c07 (docs): removing addProvider from UpgradeAdapter (#11934)
The `addProvider` function in the `UpgradeAdapter` was deprecated in this [commit](d21331e902 (diff-77163e956a7842149f583846c1c01651)) and has been removed in final. Given this, the documentation for downgrading ng2 providers for use in ng1 is invalid.
2016-09-27 10:10:45 -07:00
745e10e6d2 docs(router_config): add missing quote (#11925) 2016-09-27 10:10:12 -07:00
33340dbbd1 docs: remove outdated docs (#11875) 2016-09-24 08:23:28 +09:00
52812c08e2 chore(CHANGELOG): fix wrong issue link (#11871) 2016-09-24 07:13:24 +09:00
52f5ae1961 chore(compiler): followup fix for PR#11846 (#11870)
Original PR set [] to any, but any[], is a tighter type and still
works for SNC enabled consumers of the emit.
2016-09-24 07:13:05 +09:00
9be895b6da docs(ExceptionHandler): fix API docs (#11772)
fixes #11769
2016-09-24 07:05:43 +09:00
9f1c82537e ci(travis): increase node's heap size to prevent OOM on travis (#11869) 2016-09-24 06:04:29 +09:00
5ab5cc77bb Fix(http): invalidStateError if response body without content (#11786)
Fix(http): invalidStateError if response body without content
If the responseType has been specified and other than 'text', responseText throw an InvalidStateError exception

See XHR doc => https://xhr.spec.whatwg.org/#the-responsetext-attribute

Unit Test to prevent invalidStateError
2016-09-24 05:44:01 +09:00
f1b6c6efa1 refactor(animations): ensure animation input/outputs are managed within the template parser (#11782)
Closes #11782
Closes #11601
Related #11707
2016-09-24 05:37:04 +09:00
45ad13560b docs(changelog): remove info about an internal change 2016-09-23 12:02:56 -07:00
2045268cec chore(release): v2.1.0-beta.0 2016-09-23 11:41:35 -07:00
fb1076b44a docs(changelog): release notes for 2.1.0-beta.0 2016-09-23 11:37:28 -07:00
6fc46526ae fix(upgrade): allow attribute selectors for components in ng2 which are not part of upgrade (#11808)
fixes #11280
2016-09-24 02:47:16 +09:00
3ef5ede6d6 chore(compiler): emit ([] as any[]) instead of purely []. (#11846)
In SNC mode `[]` has type of never[], so we cast it to any[] to
typecheck correctly see
https://github.com/Microsoft/TypeScript/issues/10479.

This is temporary workaround, until we fully
migrate the framework to TS 2.0 and strictNullChecks.
2016-09-24 02:21:59 +09:00
136621ebc9 docs(Component): API docs for .encapsulation and .interpolation 2016-09-22 11:01:16 -07:00
f23b22a0f4 refactor: misc cleanup 2016-09-22 11:01:16 -07:00
0ca971c5bd refactor(common): cleanup (#11668) 2016-09-22 10:34:00 -07:00
3a6fcee0e6 docs(core): mark TestBed as stable api and add preliminary docs (#11767)
TestBed was accidentaly ommited from the 'stable' api list during the API sweep before final. We do consider it to be stable.
2016-09-22 10:32:17 -07:00
8972137c29 docs(contributing): remove preview references (#11795) 2016-09-22 10:31:56 -07:00
cc6481077f ci(BrowserStack): add Safari 10 (#11796) 2016-09-22 10:31:38 -07:00
c041b93418 refactor(TemplateParser): clearer error message for on* binding (#11802)
fixes #11756
2016-09-22 10:31:18 -07:00
bc33765913 chore(ISSUE_TEMPLATE): update Angular version field (#11821) 2016-09-22 10:29:12 -07:00
31dce72b7b fix(router): update the router not to reset router state when updating root component (#11799) 2016-09-21 11:37:43 -07:00
212f8dbde7 fix(forms): disable all radios with disable() 2016-09-20 15:00:12 -07:00
44da4984f9 fix(forms): support unbound disabled in ngModel (#11736) 2016-09-20 14:55:47 -07:00
d95344430c chore(zone.js): update to 0.6.25 (#11725) 2016-09-20 14:55:07 -07:00
131626fc61 fix(compiler): Safe property access expressions work in event bindings (#11724) 2016-09-20 14:54:53 -07:00
676bb0fa7d feat(router): update dts files 2016-09-20 14:53:52 -07:00
5a849829c4 feat(router): add router preloader to optimistically preload routes 2016-09-20 14:53:52 -07:00
671f73448c refactor: misc cleanup (#11654) 2016-09-19 17:15:57 -07:00
145 changed files with 2367 additions and 1402 deletions

View File

@ -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>
## [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)
* **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))
* **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 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 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-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>

View File

@ -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:
- [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?
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
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].
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`.
## <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
[submitting an issue](#submit-issue) to our [GitHub Repository][github]. Even better, you can
[submit a Pull Request](#submit-pr) with a fix.
## <a name="feature"></a> Want a Feature?
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
## <a name="feature"></a> Missing a Feature?
You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub
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.
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-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.
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:
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.
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
* **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)
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:
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)
@ -94,7 +101,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
* In GitHub, send a pull request to `angular:master`.
* If we suggest changes then:
* 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):
```shell

62
SAVED_REPLIES.md Normal file
View 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).
```

View File

@ -1,7 +1,7 @@
# Triage Process and Github Labels for Angular 2
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
owner of the component than is in full control of how the issues should
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
even thought no specific source tree is associated with them.
* `comp: documentation`: `@naomiblack`
* `comp: docs`: `@naomiblack`
* `comp: packaging`: `@IgorMinar`
* `comp: performance`: `@tbosch`
* `comp: security`: `@IgorMinar`
@ -53,11 +53,11 @@ What kind of problem is this?
## Caretaker Triage Process
It is the caretaker's responsibility to assign `comp: *` and `type: *`
to each new issue as they come in. The reason why we limit the
responsibility of the caretaker to these two labels is that it is
unlikely that without domain knowledge the caretaker could add any
additional labels of value.
It is the caretaker's responsibility to assign `comp: *` to each new
issue as they come in. The reason why we limit the responsibility of the
caretaker to this one label is that it is likely that without domain
knowledge the caretaker could mislabel issues or lack knowledge of
duplicate issues.
## 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
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
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.
We aim to only have at most three milestones open at a time:

40
docs/PUBLIC_API.md Normal file
View 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.

View File

@ -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.
License: Apache MIT 2.0
License: MIT
# Why?

View File

@ -8,7 +8,7 @@
import {Injectable} from '@angular/core';
import {StringWrapper, isPresent} from '../facade/lang';
import {isPresent} from '../facade/lang';
import {WebDriverAdapter} from '../web_driver_adapter';
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}); }
supports(capabilities: {[key: string]: any}): boolean {
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'firefox');
return capabilities['browserName'].toLowerCase() === 'firefox';
}
}

View File

@ -8,7 +8,7 @@
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 {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
@ -42,7 +42,7 @@ export class IOsDriverExtension extends WebDriverExtension {
var records: any[] = [];
entries.forEach(entry => {
var message = JSON.parse(entry['message'])['message'];
if (StringWrapper.equals(message['method'], 'Timeline.eventRecorded')) {
if (message['method'] === 'Timeline.eventRecorded') {
records.push(message['params']['record']);
}
});
@ -62,19 +62,16 @@ export class IOsDriverExtension extends WebDriverExtension {
var startTime = record['startTime'];
var endTime = record['endTime'];
if (StringWrapper.equals(type, 'FunctionCall') &&
(isBlank(data) || !StringWrapper.equals(data['scriptName'], 'InjectedScript'))) {
if (type === 'FunctionCall' && (isBlank(data) || data['scriptName'] !== 'InjectedScript')) {
events.push(createStartEvent('script', startTime));
endEvent = createEndEvent('script', endTime);
} else if (StringWrapper.equals(type, 'Time')) {
} else if (type === 'Time') {
events.push(createMarkStartEvent(data['message'], startTime));
} else if (StringWrapper.equals(type, 'TimeEnd')) {
} else if (type === 'TimeEnd') {
events.push(createMarkEndEvent(data['message'], startTime));
} else if (
StringWrapper.equals(type, 'RecalculateStyles') || StringWrapper.equals(type, 'Layout') ||
StringWrapper.equals(type, 'UpdateLayerTree') || StringWrapper.equals(type, 'Paint') ||
StringWrapper.equals(type, 'Rasterize') ||
StringWrapper.equals(type, 'CompositeLayers')) {
type === 'RecalculateStyles' || type === 'Layout' || type === 'UpdateLayerTree' ||
type === 'Paint' || type === 'Rasterize' || type === 'CompositeLayers') {
events.push(createStartEvent('render', startTime));
endEvent = createEndEvent('render', endTime);
}
@ -92,7 +89,7 @@ export class IOsDriverExtension extends WebDriverExtension {
perfLogFeatures(): PerfLogFeatures { return new PerfLogFeatures({render: true}); }
supports(capabilities: {[key: string]: any}): boolean {
return StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'safari');
return capabilities['browserName'].toLowerCase() === 'safari';
}
}

View File

@ -212,7 +212,7 @@ function createCountingValidator(
return new MockValidator(log, (completeSample: MeasureValues[]) => {
count--;
if (count === 0) {
return isPresent(validSample) ? validSample : completeSample;
return validSample || completeSample;
} else {
return null;
}

View File

@ -9,7 +9,7 @@
import {AsyncTestCompleter, describe, expect, inject, it} from '@angular/core/testing/testing_internal';
import {Options, ReflectiveInjector, WebDriverExtension} from '../index';
import {StringWrapper, isPresent} from '../src/facade/lang';
import {isPresent} from '../src/facade/lang';
export function main() {
function createExtension(ids: any[], caps: any) {
@ -52,6 +52,6 @@ class MockExtension extends WebDriverExtension {
constructor(public id: string) { super(); }
supports(capabilities: {[key: string]: any}): boolean {
return StringWrapper.equals(capabilities['browser'], this.id);
return capabilities['browser'] === this.id;
}
}

View File

@ -11,7 +11,7 @@ import {Component} from '@angular/core';
import {TestBed, async} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/matchers';
import {Json, StringWrapper} from '../../src/facade/lang';
import {Json} from '../../src/facade/lang';
export function main() {
describe('JsonPipe', () => {
@ -20,7 +20,7 @@ export function main() {
var inceptionObjString: string;
var pipe: JsonPipe;
function normalize(obj: string): string { return StringWrapper.replace(obj, regNewLine, ''); }
function normalize(obj: string): string { return obj.replace(regNewLine, ''); }
beforeEach(() => {
inceptionObj = {dream: {dream: {dream: 'Limbo'}}};

View File

@ -0,0 +1,6 @@
# Overview
This folder will be filled with the benchmark sources
so that we can do offline compilation for them.

View File

@ -15,5 +15,15 @@
"declaration": true,
"lib": ["es6", "dom"],
"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"
]
}

View File

@ -11,13 +11,11 @@
"dependencies": {
"@angular/tsc-wrapped": "^0.3.0",
"reflect-metadata": "^0.1.2",
"parse5": "^2.2.1",
"minimist": "^1.2.0"
},
"peerDependencies": {
"typescript": "^2.0.2",
"@angular/compiler": "0.0.0-PLACEHOLDER",
"@angular/platform-server": "0.0.0-PLACEHOLDER",
"@angular/core": "0.0.0-PLACEHOLDER"
},
"repository": {

View File

@ -21,7 +21,7 @@ import {CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry,
import {Console} from './private_import_core';
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
import {StaticReflector, StaticSymbol} from './static_reflector';
import {StaticReflector, StaticReflectorHost, StaticSymbol} from './static_reflector';
const nodeFs = require('fs');
@ -36,11 +36,26 @@ const PREAMBLE = `/**
`;
export class CodeGenerator {
export class CodeGeneratorModuleCollector {
constructor(
private options: AngularCompilerOptions, private program: ts.Program,
public host: ts.CompilerHost, private staticReflector: StaticReflector,
private compiler: compiler.OfflineCompiler, private reflectorHost: ReflectorHost) {}
private staticReflector: StaticReflector, private reflectorHost: StaticReflectorHost,
private program: ts.Program, private options: AngularCompilerOptions) {}
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 {
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
@ -71,6 +86,18 @@ export class CodeGenerator {
}
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.
private calculateEmitPath(filePath: string): string {
@ -95,18 +122,7 @@ export class CodeGenerator {
}
codegen(): Promise<any> {
// 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[]>[]);
const {fileMetas, ngModules} = this.moduleCollector.getModuleSymbols(this.program);
const analyzedNgModules = this.compiler.analyzeModules(ngModules);
return Promise.all(fileMetas.map(
(fileMeta) =>
@ -185,7 +201,7 @@ export class CodeGenerator {
}
}
interface FileMetadata {
export interface FileMetadata {
fileUrl: string;
components: StaticSymbol[];
ngModules: StaticSymbol[];

View File

@ -44,6 +44,8 @@ export interface StaticReflectorHost {
animationMetadata: string,
provider: string
};
getCanonicalFileName(fileName: string): string;
}
/**
@ -366,6 +368,9 @@ export class StaticReflector implements ReflectorReader {
}
return result;
}
if (expression instanceof StaticSymbol) {
return expression;
}
if (expression) {
if (expression['__symbolic']) {
let staticSymbol: StaticSymbol;

View File

@ -119,6 +119,11 @@ describe('StaticReflector', () => {
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', () => {
expect(simplify(noContext, [1, 2, 3])).toEqual([1, 2, 3]);
});
@ -450,6 +455,9 @@ class MockReflectorHost implements StaticReflectorHost {
provider: 'angular2/src/core/di/provider'
};
}
getCanonicalFileName(fileName: string): string { return fileName; }
getStaticSymbol(declarationFile: string, name: string, members?: string[]): StaticSymbol {
var cacheKey = `${declarationFile}:${name}${members?'.'+members.join('.'):''}`;
var result = this.staticTypeCache.get(cacheKey);

View File

@ -61,7 +61,9 @@ class _AnimationBuilder implements AnimationAstVisitor {
}
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([

View File

@ -115,13 +115,10 @@ function _parseAnimationStateTransition(
stateStyles: {[key: string]: AnimationStylesAst},
errors: AnimationParseError[]): AnimationStateTransitionAst {
var styles = new StylesCollection();
var transitionExprs: any[] /** TODO #9100 */ = [];
var transitionExprs: AnimationStateTransitionExpression[] = [];
var transitionStates = transitionStateMetadata.stateChangeExpr.split(/\s*,\s*/);
transitionStates.forEach(expr => {
_parseAnimationTransitionExpr(expr, errors).forEach(transExpr => {
transitionExprs.push(transExpr);
});
});
transitionStates.forEach(
expr => { transitionExprs.push(..._parseAnimationTransitionExpr(expr, errors)); });
var entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
var animation = _normalizeStyleSteps(entry, stateStyles, errors);
var animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
@ -136,9 +133,25 @@ function _parseAnimationStateTransition(
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(
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]+)$/);
if (!isPresent(match) || match.length < 4) {
errors.push(new AnimationParseError(`the provided ${eventStr} is not of a supported format`));
@ -165,8 +178,8 @@ function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnima
function _normalizeStyleMetadata(
entry: CompileAnimationStyleMetadata, stateStyles: {[key: string]: AnimationStylesAst},
errors: AnimationParseError[]): Array<{[key: string]: string | number}> {
var normalizedStyles: any[] /** TODO #9100 */ = [];
errors: AnimationParseError[]): {[key: string]: string | number}[] {
var normalizedStyles: {[key: string]: string | number}[] = [];
entry.styles.forEach(styleEntry => {
if (isString(styleEntry)) {
ListWrapper.addAll(
@ -338,7 +351,6 @@ function _parseAnimationKeyframes(
ListWrapper.sort(rawKeyframes, (a, b) => a[0] <= b[0] ? -1 : 1);
}
var i: any /** TODO #9100 */;
var firstKeyframe = rawKeyframes[0];
if (firstKeyframe[0] != _INITIAL_KEYFRAME) {
ListWrapper.insert(rawKeyframes, 0, firstKeyframe = [_INITIAL_KEYFRAME, {}]);
@ -353,7 +365,7 @@ function _parseAnimationKeyframes(
}
var lastKeyframeStyles = lastKeyframe[1];
for (i = 1; i <= limit; i++) {
for (let i = 1; i <= limit; i++) {
let entry = rawKeyframes[i];
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 styles = entry[1];

View File

@ -304,7 +304,7 @@ export class CompileTemplateMetadata {
this.styleUrls = _normalizeArray(styleUrls);
this.externalStylesheets = _normalizeArray(externalStylesheets);
this.animations = isPresent(animations) ? ListWrapper.flatten(animations) : [];
this.ngContentSelectors = isPresent(ngContentSelectors) ? ngContentSelectors : [];
this.ngContentSelectors = ngContentSelectors || [];
if (isPresent(interpolation) && interpolation.length != 2) {
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[] {
return isPresent(obj) ? obj : [];
return obj || [];
}
export function isStaticSymbol(value: any): value is StaticSymbol {

View File

@ -9,7 +9,7 @@
import * as chars from '../chars';
import {BaseError} from '../facade/errors';
import {StringWrapper, isPresent} from '../facade/lang';
import {isPresent} from '../facade/lang';
export enum CssTokenType {
EOF,
@ -155,7 +155,7 @@ export class CssScanner {
}
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 {
@ -310,7 +310,7 @@ export class CssScanner {
return this.scanCharacter();
}
return this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`);
return this.error(`Unexpected character [${String.fromCharCode(peek)}]`);
}
scanComment(): CssToken {
@ -476,8 +476,7 @@ export class CssScanner {
var index: number = this.index;
var column: number = this.column;
var line: number = this.line;
errorTokenValue =
isPresent(errorTokenValue) ? errorTokenValue : StringWrapper.fromCharCode(this.peek);
errorTokenValue = errorTokenValue || String.fromCharCode(this.peek);
var invalidToken = new CssToken(index, column, line, CssTokenType.Invalid, errorTokenValue);
var errorMessage =
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 {
return index >= input.length ? chars.$EOF : StringWrapper.charCodeAt(input, index);
return index >= input.length ? chars.$EOF : input.charCodeAt(index);
}
function charStr(code: number): string {
return StringWrapper.fromCharCode(code);
return String.fromCharCode(code);
}
export function isNewline(code: number): boolean {

View File

@ -71,6 +71,13 @@ export class DirectiveResolver {
} else if (a instanceof HostBinding) {
const hostBinding: HostBinding = a;
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;
} else {
host[`[${propName}]`] = propName;

View File

@ -8,7 +8,7 @@
import {Injectable} from '@angular/core';
import * as chars from '../chars';
import {NumberWrapper, StringJoiner, StringWrapper, isPresent} from '../facade/lang';
import {NumberWrapper, StringJoiner, isPresent} from '../facade/lang';
export enum TokenType {
Character,
@ -93,7 +93,7 @@ export class 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 {
@ -133,8 +133,7 @@ class _Scanner {
}
advance() {
this.peek =
++this.index >= this.length ? chars.$EOF : StringWrapper.charCodeAt(this.input, this.index);
this.peek = ++this.index >= this.length ? chars.$EOF : this.input.charCodeAt(this.index);
}
scanToken(): Token {
@ -146,7 +145,7 @@ class _Scanner {
peek = chars.$EOF;
break;
} else {
peek = StringWrapper.charCodeAt(input, index);
peek = input.charCodeAt(index);
}
}
@ -187,16 +186,16 @@ class _Scanner {
case chars.$SLASH:
case chars.$PERCENT:
case chars.$CARET:
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
return this.scanOperator(start, String.fromCharCode(peek));
case chars.$QUESTION:
return this.scanComplexOperator(start, '?', chars.$PERIOD, '.');
case chars.$LT:
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.$EQ:
return this.scanComplexOperator(
start, StringWrapper.fromCharCode(peek), chars.$EQ, '=', chars.$EQ, '=');
start, String.fromCharCode(peek), chars.$EQ, '=', chars.$EQ, '=');
case chars.$AMPERSAND:
return this.scanComplexOperator(start, '&', chars.$AMPERSAND, '&');
case chars.$BAR:
@ -207,7 +206,7 @@ class _Scanner {
}
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 {
@ -310,7 +309,7 @@ class _Scanner {
unescapedCode = unescape(this.peek);
this.advance();
}
buffer.add(StringWrapper.fromCharCode(unescapedCode));
buffer.add(String.fromCharCode(unescapedCode));
marker = this.index;
} else if (this.peek == chars.$EOF) {
return this.error('Unterminated quote', 0);

View File

@ -9,7 +9,7 @@
import {Injectable} from '@angular/core';
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 {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 {
constructor(public strings: string[], public expressions: string[]) {}
constructor(public strings: string[], public expressions: string[], public offsets: number[]) {}
}
export class TemplateBindingParseResult {
@ -41,8 +41,12 @@ export class Parser {
input: string, location: any,
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): ASTWithSource {
this._checkNoInterpolation(input, location, interpolationConfig);
const sourceToLex = 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);
}
@ -79,8 +83,12 @@ export class Parser {
}
this._checkNoInterpolation(input, location, interpolationConfig);
var tokens = this._lexer.tokenize(this._stripComments(input));
return new _ParseAST(input, location, tokens, false, this.errors).parseChain();
const sourceToLex = this._stripComments(input);
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 {
@ -95,7 +103,8 @@ export class Parser {
parseTemplateBindings(input: string, location: any): TemplateBindingParseResult {
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(
@ -107,8 +116,13 @@ export class Parser {
let expressions: AST[] = [];
for (let i = 0; i < split.expressions.length; ++i) {
var tokens = this._lexer.tokenize(this._stripComments(split.expressions[i]));
var ast = new _ParseAST(input, location, tokens, false, this.errors).parseChain();
const expressionText = split.expressions[i];
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);
}
@ -122,20 +136,25 @@ export class Parser {
input: string, location: string,
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): SplitInterpolation {
const regexp = _createInterpolateRegExp(interpolationConfig);
const parts = StringWrapper.split(input, regexp);
const parts = input.split(regexp);
if (parts.length <= 1) {
return null;
}
const strings: string[] = [];
const expressions: string[] = [];
const offsets: number[] = [];
let offset = 0;
for (let i = 0; i < parts.length; i++) {
var part: string = parts[i];
if (i % 2 === 0) {
// fixed string
strings.push(part);
offset += part.length;
} else if (part.trim().length > 0) {
offset += interpolationConfig.start.length;
expressions.push(part);
offsets.push(offset);
offset += part.length + interpolationConfig.end.length;
} else {
this._reportError(
'Blank expressions are not allowed in interpolated strings', input,
@ -143,7 +162,7 @@ export class Parser {
location);
}
}
return new SplitInterpolation(strings, expressions);
return new SplitInterpolation(strings, expressions, offsets);
}
wrapLiteralPrimitive(input: string, location: any): ASTWithSource {
@ -160,8 +179,8 @@ export class Parser {
private _commentStart(input: string): number {
var outerQuote: number = null;
for (let i = 0; i < input.length - 1; i++) {
const char = StringWrapper.charCodeAt(input, i);
const nextChar = StringWrapper.charCodeAt(input, i + 1);
const char = input.charCodeAt(i);
const nextChar = input.charCodeAt(i + 1);
if (char === chars.$SLASH && nextChar == chars.$SLASH && isBlank(outerQuote)) return i;
@ -177,7 +196,7 @@ export class Parser {
private _checkNoInterpolation(
input: string, location: any, interpolationConfig: InterpolationConfig): void {
var regexp = _createInterpolateRegExp(interpolationConfig);
var parts = StringWrapper.split(input, regexp);
var parts = input.split(regexp);
if (parts.length > 1) {
this._reportError(
`Got interpolation (${interpolationConfig.start}${interpolationConfig.end}) where expression was expected`,
@ -208,8 +227,9 @@ export class _ParseAST {
index: number = 0;
constructor(
public input: string, public location: any, public tokens: any[], public parseAction: boolean,
private errors: ParserError[]) {}
public input: string, public location: any, public tokens: Token[],
public inputLength: number, public parseAction: boolean, private errors: ParserError[],
private offset: number) {}
peek(offset: number): Token {
var i = this.index + offset;
@ -219,7 +239,8 @@ export class _ParseAST {
get next(): Token { return this.peek(0); }
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); }
@ -239,7 +260,7 @@ export class _ParseAST {
expectCharacter(code: number) {
if (this.optionalCharacter(code)) return;
this.error(`Missing expected ${StringWrapper.fromCharCode(code)}`);
this.error(`Missing expected ${String.fromCharCode(code)}`);
}
optionalOperator(op: string): boolean {
@ -311,7 +332,7 @@ export class _ParseAST {
while (this.optionalCharacter(chars.$COLON)) {
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('|'));
}

View File

@ -34,7 +34,9 @@ export class ExpansionCase 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); }
}
@ -52,6 +54,10 @@ export class Comment implements Node {
}
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;
visitAttribute(attribute: Attribute, 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[] {
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 => {
const astResult = ast.visit(visitor, context);
const astResult = visit(ast);
if (astResult) {
result.push(astResult);
}

View File

@ -331,12 +331,15 @@ class _TreeBuilder {
const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
let end = attrName.sourceSpan.end;
let value = '';
let valueSpan: ParseSourceSpan;
if (this._peek.type === lex.TokenType.ATTR_VALUE) {
const valueToken = this._advance();
value = valueToken.parts[0];
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 {

View File

@ -163,11 +163,11 @@ class _InjectorBuilder {
if (isPresent(provider.useExisting)) {
result = this._getDependency(new CompileDiDependencyMetadata({token: provider.useExisting}));
} 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));
result = o.importExpr(provider.useFactory).callFn(depsExpr);
} 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));
result =
o.importExpr(provider.useClass).instantiate(depsExpr, o.importType(provider.useClass));

View File

@ -26,7 +26,26 @@ export class SourceModule {
}
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 {
@ -41,17 +60,7 @@ export class OfflineCompiler {
private _localeId: string, private _translationFormat: string) {}
analyzeModules(ngModules: StaticSymbol[]): NgModulesSummary {
const ngModuleByComponent = new Map<StaticSymbol, CompileNgModuleMetadata>();
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);
return analyzeModules(ngModules, this._metadataResolver);
}
clearCache() {

View File

@ -6,7 +6,7 @@
* 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';
@ -368,7 +368,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
var useNewLine = ast.entries.length > 1;
ctx.print(`{`, useNewLine);
ctx.incIndent();
this.visitAllObjects((entry: any /** TODO #9100 */) => {
this.visitAllObjects(entry => {
ctx.print(`${escapeIdentifier(entry[0], this._escapeDollarInStrings, false)}: `);
entry[1].visitExpression(this, ctx);
}, ast.entries, ctx, ',', useNewLine);
@ -381,12 +381,11 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
expressions: o.Expression[], ctx: EmitterVisitorContext, separator: string,
newLine: boolean = false): void {
this.visitAllObjects(
(expr: any /** TODO #9100 */) => expr.visitExpression(this, ctx), expressions, ctx,
separator, newLine);
expr => expr.visitExpression(this, ctx), expressions, ctx, separator, newLine);
}
visitAllObjects(
handler: Function, expressions: any, ctx: EmitterVisitorContext, separator: string,
visitAllObjects<T>(
handler: (t: T) => void, expressions: T[], ctx: EmitterVisitorContext, separator: string,
newLine: boolean = false): void {
for (var i = 0; i < expressions.length; i++) {
if (i > 0) {
@ -409,8 +408,7 @@ export function escapeIdentifier(
if (isBlank(input)) {
return null;
}
var body = StringWrapper.replaceAllMapped(
input, _SINGLE_QUOTE_ESCAPE_STRING_RE, (match: any /** TODO #9100 */) => {
var body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match: string[]) => {
if (match[0] == '$') {
return escapeDollar ? '\\$' : '$';
} else if (match[0] == '\n') {

View File

@ -144,11 +144,11 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
}
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 {
var name: any /** TODO #9100 */;
var name: string;
switch (method) {
case o.BuiltinMethod.ConcatArray:
name = 'concat';

View File

@ -20,7 +20,7 @@ export class JavaScriptEmitter implements OutputEmitter {
var converter = new JsEmitterVisitor(moduleUrl);
var ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
var srcParts: any[] /** TODO #9100 */ = [];
var srcParts: string[] = [];
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
// Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push(

View File

@ -215,7 +215,7 @@ export class ReadVarExpr extends Expression {
export class WriteVarExpr extends Expression {
public value: Expression;
constructor(public name: string, value: Expression, type: Type = null) {
super(isPresent(type) ? type : value.type);
super(type || value.type);
this.value = value;
}
@ -233,7 +233,7 @@ export class WriteKeyExpr extends Expression {
public value: Expression;
constructor(
public receiver: Expression, public index: Expression, value: Expression, type: Type = null) {
super(isPresent(type) ? type : value.type);
super(type || value.type);
this.value = value;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
@ -246,7 +246,7 @@ export class WritePropExpr extends Expression {
public value: Expression;
constructor(
public receiver: Expression, public name: string, value: Expression, type: Type = null) {
super(isPresent(type) ? type : value.type);
super(type || value.type);
this.value = value;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
@ -322,7 +322,7 @@ export class ConditionalExpr extends Expression {
constructor(
public condition: Expression, trueCase: Expression, public falseCase: Expression = null,
type: Type = null) {
super(isPresent(type) ? type : trueCase.type);
super(type || trueCase.type);
this.trueCase = trueCase;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
@ -369,7 +369,7 @@ export class BinaryOperatorExpr extends Expression {
public lhs: Expression;
constructor(
public operator: BinaryOperator, lhs: Expression, public rhs: Expression, type: Type = null) {
super(isPresent(type) ? type : lhs.type);
super(type || lhs.type);
this.lhs = lhs;
}
visitExpression(visitor: ExpressionVisitor, context: any): any {
@ -416,7 +416,7 @@ export class LiteralArrayExpr extends Expression {
export class LiteralMapExpr extends Expression {
public valueType: Type = null;
constructor(public entries: Array<Array<string|Expression>>, type: MapType = null) {
constructor(public entries: [string, Expression][], type: MapType = null) {
super(type);
if (isPresent(type)) {
this.valueType = type.valueType;
@ -479,7 +479,7 @@ export class DeclareVarStmt extends Statement {
public name: string, public value: Expression, type: Type = null,
modifiers: StmtModifier[] = null) {
super(modifiers);
this.type = isPresent(type) ? type : value.type;
this.type = type || value.type;
}
visitStatement(visitor: StatementVisitor, context: any): any {
@ -625,7 +625,7 @@ export class ExpressionTransformer implements StatementVisitor, ExpressionVisito
expr.value.visitExpression(this, context));
}
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any {
var method = isPresent(ast.builtin) ? ast.builtin : ast.name;
var method = ast.builtin || ast.name;
return new InvokeMethodExpr(
ast.receiver.visitExpression(this, context), method,
this.visitAllExpressions(ast.args, context), ast.type);
@ -673,9 +673,11 @@ export class ExpressionTransformer implements StatementVisitor, ExpressionVisito
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any {
return new LiteralArrayExpr(this.visitAllExpressions(ast.entries, context));
}
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
return new LiteralMapExpr(ast.entries.map(
(entry) => [entry[0], (<Expression>entry[1]).visitExpression(this, context)]));
const entries = ast.entries.map(
(entry): [string, Expression] => [entry[0], entry[1].visitExpression(this, context), ]);
return new LiteralMapExpr(entries);
}
visitAllExpressions(exprs: Expression[], context: any): Expression[] {
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);
}
export function literalMap(
values: Array<Array<string|Expression>>, type: MapType = null): LiteralMapExpr {
export function literalMap(values: [string, Expression][], type: MapType = null): LiteralMapExpr {
return new LiteralMapExpr(values, type);
}

View File

@ -26,9 +26,9 @@ class JitEmitterVisitor extends AbstractJsEmitterVisitor {
private _evalArgValues: any[] = [];
getArgs(): {[key: string]: any} {
var result = {};
var result: {[key: string]: any} = {};
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;
}

View File

@ -46,7 +46,7 @@ export class TypeScriptEmitter implements OutputEmitter {
var converter = new _TsEmitterVisitor(moduleUrl);
var ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
var srcParts: any[] /** TODO #9100 */ = [];
var srcParts: string[] = [];
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
// Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push(
@ -75,6 +75,22 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
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 {
this._visitIdentifier(ast.value, ast.typeParams, ctx);
return null;
@ -227,7 +243,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
}
visitBuiltintType(type: o.BuiltinType, ctx: EmitterVisitorContext): any {
var typeStr: any /** TODO #9100 */;
var typeStr: string;
switch (type.name) {
case o.BuiltinTypeName.Bool:
typeStr = 'boolean';
@ -291,7 +307,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
}
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
this.visitAllObjects((param: any /** TODO #9100 */) => {
this.visitAllObjects(param => {
ctx.print(param.name);
ctx.print(':');
this.visitType(param.type, ctx);
@ -320,8 +336,7 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
}
if (isPresent(typeParams) && typeParams.length > 0) {
ctx.print(`<`);
this.visitAllObjects(
(type: any /** TODO #9100 */) => type.visitType(this, ctx), typeParams, ctx, ',');
this.visitAllObjects(type => type.visitType(this, ctx), typeParams, ctx, ',');
ctx.print(`>`);
}
}

View File

@ -22,7 +22,7 @@ class _ValueOutputAstTransformer implements ValueTransformer {
}
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)]); });
return o.literalMap(entries, type);
}

View File

@ -102,7 +102,7 @@ export class ProviderElementContext {
private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: Map<any, boolean>) {
this._getQueriesFor(token).forEach((query) => {
const queryReadToken = isPresent(query.read) ? query.read : token;
const queryReadToken = query.read || token;
if (isBlank(queryReadTokens.get(queryReadToken.reference))) {
queryReadTokens.set(queryReadToken.reference, true);
}
@ -169,11 +169,11 @@ export class ProviderElementContext {
transformedUseValue = existingDiDep.value;
}
} else if (isPresent(provider.useFactory)) {
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
var deps = provider.deps || provider.useFactory.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
} else if (isPresent(provider.useClass)) {
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
var deps = provider.deps || provider.useClass.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
}
@ -338,11 +338,11 @@ export class NgModuleProviderAnalyzer {
transformedUseValue = existingDiDep.value;
}
} else if (isPresent(provider.useFactory)) {
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
var deps = provider.deps || provider.useFactory.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
} else if (isPresent(provider.useClass)) {
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
var deps = provider.deps || provider.useClass.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
}

View File

@ -381,7 +381,10 @@ export class ShadowCss {
if (_polyfillHostRe.test(selector)) {
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 + ' ');
}

View File

@ -9,7 +9,7 @@
// Some of the code comes from WebComponents.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';
@ -30,8 +30,8 @@ export function isStyleUrlResolvable(url: string): boolean {
export function extractStyleUrls(
resolver: UrlResolver, baseUrl: string, cssText: string): StyleWithImports {
var foundUrls: string[] = [];
var modifiedCssText = StringWrapper.replaceAllMapped(cssText, _cssImportRe, (m: string[]) => {
var url = isPresent(m[1]) ? m[1] : m[2];
var modifiedCssText = cssText.replace(_cssImportRe, function(...m: string[]) {
const url = m[1] || m[2];
if (!isStyleUrlResolvable(url)) {
// Do not attempt to resolve non-package absolute URLs with URI scheme
return m[0];

View File

@ -10,7 +10,6 @@ import {SecurityContext} from '@angular/core';
import {CompileDirectiveMetadata, CompileProviderMetadata, CompileTokenMetadata} from '../compile_metadata';
import {AST} from '../expression_parser/ast';
import {isPresent} from '../facade/lang';
import {ParseSourceSpan} from '../parse_util';
import {LifecycleHooks} from '../private_import_core';
@ -84,7 +83,7 @@ export class BoundEventAst implements TemplateAst {
return visitor.visitEvent(this, context);
}
get fullName() {
if (isPresent(this.target)) {
if (this.target) {
return `${this.target}:${this.name}`;
} else {
return this.name;
@ -124,7 +123,8 @@ export class ElementAst implements TemplateAst {
public outputs: BoundEventAst[], public references: ReferenceAst[],
public directives: DirectiveAst[], public providers: ProviderAst[],
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 {
return visitor.visitElement(this, context);
@ -241,6 +241,11 @@ export enum PropertyBindingType {
* A visitor for {@link TemplateAst} trees that will process each node.
*/
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;
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any;
visitElement(ast: ElementAst, context: any): any;
@ -260,10 +265,13 @@ export interface TemplateAstVisitor {
*/
export function templateVisitAll(
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 => {
var astResult = ast.visit(visitor, context);
if (isPresent(astResult)) {
const astResult = visit(ast);
if (astResult) {
result.push(astResult);
}
});

View File

@ -118,22 +118,18 @@ export class TemplateParser {
component: CompileDirectiveMetadata, template: string, directives: CompileDirectiveMetadata[],
pipes: CompilePipeMetadata[], schemas: SchemaMetadata[],
templateUrl: string): TemplateParseResult {
let interpolationConfig: any;
if (component.template) {
interpolationConfig = InterpolationConfig.fromArray(component.template.interpolation);
}
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);
return this.tryParseHtml(
this.expandHtml(this._htmlParser.parse(
template, templateUrl, true, this.getInterpolationConfig(component))),
component, template, directives, pipes, schemas, templateUrl);
}
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) {
const uniqDirectives = removeIdentifierDuplicates(directives);
const uniqPipes = removeIdentifierDuplicates(pipes);
@ -147,7 +143,6 @@ export class TemplateParser {
} else {
result = [];
}
this._assertNoReferenceDuplicationOnTemplate(result, errors);
if (errors.length > 0) {
@ -162,6 +157,24 @@ export class TemplateParser {
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 */
_assertNoReferenceDuplicationOnTemplate(result: TemplateAst[], errors: TemplateParseError[]):
void {
@ -415,10 +428,8 @@ class TemplateParseVisitor implements html.Visitor {
let parsedElement: TemplateAst;
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
if (isPresent(element.children) && element.children.length > 0) {
this._reportError(
`<ng-content> element cannot have content. <ng-content> must be immediately followed by </ng-content>`,
element.sourceSpan);
if (element.children && !element.children.every(_isEmptyTextNode)) {
this._reportError(`<ng-content> element cannot have content.`, element.sourceSpan);
}
parsedElement = new NgContentAst(
@ -442,7 +453,7 @@ class TemplateParseVisitor implements html.Visitor {
nodeName, attrs, elementProps, events, references,
providerContext.transformedDirectiveAsts, providerContext.transformProviders,
providerContext.transformedHasViewContainer, children,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan, element.endSourceSpan);
this._findComponentDirectives(directiveAsts)
.forEach(
@ -1083,7 +1094,7 @@ class NonBindableVisitor implements html.Visitor {
const children = html.visitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT);
return new ElementAst(
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; }
@ -1188,3 +1199,7 @@ export class PipeCollector extends RecursiveAstVisitor {
function _isAnimationLabel(name: string): boolean {
return name[0] == '@';
}
function _isEmptyTextNode(node: html.Node): boolean {
return node instanceof html.Text && node.value.trim().length == 0;
}

View File

@ -8,7 +8,7 @@
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:';
@ -74,8 +74,8 @@ export class UrlResolver {
var pathSegements = path.split(/\//);
resolvedUrl = `asset:${pathSegements[0]}/lib/${pathSegements.slice(1).join('/')}`;
} else {
prefix = StringWrapper.stripRight(prefix, '/');
path = StringWrapper.stripLeft(path, '/');
prefix = prefix.replace(/\/+$/, '');
path = path.replace(/^\/+/, '');
return `${prefix}/${path}`;
}
}

View File

@ -7,7 +7,7 @@
*/
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';
export const MODULE_SUFFIX = '';
@ -15,8 +15,7 @@ export const MODULE_SUFFIX = '';
var CAMEL_CASE_REGEXP = /([A-Z])/g;
export function camelCaseToDashCase(input: string): string {
return StringWrapper.replaceAllMapped(
input, CAMEL_CASE_REGEXP, (m: string[]) => '-' + m[1].toLowerCase());
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
}
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 {
return StringWrapper.replaceAll(name, /\W/g, '_');
return name.replace(/\W/g, '_');
}
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));
}
visitStringMap(map: {[key: string]: any}, context: any): any {
var result = {};
Object.keys(map).forEach(
key => { (result as any /** TODO #9100 */)[key] = visitValue(map[key], this, context); });
var result: {[key: string]: any} = {};
Object.keys(map).forEach(key => { result[key] = visitValue(map[key], this, context); });
return result;
}
visitPrimitive(value: any, context: any): any { return value; }

View File

@ -161,11 +161,11 @@ export class CompileElement extends CompileNode {
resolvedProvider.providerType,
new CompileDiDependencyMetadata({token: provider.useExisting}));
} 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));
return o.importExpr(provider.useFactory).callFn(depsExpr);
} 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));
return o.importExpr(provider.useClass)
.instantiate(depsExpr, o.importType(provider.useClass));
@ -435,6 +435,6 @@ function createProviderProperty(
class _QueryWithRead {
public read: CompileTokenMetadata;
constructor(public query: CompileQuery, match: CompileTokenMetadata) {
this.read = isPresent(query.meta.read) ? query.meta.read : match;
this.read = query.meta.read || match;
}
}

View File

@ -59,7 +59,7 @@ export class CompileMethod {
resetDebugInfoExpr(nodeIndex: number, templateAst: TemplateAst): o.Expression {
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) {

View File

@ -64,7 +64,7 @@ export class CompileQuery {
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 updateStmts = [this.queryList.callMethod('reset', [o.literalArr(values)]).toStmt()];
if (isPresent(this.ownerDirectiveExpression)) {

View File

@ -169,16 +169,16 @@ export class CompileView implements NameResolver {
return proxyExpr.callFn(values);
}
createLiteralMap(entries: Array<Array<string|o.Expression>>): o.Expression {
createLiteralMap(entries: [string, o.Expression][]): o.Expression {
if (entries.length === 0) {
return o.importExpr(resolveIdentifier(Identifiers.EMPTY_MAP));
}
var proxyExpr = o.THIS_EXPR.prop(`_map_${this.literalMapCount++}`);
var proxyParams: o.FnParam[] = [];
var proxyReturnEntries: Array<Array<string|o.Expression>> = [];
var values: o.Expression[] = [];
const proxyExpr = o.THIS_EXPR.prop(`_map_${this.literalMapCount++}`);
const proxyParams: o.FnParam[] = [];
const proxyReturnEntries: [string, o.Expression][] = [];
const values: o.Expression[] = [];
for (var i = 0; i < entries.length; i++) {
var paramName = `p${i}`;
const paramName = `p${i}`;
proxyParams.push(new o.FnParam(paramName));
proxyReturnEntries.push([entries[i][0], o.variable(paramName)]);
values.push(<o.Expression>entries[i][1]);

View File

@ -7,7 +7,7 @@
*/
import {CompileDirectiveMetadata} from '../compile_metadata';
import {StringWrapper, isPresent} from '../facade/lang';
import {isPresent} from '../facade/lang';
import {identifierToken} from '../identifiers';
import * as o from '../output/output_ast';
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
@ -46,7 +46,7 @@ export class CompileEventListener {
public eventPhase: string, listenerIndex: number) {
this._method = new CompileMethod(compileElement.view);
this._methodName =
`_handle_${santitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`;
`_handle_${sanitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`;
this._eventParam = new o.FnParam(
EventHandlerVars.event.name,
o.importType(this.compileElement.view.genConfig.renderTypes.renderEvent));
@ -59,8 +59,7 @@ export class CompileEventListener {
this._hasComponentHostListener = true;
}
this._method.resetDebugInfo(this.compileElement.nodeIndex, hostEvent);
var context = isPresent(directiveInstance) ? directiveInstance :
this.compileElement.view.componentContext;
var context = directiveInstance || this.compileElement.view.componentContext;
var actionStmts = convertCdStatementToIr(
this.compileElement.view, context, hostEvent.handler, this.compileElement.nodeIndex);
var lastIndex = actionStmts.length - 1;
@ -96,7 +95,7 @@ export class CompileEventListener {
}
listenToRenderer() {
var listenExpr: any /** TODO #9100 */;
var listenExpr: o.Expression;
var eventListener = o.THIS_EXPR.callMethod(
'eventHandler',
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]);
@ -148,13 +147,15 @@ export class CompileEventListener {
export function collectEventListeners(
hostEvents: BoundEventAst[], dirs: DirectiveAst[],
compileElement: CompileElement): CompileEventListener[] {
var eventListeners: CompileEventListener[] = [];
const eventListeners: CompileEventListener[] = [];
hostEvents.forEach((hostEvent) => {
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
var listener = CompileEventListener.getOrCreate(
compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
listener.addAction(hostEvent, null, null);
});
dirs.forEach((directiveAst) => {
var directiveInstance =
compileElement.instances.get(identifierToken(directiveAst.directive.type).reference);
@ -165,6 +166,7 @@ export function collectEventListeners(
listener.addAction(hostEvent, directiveAst.directive, directiveInstance);
});
});
eventListeners.forEach((listener) => listener.finishMethod());
return eventListeners;
}
@ -174,6 +176,7 @@ export function bindDirectiveOutputs(
eventListeners: CompileEventListener[]) {
Object.keys(directiveAst.directive.outputs).forEach(observablePropName => {
const eventName = directiveAst.directive.outputs[observablePropName];
eventListeners.filter(listener => listener.eventName == eventName).forEach((listener) => {
listener.listenToDirective(directiveInstance, observablePropName);
});
@ -199,6 +202,6 @@ function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
return null;
}
function santitizeEventName(name: string): string {
return StringWrapper.replaceAll(name, /[^a-zA-Z_]/g, '_');
function sanitizeEventName(name: string): string {
return name.replace(/[^a-zA-Z_]/g, '_');
}

View File

@ -99,10 +99,9 @@ function bindAndWriteToRenderer(
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
var fieldExpr = createBindFieldExpr(bindingIndex);
var currValExpr = createCurrValueExpr(bindingIndex);
var renderMethod: string;
var oldRenderValue: o.Expression = sanitizedValue(boundProp, fieldExpr);
var renderValue: o.Expression = sanitizedValue(boundProp, currValExpr);
var updateStmts: any[] /** TODO #9100 */ = [];
var updateStmts: o.Statement[] = [];
var compileMethod = view.detectChangesRenderPropertiesMethod;
switch (boundProp.type) {
case PropertyBindingType.Property:

View File

@ -57,7 +57,7 @@ export function getViewFactoryName(
}
export function createFlatArray(expressions: o.Expression[]): o.Expression {
var lastNonArrayExpressions: any[] /** TODO #9100 */ = [];
var lastNonArrayExpressions: o.Expression[] = [];
var result: o.Expression = o.literalArr([]);
for (var i = 0; i < expressions.length; i++) {
var expr = expressions[i];

View File

@ -10,7 +10,7 @@ import {ViewEncapsulation} from '@angular/core';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
import {ListWrapper} from '../facade/collection';
import {StringWrapper, isPresent} from '../facade/lang';
import {isPresent} from '../facade/lang';
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {ChangeDetectorStatus, ViewType, isDefaultChangeDetectionStrategy} from '../private_import_core';
@ -79,10 +79,10 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
if (this._isRootNode(parent)) {
// store appElement as root node only for ViewContainers
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)) {
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]]); });
// We need to sort to get a defined output order
// for tests and for caching generated artifacts...
ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
ListWrapper.sort(entryArray);
return entryArray;
}
@ -511,10 +511,13 @@ function createViewFactory(
templateUrlInfo = view.component.template.templateUrl;
}
if (view.viewIndex === 0) {
var animationsExpr = o.literalMap(view.animations.map(entry => [entry.name, entry.fnExp]));
initRenderCompTypeStmts = [new o.IfStmt(
var animationsExpr = o.literalMap(
view.animations.map((entry): [string, o.Expression] => [entry.name, entry.fnExp]));
initRenderCompTypeStmts = [
new o.IfStmt(
renderCompTypeVar.identical(o.NULL_EXPR),
[renderCompTypeVar
[
renderCompTypeVar
.set(ViewConstructorVars.viewUtils.callMethod(
'createRenderComponentType',
[
@ -524,13 +527,16 @@ function createViewFactory(
view.styles,
animationsExpr,
]))
.toStmt()])];
.toStmt(),
]),
];
}
return o
.fn(viewFactoryArgs, initRenderCompTypeStmts.concat([new o.ReturnStatement(
o.variable(viewClass.name)
.fn(viewFactoryArgs, initRenderCompTypeStmts.concat([
new o.ReturnStatement(o.variable(viewClass.name)
.instantiate(viewClass.constructorMethod.params.map(
(param) => o.variable(param.name))))]),
(param) => o.variable(param.name)))),
]),
o.importType(resolveIdentifier(Identifiers.AppView), [getContextType(view)]))
.toDeclStmt(view.viewFactory.name, [o.StmtModifier.Final]);
}

View File

@ -15,7 +15,7 @@ import {CompileMetadataResolver} from '../../src/metadata_resolver';
export function main() {
describe('RuntimeAnimationCompiler', () => {
var resolver: any /** TODO #9100 */;
var resolver: CompileMetadataResolver;
beforeEach(
inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; }));

View File

@ -28,9 +28,9 @@ export function main() {
(keyframe: AnimationKeyframeAst): {[key: string]: string | number} =>
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 styles: any[] /** TODO #9100 */ = [];
var styles: {[key: string]: string | number}[] = [];
if (step.startingStyles.styles.length > 0) {
styles.push(combineStyles(step.startingStyles));
}
@ -38,7 +38,7 @@ export function main() {
return styles;
};
var resolver: any /** TODO #9100 */;
var resolver: CompileMetadataResolver;
beforeEach(
inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; }));

View File

@ -275,7 +275,7 @@ export function main() {
it('should throw an error if a selector is being parsed while in the wrong mode', () => {
var cssCode = '.class > tag';
var capturedMessage: any /** TODO #9100 */;
var capturedMessage: string;
try {
tokenize(cssCode, false, CssLexerMode.STYLE_BLOCK);
} catch (e) {
@ -298,7 +298,7 @@ export function main() {
describe('Attribute Mode', () => {
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\'';
return tokenize(cssCode, false, CssLexerMode.ATTRIBUTE_SELECTOR);
}

View File

@ -26,7 +26,7 @@ class MyVisitor implements CssAstVisitor {
* @internal
*/
_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]);
}

View File

@ -7,8 +7,8 @@
*/
import {hasLifecycleHook} from '@angular/compiler/src/lifecycle_reflector';
import {LifecycleHooks} from '@angular/core/src/metadata/lifecycle_hooks';
import {describe, expect, it} from '@angular/core/testing/testing_internal';
import {SimpleChanges} from '@angular/core';
import {LifecycleHooks as Hooks} from '@angular/core/src/metadata/lifecycle_hooks';
export function main() {
describe('Create Directive', () => {
@ -16,92 +16,81 @@ export function main() {
describe('ngOnChanges', () => {
it('should be true when the directive has the ngOnChanges method', () => {
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveWithOnChangesMethod))
.toBe(true);
expect(hasLifecycleHook(Hooks.OnChanges, DirectiveWithOnChangesMethod)).toBe(true);
});
it('should be false otherwise', () => {
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveNoHooks)).toBe(false);
});
it('should be false otherwise',
() => { expect(hasLifecycleHook(Hooks.OnChanges, DirectiveNoHooks)).toBe(false); });
});
describe('ngOnDestroy', () => {
it('should be true when the directive has the ngOnDestroy method', () => {
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveWithOnDestroyMethod))
.toBe(true);
expect(hasLifecycleHook(Hooks.OnDestroy, DirectiveWithOnDestroyMethod)).toBe(true);
});
it('should be false otherwise', () => {
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveNoHooks)).toBe(false);
});
it('should be false otherwise',
() => { expect(hasLifecycleHook(Hooks.OnDestroy, DirectiveNoHooks)).toBe(false); });
});
describe('ngOnInit', () => {
it('should be true when the directive has the ngOnInit method', () => {
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveWithOnInitMethod)).toBe(true);
});
it('should be true when the directive has the ngOnInit method',
() => { expect(hasLifecycleHook(Hooks.OnInit, DirectiveWithOnInitMethod)).toBe(true); });
it('should be false otherwise', () => {
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveNoHooks)).toBe(false);
});
it('should be false otherwise',
() => { expect(hasLifecycleHook(Hooks.OnInit, DirectiveNoHooks)).toBe(false); });
});
describe('ngDoCheck', () => {
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', () => {
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveNoHooks)).toBe(false);
});
it('should be false otherwise',
() => { expect(hasLifecycleHook(Hooks.DoCheck, DirectiveNoHooks)).toBe(false); });
});
describe('ngAfterContentInit', () => {
it('should be true when the directive has the ngAfterContentInit method', () => {
expect(hasLifecycleHook(
LifecycleHooks.AfterContentInit, DirectiveWithAfterContentInitMethod))
expect(hasLifecycleHook(Hooks.AfterContentInit, DirectiveWithAfterContentInitMethod))
.toBe(true);
});
it('should be false otherwise', () => {
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit, DirectiveNoHooks)).toBe(false);
expect(hasLifecycleHook(Hooks.AfterContentInit, DirectiveNoHooks)).toBe(false);
});
});
describe('ngAfterContentChecked', () => {
it('should be true when the directive has the ngAfterContentChecked method', () => {
expect(hasLifecycleHook(
LifecycleHooks.AfterContentChecked, DirectiveWithAfterContentCheckedMethod))
expect(
hasLifecycleHook(Hooks.AfterContentChecked, DirectiveWithAfterContentCheckedMethod))
.toBe(true);
});
it('should be false otherwise', () => {
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked, DirectiveNoHooks))
.toBe(false);
expect(hasLifecycleHook(Hooks.AfterContentChecked, DirectiveNoHooks)).toBe(false);
});
});
describe('ngAfterViewInit', () => {
it('should be true when the directive has the ngAfterViewInit method', () => {
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveWithAfterViewInitMethod))
expect(hasLifecycleHook(Hooks.AfterViewInit, DirectiveWithAfterViewInitMethod))
.toBe(true);
});
it('should be false otherwise', () => {
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveNoHooks)).toBe(false);
});
it('should be false otherwise',
() => { expect(hasLifecycleHook(Hooks.AfterViewInit, DirectiveNoHooks)).toBe(false); });
});
describe('ngAfterViewChecked', () => {
it('should be true when the directive has the ngAfterViewChecked method', () => {
expect(hasLifecycleHook(
LifecycleHooks.AfterViewChecked, DirectiveWithAfterViewCheckedMethod))
expect(hasLifecycleHook(Hooks.AfterViewChecked, DirectiveWithAfterViewCheckedMethod))
.toBe(true);
});
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 DirectiveWithOnChangesMethod {
ngOnChanges(_: any /** TODO #9100 */) {}
ngOnChanges(_: SimpleChanges) {}
}
class DirectiveWithOnInitMethod {

View File

@ -435,7 +435,7 @@ export function main() {
}
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];
if (result) {
return Promise.resolve(result);

View File

@ -117,6 +117,18 @@ class SomeDirectiveWithSameHostBindingAndInput {
@Input() @HostBinding() prop: any;
}
@Directive({selector: 'someDirective'})
class SomeDirectiveWithMalformedHostBinding1 {
@HostBinding('(a)')
onA() {}
}
@Directive({selector: 'someDirective'})
class SomeDirectiveWithMalformedHostBinding2 {
@HostBinding('[a]')
onA() {}
}
class SomeDirectiveWithoutMetadata {}
export function main() {
@ -210,6 +222,17 @@ export function main() {
expect(directiveMetadata.host)
.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', () => {

View File

@ -8,7 +8,7 @@
import {ASTWithSource, BindingPipe, Interpolation, ParserError, TemplateBinding} from '@angular/compiler/src/expression_parser/ast';
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 {isBlank, isPresent} from '../../src/facade/lang';
@ -39,6 +39,10 @@ export function main() {
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 {
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',
() => 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]);
});
});
});
}

View File

@ -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 {StringWrapper, isString} from '../../src/facade/lang';
import {isString} from '../../src/facade/lang';
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../src/ml_parser/interpolation_config';
class Unparser implements AstVisitor {
@ -134,7 +134,7 @@ class Unparser implements AstVisitor {
visitLiteralPrimitive(ast: LiteralPrimitive, context: any) {
if (isString(ast.value)) {
this._expression += `"${StringWrapper.replaceAll(ast.value, Unparser._quoteRegExp, '\"')}"`;
this._expression += `"${ast.value.replace( Unparser._quoteRegExp, '\"')}"`;
} else {
this._expression += `${ast.value}`;
}

View File

@ -376,6 +376,82 @@ export function main() {
[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', () => {

View File

@ -43,7 +43,7 @@ export function main() {
describe('output emitter', () => {
outputDefs.forEach((outputDef) => {
describe(`${outputDef['name']}`, () => {
var expressions: any /** TODO #9100 */;
var expressions: {[k: string]: any};
beforeEach(() => { expressions = outputDef['getExpressions']()(); });
it('should support literals', () => {
@ -109,13 +109,16 @@ export function main() {
});
describe('operators', () => {
var ops: any /** TODO #9100 */;
var aObj: any /** TODO #9100 */, bObj: any /** TODO #9100 */;
var ops: {[k: string]: Function};
var aObj: any;
var bObj: any;
beforeEach(() => {
ops = expressions['operators'];
aObj = new Object();
bObj = new Object();
aObj = {};
bObj = {};
});
it('should support ==', () => {
expect(ops['=='](aObj, aObj)).toBe(true);
expect(ops['=='](aObj, bObj)).toBe(false);

View File

@ -53,7 +53,7 @@ export function main() {
it('should return an error from the definitions',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
var url = '/foo';
var response: any /** TODO #9100 */ = null;
var response: string = null;
resourceLoader.when(url, response);
expectResponse(resourceLoader.get(url), url, response, () => async.done());
resourceLoader.flush();
@ -71,7 +71,7 @@ export function main() {
it('should return an error from the expectations',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
var url = '/foo';
var response: any /** TODO #9100 */ = null;
var response: string = null;
resourceLoader.expect(url, response);
expectResponse(resourceLoader.get(url), url, response, () => async.done());
resourceLoader.flush();

View File

@ -117,7 +117,7 @@ export function main() {
class SomeModule {
}
resourceLoader.spy('get').andCallFake(() => Promise.resolve('hello'));
resourceLoader.spy('get').and.callFake(() => Promise.resolve('hello'));
let ngModuleFactory: NgModuleFactory<any>;
compiler.compileModuleAsync(SomeModule).then((f) => ngModuleFactory = f);
tick();
@ -132,7 +132,7 @@ export function main() {
class SomeModule {
}
resourceLoader.spy('get').andCallFake(() => Promise.resolve(''));
resourceLoader.spy('get').and.callFake(() => Promise.resolve(''));
expect(() => compiler.compileModuleSync(SomeModule))
.toThrowError(
`Can't compile synchronously as ${stringify(SomeCompWithUrlTemplate)} is still being loaded!`);
@ -144,7 +144,7 @@ export function main() {
class SomeModule {
}
resourceLoader.spy('get').andCallFake(() => Promise.resolve(''));
resourceLoader.spy('get').and.callFake(() => Promise.resolve(''));
dirResolver.setView(SomeComp, new ViewMetadata({template: ''}));
dirResolver.setView(ChildComp, new ViewMetadata({templateUrl: '/someTpl.html'}));
expect(() => compiler.compileModuleSync(SomeModule))
@ -161,7 +161,7 @@ export function main() {
class SomeModule {
}
resourceLoader.spy('get').andCallFake(() => Promise.resolve('hello'));
resourceLoader.spy('get').and.callFake(() => Promise.resolve('hello'));
compiler.compileModuleAsync(SomeModule);
tick();

View File

@ -112,10 +112,8 @@ export function main() {
it('should handle no context',
() => { expect(s(':host {}', 'a', 'a-host')).toEqual('[a-host] {}'); });
it('should handle tag selector', () => {
expect(s(':host(ul) {}', 'a', 'a-host')).toEqual('ul[a-host] {}');
});
it('should handle tag selector',
() => { expect(s(':host(ul) {}', 'a', 'a-host')).toEqual('ul[a-host] {}'); });
it('should handle class selector',
() => { 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'))
.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'), () => {

View File

@ -11,7 +11,7 @@ import {UrlResolver} from '@angular/compiler/src/url_resolver';
export function main() {
describe('extractStyleUrls', () => {
var urlResolver: any /** TODO #9100 */;
var urlResolver: UrlResolver;
beforeEach(() => { urlResolver = new UrlResolver(); });

View File

@ -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', () => {
beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
@ -164,12 +274,17 @@ export function main() {
});
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]]);
});
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([
[ElementAst, ':svg:svg'],
[NgContentAst],
@ -1146,7 +1261,7 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
});
describe('content projection', () => {
var compCounter: any /** TODO #9100 */;
var compCounter: number;
beforeEach(() => { compCounter = 0; });
function createComp(
@ -1308,10 +1423,11 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
});
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>', []))
.toThrowError(`Template parse errors:
<ng-content> element cannot have content. <ng-content> must be immediately followed by </ng-content> ("[ERROR ->]<ng-content>content</ng-content>"): TestComp@0:0`);
.toThrowError(
`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',
@ -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', () => {
@ -1819,14 +1964,10 @@ class TemplateContentProjectionHumanizer implements TemplateAstVisitor {
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'; }
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { throw 'not implemented'; }
visitElement(ast: ElementAst, context: any): any {
if (ast.name != 'div') return ast;
return new ElementAst(
'foo', [], [], [], [], [], [], false, [], ast.ngContentIndex, ast.sourceSpan);
}
visitElement(ast: ElementAst, context: any): any { throw 'not implemented'; }
visitReference(ast: ReferenceAst, context: any): any { throw 'not implemented'; }
visitVariable(ast: VariableAst, 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 {
visitElement(ast: ElementAst, context: any): any {
if (ast.name != 'foo') return ast;
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 {
logs: string[] = [];
warnings: string[] = [];

View File

@ -46,16 +46,14 @@ export class MockDirectiveResolver extends DirectiveResolver {
let providers = metadata.providers;
if (isPresent(providerOverrides)) {
const originalViewProviders: Provider[] =
isPresent(metadata.providers) ? metadata.providers : [];
const originalViewProviders: Provider[] = metadata.providers || [];
providers = originalViewProviders.concat(providerOverrides);
}
if (metadata instanceof Component) {
let viewProviders = metadata.viewProviders;
if (isPresent(viewProviderOverrides)) {
const originalViewProviders: Provider[] =
isPresent(metadata.viewProviders) ? metadata.viewProviders : [];
const originalViewProviders: Provider[] = metadata.viewProviders || [];
viewProviders = originalViewProviders.concat(viewProviderOverrides);
}

View File

@ -67,7 +67,7 @@ export class MockResourceLoader extends ResourceLoader {
verifyNoOutstandingExpectations() {
if (this._expectations.length === 0) return;
var urls: any[] /** TODO #9100 */ = [];
var urls: string[] = [];
for (var i = 0; i < this._expectations.length; i++) {
var expectation = this._expectations[i];
urls.push(expectation.url);

View File

@ -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 core/animation/ts/dsl/animation_example.ts region='Component'}

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StringWrapper} from '../src/facade/lang';
import {OpaqueToken} from './di';
@ -38,7 +36,7 @@ export const APP_ID_RANDOM_PROVIDER = {
};
function _randomChar(): string {
return StringWrapper.fromCharCode(97 + Math.floor(Math.random() * 25));
return String.fromCharCode(97 + Math.floor(Math.random() * 25));
}
/**

View File

@ -47,7 +47,7 @@ export class DefaultIterableDiffer implements IterableDiffer {
private _identityChangesTail: CollectionChangeRecord = null;
constructor(private _trackByFn?: TrackByFn) {
this._trackByFn = isPresent(this._trackByFn) ? this._trackByFn : trackByIdentity;
this._trackByFn = this._trackByFn || trackByIdentity;
}
get collection() { return this._collection; }

View File

@ -159,7 +159,7 @@ export class ViewContainerRef_ implements ViewContainerRef {
componentFactory: ComponentFactory<C>, index: number = -1, injector: Injector = null,
projectableNodes: any[][] = null): ComponentRef<C> {
var s = this._createComponentInContainerScope();
var contextInjector = isPresent(injector) ? injector : this._element.parentInjector;
var contextInjector = injector || this._element.parentInjector;
var componentRef = componentFactory.create(contextInjector, projectableNodes);
this.insert(componentRef.hostView, index);
return wtfLeave(s, componentRef);

View File

@ -16,4 +16,4 @@ export {ReflectionInfo, Reflector} from './reflector';
* The {@link Reflector} used internally in Angular to access metadata
* about symbols.
*/
export var reflector = new Reflector(new ReflectionCapabilities());
export const reflector = new Reflector(new ReflectionCapabilities());

View File

@ -19,19 +19,11 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
isReflectionEnabled(): boolean { return true; }
factory(t: Type<any>): Function {
var prototype = t.prototype;
return function(...args: any[]) {
var instance = Object.create(prototype);
t.apply(instance, args);
return instance;
};
}
factory<T>(t: Type<T>): (args: any[]) => T { return (...args: any[]) => new t(...args); }
/** @internal */
_zipTypesAndAnnotations(
paramTypes: any /** TODO #9100 */, paramAnnotations: any /** TODO #9100 */): any[][] {
var result: any /** TODO #9100 */;
_zipTypesAndAnnotations(paramTypes: any[], paramAnnotations: any[]): any[][] {
var result: any[][];
if (typeof paramTypes === 'undefined') {
result = new Array(paramAnnotations.length);
@ -50,48 +42,45 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
} else {
result[i] = [];
}
if (isPresent(paramAnnotations) && isPresent(paramAnnotations[i])) {
if (paramAnnotations && isPresent(paramAnnotations[i])) {
result[i] = result[i].concat(paramAnnotations[i]);
}
}
return result;
}
parameters(typeOrFunc: Type<any>): any[][] {
parameters(type: Type<any>): any[][] {
// Prefer the direct API.
if (isPresent((<any>typeOrFunc).parameters)) {
return (<any>typeOrFunc).parameters;
if ((<any>type).parameters) {
return (<any>type).parameters;
}
// API of tsickle for lowering decorators to properties on the class.
if (isPresent((<any>typeOrFunc).ctorParameters)) {
let ctorParameters = (<any>typeOrFunc).ctorParameters;
let paramTypes =
ctorParameters.map((ctorParam: any /** TODO #9100 */) => ctorParam && ctorParam.type);
let paramAnnotations = ctorParameters.map(
(ctorParam: any /** TODO #9100 */) =>
if ((<any>type).ctorParameters) {
const ctorParameters = (<any>type).ctorParameters;
const paramTypes = ctorParameters.map((ctorParam: any) => ctorParam && ctorParam.type);
const paramAnnotations = ctorParameters.map(
(ctorParam: any) =>
ctorParam && convertTsickleDecoratorIntoMetadata(ctorParam.decorators));
return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
}
// API for metadata created by invoking the decorators.
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
var paramAnnotations = this._reflect.getMetadata('parameters', typeOrFunc);
var paramTypes = this._reflect.getMetadata('design:paramtypes', typeOrFunc);
if (isPresent(paramTypes) || isPresent(paramAnnotations)) {
const paramAnnotations = this._reflect.getMetadata('parameters', type);
const paramTypes = this._reflect.getMetadata('design:paramtypes', type);
if (paramTypes || paramAnnotations) {
return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
}
}
// The array has to be filled with `undefined` because holes would be skipped by `some`
let parameters = new Array((<any>typeOrFunc.length));
parameters.fill(undefined);
return parameters;
return new Array((<any>type.length)).fill(undefined);
}
annotations(typeOrFunc: Type<any>): any[] {
// Prefer the direct API.
if (isPresent((<any>typeOrFunc).annotations)) {
var annotations = (<any>typeOrFunc).annotations;
if ((<any>typeOrFunc).annotations) {
let annotations = (<any>typeOrFunc).annotations;
if (isFunction(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.
if (isPresent((<any>typeOrFunc).decorators)) {
if ((<any>typeOrFunc).decorators) {
return convertTsickleDecoratorIntoMetadata((<any>typeOrFunc).decorators);
}
// API for metadata created by invoking the decorators.
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
var annotations = this._reflect.getMetadata('annotations', typeOrFunc);
if (isPresent(annotations)) return annotations;
if (this._reflect && this._reflect.getMetadata) {
const annotations = this._reflect.getMetadata('annotations', typeOrFunc);
if (annotations) return annotations;
}
return [];
}
propMetadata(typeOrFunc: any): {[key: string]: any[]} {
// Prefer the direct API.
if (isPresent((<any>typeOrFunc).propMetadata)) {
var propMetadata = (<any>typeOrFunc).propMetadata;
if ((<any>typeOrFunc).propMetadata) {
let propMetadata = (<any>typeOrFunc).propMetadata;
if (isFunction(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.
if (isPresent((<any>typeOrFunc).propDecorators)) {
let propDecorators = (<any>typeOrFunc).propDecorators;
let propMetadata = <{[key: string]: any[]}>{};
if ((<any>typeOrFunc).propDecorators) {
const propDecorators = (<any>typeOrFunc).propDecorators;
const propMetadata = <{[key: string]: any[]}>{};
Object.keys(propDecorators).forEach(prop => {
propMetadata[prop] = convertTsickleDecoratorIntoMetadata(propDecorators[prop]);
});
@ -132,9 +121,9 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
}
// API for metadata created by invoking the decorators.
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
var propMetadata = this._reflect.getMetadata('propMetadata', typeOrFunc);
if (isPresent(propMetadata)) return propMetadata;
if (this._reflect && this._reflect.getMetadata) {
const propMetadata = this._reflect.getMetadata('propMetadata', typeOrFunc);
if (propMetadata) return propMetadata;
}
return {};
}
@ -147,7 +136,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
hasLifecycleHook(type: any, lcInterface: Type<any>, lcProperty: string): boolean {
if (!(type instanceof Type)) return false;
var proto = (<any>type).prototype;
const proto = (<any>type).prototype;
return !!proto[lcProperty];
}
@ -158,7 +147,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
}
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 <MethodFn>new Function('o', 'args', functionBody);
}

View File

@ -7,7 +7,6 @@
*/
import {MapWrapper} from '../facade/collection';
import {isPresent} from '../facade/lang';
import {Type} from '../type';
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
import {ReflectorReader} from './reflector_reader';
@ -60,10 +59,10 @@ export class Reflector extends ReflectorReader {
* potential dead code.
*/
listUnusedKeys(): any[] {
if (this._usedKeys == null) {
if (!this._usedKeys) {
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));
}
@ -83,87 +82,73 @@ export class Reflector extends ReflectorReader {
factory(type: Type<any>): Function {
if (this._containsReflectionInfo(type)) {
var res = this._getReflectionInfo(type).factory;
return isPresent(res) ? res : null;
} else {
return this.reflectionCapabilities.factory(type);
return this._getReflectionInfo(type).factory || null;
}
return this.reflectionCapabilities.factory(type);
}
parameters(typeOrFunc: Type<any>): any[][] {
if (this._injectableInfo.has(typeOrFunc)) {
var res = this._getReflectionInfo(typeOrFunc).parameters;
return isPresent(res) ? res : [];
} else {
return this.reflectionCapabilities.parameters(typeOrFunc);
return this._getReflectionInfo(typeOrFunc).parameters || [];
}
return this.reflectionCapabilities.parameters(typeOrFunc);
}
annotations(typeOrFunc: Type<any>): any[] {
if (this._injectableInfo.has(typeOrFunc)) {
var res = this._getReflectionInfo(typeOrFunc).annotations;
return isPresent(res) ? res : [];
} else {
return this.reflectionCapabilities.annotations(typeOrFunc);
return this._getReflectionInfo(typeOrFunc).annotations || [];
}
return this.reflectionCapabilities.annotations(typeOrFunc);
}
propMetadata(typeOrFunc: Type<any>): {[key: string]: any[]} {
if (this._injectableInfo.has(typeOrFunc)) {
var res = this._getReflectionInfo(typeOrFunc).propMetadata;
return isPresent(res) ? res : {};
} else {
return this.reflectionCapabilities.propMetadata(typeOrFunc);
return this._getReflectionInfo(typeOrFunc).propMetadata || {};
}
return this.reflectionCapabilities.propMetadata(typeOrFunc);
}
interfaces(type: Type<any>): any[] {
if (this._injectableInfo.has(type)) {
var res = this._getReflectionInfo(type).interfaces;
return isPresent(res) ? res : [];
} else {
return this.reflectionCapabilities.interfaces(type);
return this._getReflectionInfo(type).interfaces || [];
}
return this.reflectionCapabilities.interfaces(type);
}
hasLifecycleHook(type: any, lcInterface: Type<any>, lcProperty: string): boolean {
var interfaces = this.interfaces(type);
if (interfaces.indexOf(lcInterface) !== -1) {
if (this.interfaces(type).indexOf(lcInterface) !== -1) {
return true;
} else {
return this.reflectionCapabilities.hasLifecycleHook(type, lcInterface, lcProperty);
}
return this.reflectionCapabilities.hasLifecycleHook(type, lcInterface, lcProperty);
}
getter(name: string): GetterFn {
if (this._getters.has(name)) {
return this._getters.get(name);
} else {
return this.reflectionCapabilities.getter(name);
}
return this._getters.has(name) ? this._getters.get(name) :
this.reflectionCapabilities.getter(name);
}
setter(name: string): SetterFn {
if (this._setters.has(name)) {
return this._setters.get(name);
} else {
return this.reflectionCapabilities.setter(name);
}
return this._setters.has(name) ? this._setters.get(name) :
this.reflectionCapabilities.setter(name);
}
method(name: string): MethodFn {
if (this._methods.has(name)) {
return this._methods.get(name);
} else {
return this.reflectionCapabilities.method(name);
}
return this._methods.has(name) ? this._methods.get(name) :
this.reflectionCapabilities.method(name);
}
/** @internal */
_getReflectionInfo(typeOrFunc: any): ReflectionInfo {
if (isPresent(this._usedKeys)) {
if (this._usedKeys) {
this._usedKeys.add(typeOrFunc);
}
return this._injectableInfo.get(typeOrFunc);
}

View File

@ -6,10 +6,11 @@
* 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';
var _nextClassId = 0;
let _nextClassId = 0;
const Reflect = global.Reflect;
/**
* Declares the interface to be used with {@link Class}.
@ -87,7 +88,7 @@ export interface TypeDecorator {
}
function extractAnnotation(annotation: any): any {
if (isFunction(annotation) && annotation.hasOwnProperty('annotation')) {
if (typeof annotation === 'function' && annotation.hasOwnProperty('annotation')) {
// it is a decorator, extract annotation
annotation = annotation.annotation;
}
@ -99,13 +100,16 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
fnOrArray === Number || fnOrArray === Array) {
throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`);
}
if (isFunction(fnOrArray)) {
return <Function>fnOrArray;
} else if (fnOrArray instanceof Array) {
if (typeof fnOrArray === 'function') {
return fnOrArray;
}
if (Array.isArray(fnOrArray)) {
const annotations: any[] = fnOrArray;
const annoLength = annotations.length - 1;
const fn: Function = fnOrArray[annoLength];
if (!isFunction(fn)) {
if (typeof fn !== 'function') {
throw new Error(
`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[] = [];
paramsAnnotations.push(paramAnnotations);
const annotation = annotations[i];
if (annotation instanceof Array) {
if (Array.isArray(annotation)) {
for (let j = 0; j < annotation.length; j++) {
paramAnnotations.push(extractAnnotation(annotation[j]));
}
} else if (isFunction(annotation)) {
} else if (typeof annotation === 'function') {
paramAnnotations.push(extractAnnotation(annotation));
} else {
paramAnnotations.push(annotation);
@ -130,10 +134,10 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
}
Reflect.defineMetadata('parameters', paramsAnnotations, fn);
return fn;
} else {
}
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({
* 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 {
* 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> {
const constructor = applyParams(
clsDef.hasOwnProperty('constructor') ? clsDef.constructor : undefined, 'constructor');
let proto = constructor.prototype;
if (clsDef.hasOwnProperty('extends')) {
if (isFunction(clsDef.extends)) {
if (typeof clsDef.extends === 'function') {
(<Function>constructor).prototype = proto =
Object.create((<Function>clsDef.extends).prototype);
} 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)}`);
}
}
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);
}
}
@ -249,10 +256,8 @@ export function Class(clsDef: ClassDefinition): Type<any> {
return <Type<any>>constructor;
}
var Reflect = global.Reflect;
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 {
const metaCtor = makeMetadataCtor([props]);
@ -264,10 +269,11 @@ export function makeDecorator(
if (this instanceof DecoratorFactory) {
metaCtor.call(this, objOrType);
return this;
} else {
}
const annotationInstance = new (<any>DecoratorFactory)(objOrType);
const chainAnnotation =
isFunction(this) && this.annotations instanceof Array ? this.annotations : [];
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) || [];
@ -280,10 +286,11 @@ export function makeDecorator(
if (chainFn) chainFn(TypeDecorator);
return TypeDecorator;
}
}
if (parentClass) {
DecoratorFactory.prototype = Object.create(parentClass.prototype);
}
DecoratorFactory.prototype.toString = () => `@${name}`;
(<any>DecoratorFactory).annotationCls = DecoratorFactory;
return DecoratorFactory;
@ -295,12 +302,11 @@ function makeMetadataCtor(props: ([string, any] | {[key: string]: any})[]): any
const argVal = args[i];
if (Array.isArray(prop)) {
// plain parameter
const val = !argVal || argVal === undefined ? prop[1] : argVal;
this[prop[0]] = val;
this[prop[0]] = !argVal || argVal === undefined ? prop[1] : argVal;
} else {
for (let propName in prop) {
const val = !argVal || argVal[propName] === undefined ? prop[propName] : argVal[propName];
this[propName] = val;
this[propName] =
!argVal || argVal[propName] === undefined ? prop[propName] : argVal[propName];
}
}
});
@ -309,7 +315,7 @@ function makeMetadataCtor(props: ([string, any] | {[key: string]: any})[]): any
}
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);
function ParamDecoratorFactory(...args: any[]): any {
if (this instanceof ParamDecoratorFactory) {
@ -331,8 +337,7 @@ export function makeParamDecorator(
}
parameters[index] = parameters[index] || [];
var annotationsForParam: any[] = parameters[index];
annotationsForParam.push(annotationInstance);
parameters[index].push(annotationInstance);
Reflect.defineMetadata('parameters', parameters, cls);
return cls;
@ -353,8 +358,9 @@ export function makePropDecorator(
if (this instanceof PropDecoratorFactory) {
metaCtor.apply(this, args);
return this;
} else {
var decoratorInstance = new (<any>PropDecoratorFactory)(...args);
}
const decoratorInstance = new (<any>PropDecoratorFactory)(...args);
return function PropDecorator(target: any, name: string) {
const meta = Reflect.getOwnMetadata('propMetadata', target.constructor) || {};
meta[name] = meta[name] || [];
@ -362,7 +368,6 @@ export function makePropDecorator(
Reflect.defineMetadata('propMetadata', meta, target.constructor);
};
}
}
if (parentClass) {
PropDecoratorFactory.prototype = Object.create(parentClass.prototype);
}

View File

@ -8,25 +8,22 @@
import {EventEmitter} from '../facade/async';
import {NgZoneImpl} from './ng_zone_impl';
/**
* 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
* 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
* can reenter the Angular zone via {@link #run}.
* Angular. Such tasks can be kicked off via {@link runOutsideAngular} and if needed, these tasks
* can reenter the Angular zone via {@link run}.
*
* <!-- TODO: add/fix links to:
* - docs explaining zones and the use of zones in Angular and change-detection
* - 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';
*
* @Component({
@ -67,7 +64,6 @@ import {NgZoneImpl} from './ng_zone_impl';
* }}));
* }
*
*
* _increaseProgress(doneCallback: () => void) {
* this.progress += 1;
* console.log(`Current progress: ${this.progress}%`);
@ -83,82 +79,85 @@ import {NgZoneImpl} from './ng_zone_impl';
* @experimental
*/
export class NgZone {
static isInAngularZone(): boolean { return NgZoneImpl.isInAngularZone(); }
static assertInAngularZone(): void {
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 outer: Zone;
private inner: Zone;
private _hasPendingMicrotasks: boolean = false;
private _hasPendingMacrotasks: boolean = false;
/** @internal */
private _isStable = true;
/** @internal */
private _nesting = 0;
/** @internal */
private _nesting: number = 0;
private _onUnstable: EventEmitter<any> = new EventEmitter(false);
/** @internal */
private _onMicrotaskEmpty: EventEmitter<any> = new EventEmitter(false);
/** @internal */
private _onStable: EventEmitter<any> = new EventEmitter(false);
/** @internal */
private _onErrorEvents: EventEmitter<any> = new EventEmitter(false);
constructor({enableLongStackTrace = false}) {
this._zoneImpl = new NgZoneImpl({
trace: enableLongStackTrace,
onEnter: () => {
// console.log('ZONE.enter', this._nesting, this._isStable);
this._nesting++;
if (this._isStable) {
this._isStable = false;
this._onUnstable.emit(null);
}
},
onLeave: () => {
this._nesting--;
// console.log('ZONE.leave', this._nesting, this._isStable);
this._checkStable();
},
setMicrotask: (hasMicrotasks: boolean) => {
this._hasPendingMicrotasks = hasMicrotasks;
this._checkStable();
},
setMacrotask: (hasMacrotasks: boolean) => { this._hasPendingMacrotasks = hasMacrotasks; },
onError: (error: any) => this._onErrorEvents.emit(error)
});
if (typeof Zone == 'undefined') {
throw new Error('Angular requires Zone.js prolyfill.');
}
private _checkStable() {
if (this._nesting == 0) {
if (!this._hasPendingMicrotasks && !this._isStable) {
try {
// console.log('ZONE.microtaskEmpty');
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;
Zone.assertZonePatched();
this.outer = this.inner = Zone.current;
if ((Zone as any)['wtfZoneSpec']) {
this.inner = this.inner.fork((Zone as any)['wtfZoneSpec']);
}
if (enableLongStackTrace && (Zone as any)['longStackTraceZoneSpec']) {
this.inner = this.inner.fork((Zone as any)['longStackTraceZoneSpec']);
}
this.forkInnerZoneWithAngularBehavior();
}
static isInAngularZone(): boolean { return Zone.current.get('isAngularZone') === true; }
static assertInAngularZone(): void {
if (!NgZone.isInAngularZone()) {
throw new Error('Expected to be in Angular Zone, but it is not!');
}
}
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.
@ -185,51 +184,98 @@ export class NgZone {
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; }
/**
* Whether there are any outstanding microtasks.
*/
get hasPendingMicrotasks(): boolean { return this._hasPendingMicrotasks; }
/**
* Whether there are any outstanding microtasks.
*/
get hasPendingMacrotasks(): boolean { return this._hasPendingMacrotasks; }
/**
* 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._zoneImpl.runInner(fn); }
private checkStable() {
if (this._nesting == 0 && !this._hasPendingMicrotasks && !this._isStable) {
try {
this._nesting++;
this._onMicrotaskEmpty.emit(null);
} finally {
this._nesting--;
if (!this._hasPendingMicrotasks) {
try {
this.runOutsideAngular(() => this._onStable.emit(null));
} finally {
this._isStable = true;
}
}
}
}
}
/**
* Same as #run, except that synchronous errors are caught and forwarded
* via `onError` and not rethrown.
*/
runGuarded(fn: () => any): any { return this._zoneImpl.runInnerGuarded(fn); }
private forkInnerZoneWithAngularBehavior() {
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();
}
},
/**
* 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._zoneImpl.runOuter(fn); }
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.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); }
}

View File

@ -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); };
}

View File

@ -146,6 +146,87 @@ function declareTests({useJit}: {useJit: boolean}) {
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',
fakeAsync(() => {
TestBed.overrideComponent(DummyIfCmp, {

View File

@ -74,7 +74,7 @@ export function main() {
var cdRef = <any>new SpyChangeDetectorRef();
try {
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');
} finally {
ref.unregisterChangeDetector(cdRef);

View File

@ -31,17 +31,17 @@ export function main() {
});
it('should return the first suitable implementation', () => {
factory1.spy('supports').andReturn(false);
factory2.spy('supports').andReturn(true);
factory3.spy('supports').andReturn(true);
factory1.spy('supports').and.returnValue(false);
factory2.spy('supports').and.returnValue(true);
factory3.spy('supports').and.returnValue(true);
var differs = IterableDiffers.create(<any>[factory1, factory2, factory3]);
expect(differs.find('some object')).toBe(factory2);
});
it('should copy over differs from the parent repo', () => {
factory1.spy('supports').andReturn(true);
factory2.spy('supports').andReturn(false);
factory1.spy('supports').and.returnValue(true);
factory2.spy('supports').and.returnValue(false);
var parent = IterableDiffers.create(<any>[factory1]);
var child = IterableDiffers.create(<any>[factory2], parent);

View File

@ -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 {iterateListLike} from '../../src/facade/collection';
import {StringWrapper} from '../../src/facade/lang';
interface _JsQueryList {
filter(c: any): any;
@ -104,8 +103,8 @@ export function main() {
it('should support toString', () => {
queryList.reset(['one', 'two']);
var listString = queryList.toString();
expect(StringWrapper.contains(listString, 'one')).toBeTruthy();
expect(StringWrapper.contains(listString, 'two')).toBeTruthy();
expect(listString.indexOf('one') != -1).toBeTruthy();
expect(listString.indexOf('two') != -1).toBeTruthy();
});
it('should support first and last', () => {

View File

@ -66,7 +66,7 @@ export function main() {
});
describe('spy objects', () => {
var spyObj: any /** TODO #9100 */;
let spyObj: any;
beforeEach(() => { spyObj = <any>new SpyTestObj(); });
@ -74,7 +74,7 @@ export function main() {
() => { expect(spyObj.spy('someFunc')).not.toHaveBeenCalled(); });
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.spy('someFunc')).toHaveBeenCalledWith(1, 2);
@ -106,12 +106,6 @@ export function main() {
it('should create spys for all methods',
() => { 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);
});
});
});
}

View File

@ -19,7 +19,7 @@ export {inject} from './test_bed';
export * from './logger';
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);
@ -35,7 +35,6 @@ var jsmIIt = _global.fit;
var jsmXIt = _global.xit;
var runnerStack: BeforeEachRunner[] = [];
var inIt = false;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000;
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 timeOut = Math.max(globalTimeOut, testTimeOut);
jsmFn(name, (done: any /** TODO #9100 */) => {
jsmFn(name, (done: any) => {
var completerProvider = {
provide: AsyncTestCompleter,
useFactory: () => {
@ -134,7 +133,6 @@ function _it(jsmFn: Function, name: string, testFn: Function, testTimeOut: numbe
testBed.configureTestingModule({providers: [completerProvider]});
runner.run();
inIt = true;
if (testFn.length == 0) {
let retVal = testFn();
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.
testFn(done);
}
inIt = false;
}, timeOut);
}
export function it(
name: any /** TODO #9100 */, fn: any /** TODO #9100 */,
timeOut: any /** TODO #9100 */ = null): void {
export function it(name: any, fn: any, timeOut: any = null): void {
return _it(jsmIt, name, fn, timeOut);
}
export function xit(
name: any /** TODO #9100 */, fn: any /** TODO #9100 */,
timeOut: any /** TODO #9100 */ = null): void {
export function xit(name: any, fn: any, timeOut: any = null): void {
return _it(jsmXIt, name, fn, timeOut);
}
export function iit(
name: any /** TODO #9100 */, fn: any /** TODO #9100 */,
timeOut: any /** TODO #9100 */ = null): void {
export function iit(name: any, fn: any, timeOut: any = null): void {
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 {
constructor(type: any /** TODO #9100 */ = null) {
constructor(type?: any) {
if (type) {
for (var prop in type.prototype) {
var m: any /** TODO #9100 */ = null;
for (let prop in type.prototype) {
let m: any = null;
try {
m = type.prototype[prop];
} 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 */) {
if (!(this as any /** TODO #9100 */)[name]) {
(this as any /** TODO #9100 */)[name] = this._createGuinnessCompatibleSpy(name);
spy(name: string) {
if (!(this as any)[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 */) {
(this as any /** TODO #9100 */)[name] = value;
}
prop(name: string, value: any) { (this as any)[name] = value; }
static stub(
object: any /** TODO #9100 */ = null, config: any /** TODO #9100 */ = null,
overrides: any /** TODO #9100 */ = null) {
static stub(object: any = null, config: any = null, overrides: any = null) {
if (!(object instanceof SpyObject)) {
overrides = config;
config = object;
@ -224,18 +198,7 @@ export class SpyObject {
}
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;
}
/** @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;
}
}

View File

@ -78,14 +78,14 @@ export class EventEmitter<T> extends Subject<T> {
emit(value?: T) { super.next(value); }
subscribe(generatorOrNext?: any, error?: any, complete?: any): any {
let schedulerFn: any /** TODO #9100 */;
let errorFn = (err: any): any /** TODO #9100 */ => null;
let completeFn = (): any /** TODO #9100 */ => null;
let schedulerFn: (t: any) => any;
let errorFn = (err: any): any => null;
let completeFn = (): any => null;
if (generatorOrNext && typeof generatorOrNext === 'object') {
schedulerFn = this.__isAsync ? (value: any /** TODO #9100 */) => {
schedulerFn = this.__isAsync ? (value: any) => {
setTimeout(() => generatorOrNext.next(value));
} : (value: any /** TODO #9100 */) => { generatorOrNext.next(value); };
} : (value: any) => { generatorOrNext.next(value); };
if (generatorOrNext.error) {
errorFn = this.__isAsync ? (err) => { setTimeout(() => generatorOrNext.error(err)); } :
@ -97,9 +97,8 @@ export class EventEmitter<T> extends Subject<T> {
() => { generatorOrNext.complete(); };
}
} else {
schedulerFn = this.__isAsync ? (value: any /** TODO #9100 */) => {
setTimeout(() => generatorOrNext(value));
} : (value: any /** TODO #9100 */) => { generatorOrNext(value); };
schedulerFn = this.__isAsync ? (value: any) => { setTimeout(() => generatorOrNext(value)); } :
(value: any) => { generatorOrNext(value); };
if (error) {
errorFn =

View File

@ -137,73 +137,6 @@ export function stringify(token: any): string {
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 {
constructor(public parts: string[] = []) {}

View File

@ -6,7 +6,7 @@
* 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 MySubclass extends MySuperclass {}
@ -50,92 +50,4 @@ export function main() {
() => { 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); });
});
});
}

View File

@ -48,7 +48,8 @@ const resolvedPromise = Promise.resolve(null);
* sub-groups within the form.
*
* 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'}
*
@ -61,7 +62,7 @@ const resolvedPromise = Promise.resolve(null);
@Directive({
selector: 'form:not([ngNoForm]):not([formGroup]),ngForm,[ngForm]',
providers: [formDirectiveProvider],
host: {'(submit)': 'onSubmit()', '(reset)': 'onReset()'},
host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'},
outputs: ['ngSubmit'],
exportAs: 'ngForm'
})
@ -102,7 +103,7 @@ export class NgForm extends ControlContainer implements Form {
removeControl(dir: NgModel): void {
resolvedPromise.then(() => {
var container = this._findContainer(dir.path);
const container = this._findContainer(dir.path);
if (isPresent(container)) {
container.removeControl(dir.name);
}
@ -111,8 +112,8 @@ export class NgForm extends ControlContainer implements Form {
addFormGroup(dir: NgModelGroup): void {
resolvedPromise.then(() => {
var container = this._findContainer(dir.path);
var group = new FormGroup({});
const container = this._findContainer(dir.path);
const group = new FormGroup({});
setUpFormContainer(group, dir);
container.registerControl(dir.name, group);
group.updateValueAndValidity({emitEvent: false});
@ -121,7 +122,7 @@ export class NgForm extends ControlContainer implements Form {
removeFormGroup(dir: NgModelGroup): void {
resolvedPromise.then(() => {
var container = this._findContainer(dir.path);
const container = this._findContainer(dir.path);
if (isPresent(container)) {
container.removeControl(dir.name);
}
@ -132,16 +133,16 @@ export class NgForm extends ControlContainer implements Form {
updateModel(dir: NgControl, value: any): void {
resolvedPromise.then(() => {
var ctrl = <FormControl>this.form.get(dir.path);
const ctrl = <FormControl>this.form.get(dir.path);
ctrl.setValue(value);
});
}
setValue(value: {[key: string]: any}): void { this.control.setValue(value); }
onSubmit(): boolean {
onSubmit($event: Event): boolean {
this._submitted = true;
this.ngSubmit.emit(null);
this.ngSubmit.emit($event);
return false;
}

View File

@ -44,6 +44,10 @@ export const formDirectiveProvider: any = {
* its {@link AbstractControl.statusChanges} event to be notified when the validation status is
* 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
*
* In this example, we create form controls for first name and last name.
@ -59,7 +63,7 @@ export const formDirectiveProvider: any = {
@Directive({
selector: '[formGroup]',
providers: [formDirectiveProvider],
host: {'(submit)': 'onSubmit()', '(reset)': 'onReset()'},
host: {'(submit)': 'onSubmit($event)', '(reset)': 'onReset()'},
exportAs: 'ngForm'
})
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); }
addFormGroup(dir: FormGroupName): void {
var ctrl: any = this.form.get(dir.path);
const ctrl: any = this.form.get(dir.path);
setUpFormContainer(ctrl, dir);
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); }
addFormArray(dir: FormArrayName): void {
var ctrl: any = this.form.get(dir.path);
const ctrl: any = this.form.get(dir.path);
setUpFormContainer(ctrl, dir);
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); }
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);
}
onSubmit(): boolean {
onSubmit($event: Event): boolean {
this._submitted = true;
this.ngSubmit.emit(null);
this.ngSubmit.emit($event);
return false;
}

View File

@ -9,7 +9,7 @@
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core';
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';
@ -22,7 +22,7 @@ export const SELECT_VALUE_ACCESSOR: any = {
function _buildValueString(id: string, value: any): string {
if (isBlank(id)) return `${value}`;
if (!isPrimitive(value)) value = 'Object';
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
return `${id}: ${value}`.slice(0, 50);
}
function _extractId(valueString: string): string {

View File

@ -9,7 +9,7 @@
import {Directive, ElementRef, Host, Input, OnDestroy, OpaqueToken, Optional, Renderer, Type, forwardRef} from '@angular/core';
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';
@ -23,7 +23,7 @@ function _buildValueString(id: string, value: any): string {
if (isBlank(id)) return `${value}`;
if (isString(value)) value = `'${value}'`;
if (!isPrimitive(value)) value = 'Object';
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
return `${id}: ${value}`.slice(0, 50);
}
function _extractId(valueString: string): string {

View File

@ -52,13 +52,14 @@ function _find(control: AbstractControl, path: Array<string|number>| string, del
return (<Array<string|number>>path).reduce((v, name) => {
if (v instanceof FormGroup) {
return isPresent(v.controls[name]) ? 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;
return v.controls[name] || null;
}
if (v instanceof FormArray) {
return v.at(<number>name) || null;
}
return null;
}, control);
}

View File

@ -11,11 +11,13 @@ import {toPromise} from 'rxjs/operator/toPromise';
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
import {StringMapWrapper} from './facade/collection';
import {isBlank, isPresent, isString} from './facade/lang';
import {isPresent} from './facade/lang';
import {AbstractControl} from './model';
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.
@ -60,9 +62,7 @@ export class Validators {
* Validator that requires controls to have a non-empty value.
*/
static required(control: AbstractControl): {[key: string]: boolean} {
return isBlank(control.value) || (isString(control.value) && control.value == '') ?
{'required': true} :
null;
return isEmptyInputValue(control.value) ? {'required': true} : null;
}
/**
@ -70,10 +70,12 @@ export class Validators {
*/
static minLength(minLength: number): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} => {
if (isPresent(Validators.required(control))) return null;
var v: string = control.value;
return v.length < minLength ?
{'minlength': {'requiredLength': minLength, 'actualLength': v.length}} :
if (isEmptyInputValue(control.value)) {
return null; // don't validate empty values to allow optional controls
}
const length = typeof control.value === 'string' ? control.value.length : 0;
return length < minLength ?
{'minlength': {'requiredLength': minLength, 'actualLength': length}} :
null;
};
}
@ -83,10 +85,9 @@ export class Validators {
*/
static maxLength(maxLength: number): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} => {
if (isPresent(Validators.required(control))) return null;
var v: string = control.value;
return v.length > maxLength ?
{'maxlength': {'requiredLength': maxLength, 'actualLength': v.length}} :
const length = typeof control.value === 'string' ? control.value.length : 0;
return length > maxLength ?
{'maxlength': {'requiredLength': maxLength, 'actualLength': length}} :
null;
};
}
@ -96,10 +97,14 @@ export class Validators {
*/
static pattern(pattern: string): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} => {
let regex = new RegExp(`^${pattern}$`);
let v: string = control.value;
return regex.test(v) ? null :
{'pattern': {'requiredPattern': `^${pattern}$`, 'actualValue': v}};
if (isEmptyInputValue(control.value)) {
return null; // don't validate empty values to allow optional controls
}
const regex = new RegExp(`^${pattern}$`);
const value: string = control.value;
return regex.test(value) ?
null :
{'pattern': {'requiredPattern': `^${pattern}$`, 'actualValue': value}};
};
}

View File

@ -529,17 +529,17 @@ export function main() {
});
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);
fixture.componentInstance.form = new FormGroup({'login': new FormControl('loginValue')});
fixture.componentInstance.data = 'should be changed';
fixture.componentInstance.event = null;
fixture.detectChanges();
const formEl = fixture.debugElement.query(By.css('form')).nativeElement;
dispatchEvent(formEl, 'submit');
fixture.detectChanges();
expect(fixture.componentInstance.data).toEqual('submitted');
expect(fixture.componentInstance.event.type).toEqual('submit');
});
it('should mark formGroup as submitted on submit event', () => {
@ -1760,7 +1760,7 @@ class FormControlComp {
@Component({
selector: 'form-group-comp',
template: `
<form [formGroup]="form" (ngSubmit)="data='submitted'">
<form [formGroup]="form" (ngSubmit)="event=$event">
<input type="text" formControlName="login">
</form>
`
@ -1769,7 +1769,7 @@ class FormGroupComp {
control: FormControl;
form: FormGroup;
myGroup: FormGroup;
data: string;
event: Event;
}
@Component({

View File

@ -22,7 +22,7 @@ export function main() {
StandaloneNgModel, NgModelForm, NgModelGroupForm, NgModelValidBinding, NgModelNgIfForm,
NgModelRadioForm, NgModelSelectForm, NgNoFormComp, InvalidNgModelNoName,
NgModelOptionsStandalone, NgModelCustomComp, NgModelCustomWrapper,
NgModelValidationBindings
NgModelValidationBindings, NgModelMultipleValidators
],
imports: [FormsModule]
});
@ -237,15 +237,15 @@ export function main() {
});
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);
fixture.componentInstance.name = 'old';
fixture.componentInstance.event = null;
const form = fixture.debugElement.query(By.css('form'));
dispatchEvent(form.nativeElement, 'submit');
tick();
expect(fixture.componentInstance.name).toEqual('submitted');
expect(fixture.componentInstance.event.type).toEqual('submit');
}));
it('should mark NgForm as submitted on submit event', fakeAsync(() => {
@ -728,6 +728,50 @@ export function main() {
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',
fakeAsync(() => {
const fixture = TestBed.createComponent(NgModelValidationBindings);
@ -854,13 +898,14 @@ class StandaloneNgModel {
@Component({
selector: 'ng-model-form',
template: `
<form (ngSubmit)="name='submitted'" (reset)="onReset()">
<form (ngSubmit)="event=$event" (reset)="onReset()">
<input name="name" [(ngModel)]="name" minlength="10" [ngModelOptions]="options">
</form>
`
})
class NgModelForm {
name: string;
event: Event;
options = {};
onReset() {}
@ -1037,6 +1082,20 @@ class NgModelValidationBindings {
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) {
const l = getDOM().classList(el);
l.sort();

View File

@ -44,21 +44,24 @@ export function main() {
() => { expect(Validators.required(new FormControl(null))).toEqual({'required': true}); });
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',
() => { expect(Validators.required(new FormControl(0))).toEqual(null); });
() => { expect(Validators.required(new FormControl(0))).toBeNull(); });
});
describe('minLength', () => {
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',
() => { 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',
() => { expect(Validators.minLength(2)(new FormControl('aa'))).toEqual(null); });
() => { expect(Validators.minLength(2)(new FormControl('aa'))).toBeNull(); });
it('should error on short strings', () => {
expect(Validators.minLength(2)(new FormControl('a'))).toEqual({
@ -69,13 +72,13 @@ export function main() {
describe('maxLength', () => {
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',
() => { expect(Validators.maxLength(2)(new FormControl(null))).toEqual(null); });
() => { expect(Validators.maxLength(2)(new FormControl(null))).toBeNull(); });
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', () => {
expect(Validators.maxLength(2)(new FormControl('aaa'))).toEqual({
@ -86,29 +89,25 @@ export function main() {
describe('pattern', () => {
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',
() => { 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',
() => { expect(Validators.pattern('null')(new FormControl(null))).toEqual(null); });
() => { expect(Validators.pattern('null')(new FormControl(null))).toBeNull(); });
it('should not error on valid strings', () => {
expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toEqual(null);
});
it('should not error on valid strings',
() => { expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toBeNull(); });
it('should error on failure to match string', () => {
expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaa0'))).toEqual({
'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', () => {
@ -127,7 +126,7 @@ export function main() {
it('should return null when no errors', () => {
var c = Validators.compose([Validators.nullValidator, Validators.nullValidator]);
expect(c(new FormControl(''))).toEqual(null);
expect(c(new FormControl(''))).toBeNull();
});
it('should ignore nulls', () => {
@ -154,7 +153,7 @@ export function main() {
}
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(() => {
var c = Validators.composeAsync([
@ -187,7 +186,7 @@ export function main() {
(<Promise<any>>c(new FormControl('expected'))).then(v => value = v);
tick(1);
expect(value).toEqual(null);
expect(value).toBeNull();
}));
it('should ignore nulls', fakeAsync(() => {

View File

@ -12,7 +12,7 @@ import {Observer} from 'rxjs/Observer';
import {ResponseOptions} from '../base_response_options';
import {ReadyState, RequestMethod, ResponseType} from '../enums';
import {StringWrapper, isPresent} from '../facade/lang';
import {isPresent} from '../facade/lang';
import {Connection, ConnectionBackend} from '../interfaces';
import {Request} from '../static_request';
import {Response} from '../static_response';
@ -75,7 +75,7 @@ export class JSONPConnection_ extends JSONPConnection {
let callback = _dom.requestCallback(this._id);
let url: string = req.url;
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) {
url = url.substring(0, url.length - '=JSONP_CALLBACK'.length) + `=${callback}`;
}

View File

@ -49,19 +49,16 @@ export class Headers {
}
if (headers instanceof Headers) {
headers._headers.forEach((value: string[], name: string) => {
const lcName = name.toLowerCase();
this._headers.set(lcName, value);
this.mayBeSetNormalizedName(name);
headers._headers.forEach((values: string[], name: string) => {
values.forEach(value => this.append(name, value));
});
return;
}
Object.keys(headers).forEach((name: string) => {
const value = headers[name];
const lcName = name.toLowerCase();
this._headers.set(lcName, Array.isArray(value) ? value : [value]);
this.mayBeSetNormalizedName(name);
const values: string[] = Array.isArray(headers[name]) ? headers[name] : [headers[name]];
this.delete(name);
values.forEach(value => this.append(name, value));
});
}
@ -88,7 +85,12 @@ export class Headers {
*/
append(name: string, value: string): void {
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.
*/
set(name: string, value: string|string[]): void {
const strValue = Array.isArray(value) ? value.join(',') : value;
this._headers.set(name.toLowerCase(), [strValue]);
if (Array.isArray(value)) {
if (value.length) {
this._headers.set(name.toLowerCase(), [value.join(',')]);
}
} else {
this._headers.set(name.toLowerCase(), [value]);
}
this.mayBeSetNormalizedName(name);
}

View File

@ -6,7 +6,7 @@
* 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 {ContentType, RequestMethod, ResponseContentType} from './enums';
@ -82,7 +82,7 @@ export class Request extends Body {
let search = requestOptions.search.toString();
if (search.length > 0) {
let prefix = '?';
if (StringWrapper.contains(this.url, '?')) {
if (this.url.indexOf('?') != -1) {
prefix = (this.url[this.url.length - 1] == '&') ? '' : '&';
}
// TODO: just delete search-query-looking string in url?

View File

@ -37,8 +37,25 @@ export function main() {
secondHeaders.append('Content-Type', 'image/jpeg');
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()', () => {
it('should clear all values and re-set for the provided key', () => {
@ -112,6 +129,14 @@ export function main() {
});
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', () => {
const headers = new Headers();
@ -133,7 +158,6 @@ export function main() {
ref = {'Accept': values};
});
it('should be serializable with toJSON',
() => { expect(JSON.stringify(headers)).toEqual(JSON.stringify(ref)); });
@ -143,10 +167,8 @@ export function main() {
expect(JSON.stringify(parsedHeaders)).toEqual(JSON.stringify(recreatedHeaders));
});
});
});
describe('.fromResponseHeaderString()', () => {
it('should parse a response header string', () => {
const response = `Date: Fri, 20 Nov 2015 01:45:26 GMT\n` +
`Content-Type: application/json; charset=utf-8\n` +
@ -159,4 +181,5 @@ export function main() {
expect(headers.get('Connection')).toEqual('keep-alive');
});
});
});
}

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