Compare commits
433 Commits
2.0.0-rc.3
...
2.0.0-rc.5
Author | SHA1 | Date | |
---|---|---|---|
ebcd14f8e9 | |||
b65f66feff | |||
dd68ae3ef1 | |||
1b04d70626 | |||
01bca41168 | |||
94e1ab33ce | |||
0b08dd8674 | |||
b2b47177cd | |||
f08257ff4a | |||
d1f4222c83 | |||
d21331e902 | |||
37f138e83d | |||
5dab0bad3c | |||
85e70a4cde | |||
6b564ecda5 | |||
d4cceff0ef | |||
a415613457 | |||
1a41bd1ca4 | |||
5a99393355 | |||
afcb3c0035 | |||
d2d36c61f3 | |||
0bd97ecda2 | |||
46bbcefb36 | |||
74b57dfa7d | |||
8c9c0986e9 | |||
4028fcaa51 | |||
7a8ef1eae5 | |||
e811a5d97f | |||
df44e3e425 | |||
cdb1a237e5 | |||
fcafdff10b | |||
c586656d43 | |||
3a307c2794 | |||
4f17dbc721 | |||
99989f5d3f | |||
6baf3baedd | |||
0d1f3c3b07 | |||
83e2d3d1cb | |||
b4613ab2d2 | |||
0ca05eee45 | |||
26c9e1dc70 | |||
797cb5ae7b | |||
63b82cd730 | |||
2b704f0586 | |||
ce5ba80792 | |||
8b18ef4ba2 | |||
cd18de7a21 | |||
fd19671c07 | |||
3fcd6fd93f | |||
c8d53d71a3 | |||
790362e243 | |||
2eda7a5293 | |||
9925aa89dc | |||
6195a45ae2 | |||
422d380b3e | |||
550ab31bd0 | |||
5fceb21549 | |||
29caa37943 | |||
8efbcc996a | |||
91c64d2b8d | |||
82e7ecd611 | |||
3d53b33391 | |||
34624b2db2 | |||
1ab5eb0844 | |||
630028350a | |||
7e4fd7d7da | |||
af2e80e068 | |||
8e6091de6c | |||
ecdaded25f | |||
c161ed415d | |||
ff3b71f7b3 | |||
16cc9b46aa | |||
3ce11ed58c | |||
7db75fa361 | |||
d6d4568830 | |||
a55d796c4b | |||
73f02c7861 | |||
8c8754e573 | |||
c977a906b3 | |||
e0eea6c2f4 | |||
3e377f520e | |||
8dc82a0080 | |||
4624a35845 | |||
8d4499959a | |||
2dfc9c653b | |||
106db0aba8 | |||
28c4852cd6 | |||
13c8211065 | |||
e73d0511cf | |||
e18626b7a2 | |||
4df7b1cfbc | |||
f9573ece41 | |||
93ade740e2 | |||
a46437c57d | |||
633c7d1ebe | |||
251953218c | |||
0d6cc17252 | |||
c8989c900f | |||
6134320f16 | |||
7f647822bd | |||
e34a04d2ad | |||
44093905e2 | |||
3e2900f74b | |||
11fd2eccec | |||
0eee1d5de3 | |||
1b77604ee2 | |||
cc5cfe87c3 | |||
48f230a951 | |||
2be50bdbb0 | |||
f7258ea52a | |||
28e8b2faab | |||
3c3e9ddb10 | |||
5162fb6d52 | |||
2fdb39e60a | |||
43c71ae103 | |||
f0bd528d77 | |||
50a024b42f | |||
b48f7bcb8d | |||
763ca60f5b | |||
0eca7abdd8 | |||
d0a95e35af | |||
acc6c8d0b7 | |||
3dbc66c1ac | |||
4ad6bcce54 | |||
0988cc82b0 | |||
20b03bad11 | |||
81d27daf0d | |||
bb8b82b3f5 | |||
915a6666f8 | |||
72da547d6a | |||
7c76a75452 | |||
a32c4ad2f0 | |||
fb3608aa5d | |||
0a46f37444 | |||
9b39e499ac | |||
a67cc8229d | |||
b58e9ea775 | |||
422effdd18 | |||
3b690b68a6 | |||
43349dd373 | |||
e44e8668ea | |||
69e72c0786 | |||
553344739c | |||
367f0fd142 | |||
58d9e7fc5a | |||
9d9e9c6ff1 | |||
ba88db5141 | |||
62e7c0f464 | |||
fc83bbbe98 | |||
482c019199 | |||
b449467940 | |||
0aba42ae5b | |||
0d1bf8148b | |||
b42411ba1f | |||
5a21f168d6 | |||
00b726f695 | |||
f02da4e91a | |||
d6b65db9a7 | |||
6f4e49ed53 | |||
46b212706b | |||
ca16fc29a6 | |||
9edea0b139 | |||
d15a1d64e1 | |||
c87847974a | |||
6f68330fa5 | |||
2b63330a36 | |||
06e4ca4bb3 | |||
43437c175a | |||
8d90a5a4cf | |||
93a4ca652a | |||
41178367d1 | |||
54f2edbb90 | |||
b652a7fc9f | |||
e34eb4520f | |||
190bcc89c1 | |||
64fc4648b7 | |||
bdb59129d0 | |||
d455942389 | |||
cdb3678fe3 | |||
e73ac1e992 | |||
51f3d22e4f | |||
00aa7a76b6 | |||
27b87ef535 | |||
44709e0dca | |||
31a7709ece | |||
a441b5b8fe | |||
76b8a49bfb | |||
db54a84d14 | |||
eb6ff65af7 | |||
23ee29b6a2 | |||
73a69895d8 | |||
2799e7a3ca | |||
b43f95435b | |||
450f61d384 | |||
f3dd91e1d7 | |||
979946c062 | |||
51e661eb74 | |||
921a17960c | |||
7a4f6621ed | |||
83bc5c97ef | |||
3f08efa35d | |||
0914dc35e8 | |||
b6746cce9c | |||
8cd97c2054 | |||
32d8cde9c6 | |||
1803ed2512 | |||
f08060b0b0 | |||
b77a4a40a4 | |||
e1109d52e1 | |||
0668ba50e8 | |||
44ff005ce3 | |||
aa88438b54 | |||
85be729c70 | |||
a5dc5705a3 | |||
9e3d13f61f | |||
961c9d48ae | |||
9229bbbc80 | |||
34feecf60e | |||
4c762a6be3 | |||
0426325ef7 | |||
0b54e3cf0a | |||
5cf58971f1 | |||
6518ff88b2 | |||
42b0c1d8a2 | |||
4a965052f9 | |||
5725c5925c | |||
a46291b67c | |||
4ac76ca281 | |||
e7a8e2757b | |||
1266460386 | |||
0ccb6e0dfc | |||
3050ae155c | |||
402fd934d0 | |||
6c86e8d80a | |||
60e6f91a53 | |||
e676fded21 | |||
25e070dd65 | |||
da8eb9f8b8 | |||
806a25413c | |||
5af1e891cd | |||
79eda30f0f | |||
6d02d2f107 | |||
ded518d47f | |||
27436270fd | |||
b4ea0b1601 | |||
7b31178546 | |||
2ff83324af | |||
4ef86891a3 | |||
eb5763c23f | |||
d1a3e3aff1 | |||
93d0a01d3d | |||
9af2d8b810 | |||
94dc632a6d | |||
29231877e6 | |||
e68252a79b | |||
4ec2a30942 | |||
e6b24437a9 | |||
a05f7b2d76 | |||
61e18434d3 | |||
57473e72ec | |||
c3bdd504d0 | |||
daa9da4047 | |||
245b0910ed | |||
a77db44129 | |||
34b3c534e7 | |||
fa47890032 | |||
d84a43c828 | |||
9a1babb30c | |||
30a332ee36 | |||
426b002897 | |||
2de8364de2 | |||
eacc9e6541 | |||
749dec7dfb | |||
96a9e66616 | |||
46e105f3ab | |||
f7a0e9ecb6 | |||
93025d1bc6 | |||
98d49d4ce3 | |||
1426f680f5 | |||
c7fc51a185 | |||
b7e69bc1a1 | |||
72544ba551 | |||
c43dd5a655 | |||
7f4954bed6 | |||
f1fc1dc669 | |||
cbe85a0893 | |||
7073cf74fe | |||
9d265b6f61 | |||
776a83f9da | |||
f29457f3f0 | |||
a005d1595e | |||
8d746e3f67 | |||
37e6da6dfb | |||
8aa2a0c1b2 | |||
6bfd514caf | |||
ad3f18c0dd | |||
39d04b4a15 | |||
6fbe56dbf2 | |||
8ebb8e44c8 | |||
6fcf962fb5 | |||
2708ce6a17 | |||
30bec78da3 | |||
9a04fcd061 | |||
ae62f082fd | |||
9cc3b2ca9e | |||
3fe1cb0253 | |||
0ed7773223 | |||
3f55aa609f | |||
74b45dfbf8 | |||
5c9f871b21 | |||
77dc6ef411 | |||
5eca6e4e40 | |||
0c65d5cf2b | |||
f65ebec3ed | |||
81bf3f66ca | |||
3cbded6694 | |||
137fff9632 | |||
695c08b9dd | |||
119794249b | |||
afb72164e4 | |||
9fee5630fd | |||
01de58d650 | |||
dabf214f17 | |||
fb2539e1d5 | |||
ad9f02a73e | |||
2d73583253 | |||
73f017bad9 | |||
055282f156 | |||
fe7de53b89 | |||
17e4cfc748 | |||
1608d91728 | |||
a3b90411aa | |||
5781b96490 | |||
f208ee0d57 | |||
8aa388de6c | |||
51d4c9dcbd | |||
e81dea695c | |||
3fec27961e | |||
3784696b9e | |||
8c45aebc18 | |||
810c722413 | |||
e2116c53f3 | |||
296a447e3c | |||
0961bd1eff | |||
9340e1b065 | |||
ae4fa56ee9 | |||
2d9d7f1310 | |||
5ee84fe0f6 | |||
1620426393 | |||
bf598d6b8b | |||
24eb8389d2 | |||
fcfddbf79c | |||
dc64e90ab9 | |||
e12b1277df | |||
797914e948 | |||
e0b0a594bb | |||
ed0ade6f34 | |||
5cc7b41f39 | |||
f2f1ec0117 | |||
e913d9954d | |||
d20488752b | |||
855f3afb28 | |||
3f44377f2f | |||
90295e3252 | |||
a620f95891 | |||
db66509e66 | |||
6605eb30e9 | |||
3644eef860 | |||
fb2509675d | |||
eef9512ce6 | |||
9f00a1b902 | |||
c369bc747d | |||
c03e1f2f59 | |||
17dcbf66b9 | |||
40b907a657 | |||
a33195dcf3 | |||
c693c03f1d | |||
de127109f9 | |||
83208983b3 | |||
327d04c9c6 | |||
54edce2bab | |||
1a145ac500 | |||
9f978cf49d | |||
41b781107b | |||
dcf75126bf | |||
1143b0389a | |||
97a2119596 | |||
fbd2dd9ca2 | |||
f463e09b9f | |||
42a5b6cbda | |||
0ad1215a92 | |||
7733c97df3 | |||
8a9e9c7bd3 | |||
3d8eb8cbca | |||
894747c34c | |||
8d5a312585 | |||
8eb81b3741 | |||
22d8f73bc9 | |||
249a6bdd98 | |||
3ad81b1beb | |||
5ab0534164 | |||
5150344213 | |||
98cef76931 | |||
6c5b653593 | |||
9ed8f2d26e | |||
33a2f86b28 | |||
fed1672a43 | |||
54dbed4f48 | |||
df759b8d4b | |||
6edf0474cc | |||
826f89f862 | |||
c43aec2182 | |||
ae75e3640a | |||
a5f2cc73f6 | |||
e1e5c40ef7 | |||
6420f75320 | |||
5face35ae5 | |||
398060d5ff | |||
638fd744aa | |||
098b461b69 | |||
9decc3d823 | |||
a5f2e205ef | |||
8899b83927 | |||
f6a410a4a8 | |||
3d5bb23184 | |||
ef37d2ae0b | |||
2eb234bc63 | |||
758ee95880 | |||
40e1112a8e | |||
397f5e2390 | |||
1a212259af | |||
f114dd300b | |||
5954a26bce |
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -3,3 +3,6 @@
|
||||
|
||||
# JS files must always use LF for tools to work
|
||||
*.js eol=lf
|
||||
|
||||
# Must keep Windows line ending to be parsed correctly
|
||||
scripts/windows/packages.txt eol=crlf
|
||||
|
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@ -30,4 +30,4 @@ If the current behavior is a bug or you can illustrate your feature request bett
|
||||
|
||||
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
|
||||
|
||||
* **Language:** [all | TypeScript X.X | ES6/7 | ES5 | Dart]
|
||||
* **Language:** [all | TypeScript X.X | ES6/7 | ES5]
|
||||
|
736
CHANGELOG.md
736
CHANGELOG.md
@ -1,3 +1,729 @@
|
||||
<a name="2.0.0-rc.5"></a>
|
||||
# [2.0.0-rc.5](https://github.com/angular/angular/compare/2.0.0-rc.4...2.0.0-rc.5) (2016-08-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** change trigger binding syntax to function as a property binding [] ([7f4954b](https://github.com/angular/angular/commit/7f4954b))
|
||||
* **animations:** ensure a null easing value is never used with web-animations ([9cc3b2c](https://github.com/angular/angular/commit/9cc3b2c)), closes [#9780](https://github.com/angular/angular/issues/9780) [#9752](https://github.com/angular/angular/issues/9752)
|
||||
* **animations:** ensure all child elements are rendered before running animations ([c3bdd50](https://github.com/angular/angular/commit/c3bdd50)), closes [#9402](https://github.com/angular/angular/issues/9402) [#9775](https://github.com/angular/angular/issues/9775) [#9887](https://github.com/angular/angular/issues/9887)
|
||||
* **animations:** ensure animation detection doesn't rely on the body node ([0d1bf81](https://github.com/angular/angular/commit/0d1bf81)), closes [#10230](https://github.com/angular/angular/issues/10230) [#10191](https://github.com/angular/angular/issues/10191) [#10273](https://github.com/angular/angular/issues/10273)
|
||||
* **animations:** throw errors when duplicate component trigger names are registered ([5af1e89](https://github.com/angular/angular/commit/5af1e89))
|
||||
* **compiler:** allow to use pipes inside of `*ngIf` ([#10452](https://github.com/angular/angular/issues/10452)) ([8efbcc9](https://github.com/angular/angular/commit/8efbcc9)), closes [#9746](https://github.com/angular/angular/issues/9746)
|
||||
* **compiler:** auto declare `entryComponents` recursively ([a32c4ad](https://github.com/angular/angular/commit/a32c4ad)), closes [#10348](https://github.com/angular/angular/issues/10348)
|
||||
* **compiler:** Collector collects enum values. ([#9967](https://github.com/angular/angular/issues/9967)) ([e6b2443](https://github.com/angular/angular/commit/e6b2443))
|
||||
* **compiler:** Fixed ?. operator to short-circut execution ([#9965](https://github.com/angular/angular/issues/9965)) ([4ec2a30](https://github.com/angular/angular/commit/4ec2a30)), closes [#9965](https://github.com/angular/angular/issues/9965)
|
||||
* **compiler:** Generates function expressions as returning any ([#9980](https://github.com/angular/angular/issues/9980)) ([eb5763c](https://github.com/angular/angular/commit/eb5763c))
|
||||
* **compiler:** Ignore references to declared modules and unneeded types ([#9776](https://github.com/angular/angular/issues/9776)) ([4ef8689](https://github.com/angular/angular/commit/4ef8689))
|
||||
* **compiler:** Missing metadata files should result in undefined ([#9704](https://github.com/angular/angular/issues/9704)) ([30bec78](https://github.com/angular/angular/commit/30bec78)), closes [#9678](https://github.com/angular/angular/issues/9678)
|
||||
* **compiler:** No longer writes 0 length files outside of genDir ([#10023](https://github.com/angular/angular/issues/10023)) ([6518ff8](https://github.com/angular/angular/commit/6518ff8))
|
||||
* **compiler:** Query expression lambdas should have dynamic type ([961c9d4](https://github.com/angular/angular/commit/961c9d4))
|
||||
* **compiler:** report better error messages for `host` bindings ([fb3608a](https://github.com/angular/angular/commit/fb3608a)), closes [#10346](https://github.com/angular/angular/issues/10346)
|
||||
* **compiler:** Report references to non-exported symbols. ([9925aa8](https://github.com/angular/angular/commit/9925aa8))
|
||||
* **compiler:** StaticReflect now resolves re-exported symbols ([#10453](https://github.com/angular/angular/issues/10453)) ([82e7ecd](https://github.com/angular/angular/commit/82e7ecd)), closes [#10453](https://github.com/angular/angular/issues/10453)
|
||||
* **compiler:** treat custom elements as unknown elements by default ([fc83bbb](https://github.com/angular/angular/commit/fc83bbb)), closes [#10300](https://github.com/angular/angular/issues/10300)
|
||||
* **compiler:** Catch exceptions in the logging of binding update ([2743627](https://github.com/angular/angular/commit/2743627)), closes [#9994](https://github.com/angular/angular/issues/9994)
|
||||
* **compiler:** Better error message in case of unknown property binding ([a55d796](https://github.com/angular/angular/commit/a55d796))
|
||||
* **compiler:** String.split(str, n) stops after n separator ([#10408](https://github.com/angular/angular/issues/10408)) ([13c8211](https://github.com/angular/angular/commit/13c8211))
|
||||
* **compiler-cli:** put all `ngc` files into a single directory ([#10486](https://github.com/angular/angular/issues/10486)) ([790362e](https://github.com/angular/angular/commit/790362e))
|
||||
* **compiler-cli:** support trailing slash in basePath ([#10533](https://github.com/angular/angular/issues/10533)) ([0d1f3c3](https://github.com/angular/angular/commit/0d1f3c3))
|
||||
* **core:** allow module providers to overwrite providers from `ModuleWithProviders` ([5533447](https://github.com/angular/angular/commit/5533447)), closes [#10313](https://github.com/angular/angular/issues/10313) [#10317](https://github.com/angular/angular/issues/10317)
|
||||
* **core:** Don't use ES6 spread operator when undefined is allowed. ([2ff8332](https://github.com/angular/angular/commit/2ff8332))
|
||||
* **core:** ensure ngFor only inserts/moves/removes elements when necessary ([#10287](https://github.com/angular/angular/issues/10287)) ([e18626b](https://github.com/angular/angular/commit/e18626b)), closes [#9960](https://github.com/angular/angular/issues/9960) [#7239](https://github.com/angular/angular/issues/7239) [#9672](https://github.com/angular/angular/issues/9672) [#9454](https://github.com/angular/angular/issues/9454) [#10287](https://github.com/angular/angular/issues/10287)
|
||||
* **core:** fix offline detection in ng_module_factory_loader ([915a666](https://github.com/angular/angular/commit/915a666))
|
||||
* **core:** only warn and auto declare undeclared `entryComponents`. ([e44e866](https://github.com/angular/angular/commit/e44e866)), closes [#10316](https://github.com/angular/angular/issues/10316)
|
||||
* **core:** support components without a selector ([#10331](https://github.com/angular/angular/issues/10331)) ([9b39e49](https://github.com/angular/angular/commit/9b39e49)), closes [#3464](https://github.com/angular/angular/issues/3464) [#10216](https://github.com/angular/angular/issues/10216)
|
||||
* **CurrencyPipe:** use default Intl formatting options when none provided ([d455942](https://github.com/angular/angular/commit/d455942)), closes [#10189](https://github.com/angular/angular/issues/10189)
|
||||
* **datePipe:** short timezone not displayed, closes [#9812](https://github.com/angular/angular/issues/9812) ([#9816](https://github.com/angular/angular/issues/9816)) ([f29457f](https://github.com/angular/angular/commit/f29457f)), closes [#9812](https://github.com/angular/angular/issues/9812) [#9816](https://github.com/angular/angular/issues/9816)
|
||||
* **DirectiveResolver:** throw on duplicate Input & Output names ([d1a3e3a](https://github.com/angular/angular/commit/d1a3e3a))
|
||||
* **docs:** typo in comments ([#9743](https://github.com/angular/angular/issues/9743)) ([afb7216](https://github.com/angular/angular/commit/afb7216))
|
||||
* **ExpressionParser:** undefined is undefined (was null) ([b4613ab](https://github.com/angular/angular/commit/b4613ab))
|
||||
* **fake_async:** share zone between `beforeEach` and `it` ([16cc9b4](https://github.com/angular/angular/commit/16cc9b4))
|
||||
* **forms:** allow arrays as parents ([#10440](https://github.com/angular/angular/issues/10440)) ([d6d4568](https://github.com/angular/angular/commit/d6d4568)), closes [#10432](https://github.com/angular/angular/issues/10432)
|
||||
* **forms:** export AbstractFormGroupDirective ([6195a45](https://github.com/angular/angular/commit/6195a45))
|
||||
* **forms:** export form directive arrays for offline compile ([#9893](https://github.com/angular/angular/issues/9893)) ([93025d1](https://github.com/angular/angular/commit/93025d1))
|
||||
* **forms:** improve ngModel error messages ([#10314](https://github.com/angular/angular/issues/10314)) ([43349dd](https://github.com/angular/angular/commit/43349dd))
|
||||
* **forms:** improve no value accessor error message ([#10051](https://github.com/angular/angular/issues/10051)) ([34feecf](https://github.com/angular/angular/commit/34feecf))
|
||||
* **forms:** mark control containers as touched when child controls are touched ([#9735](https://github.com/angular/angular/issues/9735)) ([77dc6ef](https://github.com/angular/angular/commit/77dc6ef))
|
||||
* **forms:** normalize written value in NumberValueAccessor ([b48f7bc](https://github.com/angular/angular/commit/b48f7bc)), closes [#10379](https://github.com/angular/angular/issues/10379)
|
||||
* **forms:** re-enable form provider functions for easier migration ([#9972](https://github.com/angular/angular/issues/9972)) ([e68252a](https://github.com/angular/angular/commit/e68252a))
|
||||
* **forms:** throw error if wrong control container for reactive forms ([#10286](https://github.com/angular/angular/issues/10286)) ([0aba42a](https://github.com/angular/angular/commit/0aba42a))
|
||||
* **forms:** update dirty before emitting value change ([#10362](https://github.com/angular/angular/issues/10362)) ([7c76a75](https://github.com/angular/angular/commit/7c76a75)), closes [#5328](https://github.com/angular/angular/issues/5328)
|
||||
* **forms:** missing export for validators ([91c64d2](https://github.com/angular/angular/commit/91c64d2))
|
||||
* **forms:** use change event for select multiple ([#9713](https://github.com/angular/angular/issues/9713)) ([3cbded6](https://github.com/angular/angular/commit/3cbded6))
|
||||
* **HtmlParser:** correctly propagate the interpolation config across layers ([25e070d](https://github.com/angular/angular/commit/25e070d))
|
||||
* **http:** convert objects passed to requests into a string ([#10124](https://github.com/angular/angular/issues/10124)) ([83bc5c9](https://github.com/angular/angular/commit/83bc5c9)), closes [#10073](https://github.com/angular/angular/issues/10073)
|
||||
* **http:** headers should be case-insensitive. ([7f64782](https://github.com/angular/angular/commit/7f64782)), closes [#9452](https://github.com/angular/angular/issues/9452)
|
||||
* **http:** URLSearchParams.clone propagate the QueryEncoder ([#9900](https://github.com/angular/angular/issues/9900)) ([2519532](https://github.com/angular/angular/commit/2519532))
|
||||
* **i18n extractor:** array manipulation ([df44e3e](https://github.com/angular/angular/commit/df44e3e))
|
||||
* **KeyValueDiffer:** check for changes ([3f08efa](https://github.com/angular/angular/commit/3f08efa)), closes [#9115](https://github.com/angular/angular/issues/9115)
|
||||
* **linker:** prevent pollution of empty embeddedView context ([#10548](https://github.com/angular/angular/issues/10548)) ([46bbcef](https://github.com/angular/angular/commit/46bbcef)), closes [#10045](https://github.com/angular/angular/issues/10045)
|
||||
* **linker/compiler:** rename const to avoid duplicate declaration ([#10457](https://github.com/angular/angular/issues/10457)) ([2b704f0](https://github.com/angular/angular/commit/2b704f0))
|
||||
* **metadata:** fix typechecking with typescript[@next](https://github.com/next) ([0a46f37](https://github.com/angular/angular/commit/0a46f37))
|
||||
* **ng upgrade:** do not compile ng2 components until after ng1 bootstrap ([#10084](https://github.com/angular/angular/issues/10084)) ([9edea0b](https://github.com/angular/angular/commit/9edea0b)), closes [#9407](https://github.com/angular/angular/issues/9407) [angular/protractor#2944](https://github.com/angular/protractor/issues/2944)
|
||||
* **ngc:** gather metadata for OpaqueToken ([c8d53d7](https://github.com/angular/angular/commit/c8d53d7)), closes [#10482](https://github.com/angular/angular/issues/10482)
|
||||
* **ngClass:** do not deconstruct classes on element removal ([#10303](https://github.com/angular/angular/issues/10303)) ([ba88db5](https://github.com/angular/angular/commit/ba88db5)), closes [#10008](https://github.com/angular/angular/issues/10008) [#10303](https://github.com/angular/angular/issues/10303)
|
||||
* **NgPlural:** expression inside cases ([#9883](https://github.com/angular/angular/issues/9883)) ([b7e69bc](https://github.com/angular/angular/commit/b7e69bc)), closes [#9868](https://github.com/angular/angular/issues/9868)
|
||||
* **NgStyle:** remove duplicate input declaration ([#9978](https://github.com/angular/angular/issues/9978)) ([94dc632](https://github.com/angular/angular/commit/94dc632)), closes [#9977](https://github.com/angular/angular/issues/9977)
|
||||
* **ngUpgrade:** to work with [@NgModule](https://github.com/NgModule) ([d21331e](https://github.com/angular/angular/commit/d21331e))
|
||||
* **platform-browser:** IEMobile is badly detected when testing ([#10382](https://github.com/angular/angular/issues/10382)) ([43c71ae](https://github.com/angular/angular/commit/43c71ae))
|
||||
* **platform-browser:** remove testing_e2e target ([#10029](https://github.com/angular/angular/issues/10029)) ([4a96505](https://github.com/angular/angular/commit/4a96505))
|
||||
* **platform-browser:** throw useful error on missing platform module. ([73f02c7](https://github.com/angular/angular/commit/73f02c7))
|
||||
* **platform-browser-dynamic:** Add [@Injectable](https://github.com/Injectable)() annotation to XHRImpl. ([7b31178](https://github.com/angular/angular/commit/7b31178))
|
||||
* **router:** absolute redirects should work with lazy loading ([3a307c2](https://github.com/angular/angular/commit/3a307c2))
|
||||
* **router:** add segmentPath to the link DSL ([4f17dbc](https://github.com/angular/angular/commit/4f17dbc))
|
||||
* **router:** advance query params and fragment after advanced routes ([06e4ca4](https://github.com/angular/angular/commit/06e4ca4))
|
||||
* **router:** back button does not work in IE11 and Safari ([f08060b](https://github.com/angular/angular/commit/f08060b))
|
||||
* **router:** configure DI correctly when using the old `provideRouter` function ([93ade74](https://github.com/angular/angular/commit/93ade74))
|
||||
* **router:** disallow root segments with matrix params ([34b3c53](https://github.com/angular/angular/commit/34b3c53))
|
||||
* **router:** do not fire events on 'duplicate' location events ([0b54e3c](https://github.com/angular/angular/commit/0b54e3c))
|
||||
* **router:** encode/decode params and path segments ([46e105f](https://github.com/angular/angular/commit/46e105f))
|
||||
* **router:** export navigation extras ([51e661e](https://github.com/angular/angular/commit/51e661e))
|
||||
* **router:** expose initalNavigation and dispose so they can be used with webworkers ([b77a4a4](https://github.com/angular/angular/commit/b77a4a4))
|
||||
* **router:** fix matrix params check to handle 'special' objects ([d2d36c6](https://github.com/angular/angular/commit/d2d36c6))
|
||||
* **router:** fix offline compilation by exporting provideLocationStrategy ([8dc82a0](https://github.com/angular/angular/commit/8dc82a0))
|
||||
* **router:** fix rollup config to properly set up rxjs ([1803ed2](https://github.com/angular/angular/commit/1803ed2))
|
||||
* **router:** fix RouterLinKActive to work with RouterLink ([f7a0e9e](https://github.com/angular/angular/commit/f7a0e9e))
|
||||
* **router:** fix type definition ([c586656](https://github.com/angular/angular/commit/c586656))
|
||||
* **router:** freeze params and queryParams to prevent common source of bugs ([0668ba5](https://github.com/angular/angular/commit/0668ba5))
|
||||
* **router:** handle lastPathIndex of empty-path routes ([7a4f662](https://github.com/angular/angular/commit/7a4f662))
|
||||
* **router:** handle router outlets in ngIf ([0c65d5c](https://github.com/angular/angular/commit/0c65d5c))
|
||||
* **router:** handle url fragments when no url segments present ([43437c1](https://github.com/angular/angular/commit/43437c1))
|
||||
* **router:** handle urls with only secondary top-level segments ([44709e0](https://github.com/angular/angular/commit/44709e0))
|
||||
* **router:** handle when both primary and secondary are empty-path and primary has a child ([2b63330](https://github.com/angular/angular/commit/2b63330))
|
||||
* **router:** lazily-loaded modules should use loaded injectors instead of the root one ([85be729](https://github.com/angular/angular/commit/85be729))
|
||||
* **router:** lazy loaded components should use loaded injector ([921a179](https://github.com/angular/angular/commit/921a179))
|
||||
* **router:** make an outlet to unregister itself when it is removed from the dom ([3e377f5](https://github.com/angular/angular/commit/3e377f5))
|
||||
* **router:** make router provides work with cli and offline compilation ([d15a1d6](https://github.com/angular/angular/commit/d15a1d6))
|
||||
* **router:** merge SystemJsAppModuleFactoryLoader and SystemJsAllModuleLoader ([0426325](https://github.com/angular/angular/commit/0426325))
|
||||
* **router:** navigation should not preserve query params and fragment by default ([23ee29b](https://github.com/angular/angular/commit/23ee29b))
|
||||
* **router:** provideRouter should use provideRoutes ([#10488](https://github.com/angular/angular/issues/10488)) ([2eda7a5](https://github.com/angular/angular/commit/2eda7a5))
|
||||
* **router:** relax type defintion of Route to improve dev ergonomics ([bb8b82b](https://github.com/angular/angular/commit/bb8b82b))
|
||||
* **router:** remove a circular dep ([6bfd514](https://github.com/angular/angular/commit/6bfd514))
|
||||
* **router:** remove private and internal annotations ([#9745](https://github.com/angular/angular/issues/9745)) ([dabf214](https://github.com/angular/angular/commit/dabf214))
|
||||
* **router:** remove private and internal annotations ([#9753](https://github.com/angular/angular/issues/9753)) ([137fff9](https://github.com/angular/angular/commit/137fff9))
|
||||
* **router:** remove the precompile warning ([fb2539e](https://github.com/angular/angular/commit/fb2539e))
|
||||
* **router:** route.parent should work for secondary children ([5a99393](https://github.com/angular/angular/commit/5a99393))
|
||||
* **router:** router link active should take all descendants into account ([8d90a5a](https://github.com/angular/angular/commit/8d90a5a))
|
||||
* **router:** routerLinkActive should only set classes after the router has successfully navigated ([db54a84](https://github.com/angular/angular/commit/db54a84))
|
||||
* **router:** support outlets in non-absolute positions ([afcb3c0](https://github.com/angular/angular/commit/afcb3c0))
|
||||
* **router:** throw when cannot parse a url ([27b87ef](https://github.com/angular/angular/commit/27b87ef))
|
||||
* **router:** update current state and url before activating components ([5cf5897](https://github.com/angular/angular/commit/5cf5897))
|
||||
* **router:** update dts files ([81d27da](https://github.com/angular/angular/commit/81d27da))
|
||||
* **router:** update dts files ([a415613](https://github.com/angular/angular/commit/a415613))
|
||||
* **router:** update links when query params change ([f65ebec](https://github.com/angular/angular/commit/f65ebec))
|
||||
* **router:** updates router module to be offline-compilation friendly ([72da547](https://github.com/angular/angular/commit/72da547))
|
||||
* **static_reflector:** report methods with decorators in `propMetadata` as well ([367f0fd](https://github.com/angular/angular/commit/367f0fd)), closes [#10308](https://github.com/angular/angular/issues/10308) [#10318](https://github.com/angular/angular/issues/10318)
|
||||
* **static_reflector:** resolve values of functions in the function context ([d6b65db](https://github.com/angular/angular/commit/d6b65db))
|
||||
* **SyncAsyncResult:** fix default async value ([#10013](https://github.com/angular/angular/issues/10013)) ([6d02d2f](https://github.com/angular/angular/commit/6d02d2f)), closes [#10013](https://github.com/angular/angular/issues/10013)
|
||||
* **TemplateParser:** add support for data-template attribute ([d84a43c](https://github.com/angular/angular/commit/d84a43c)), closes [#9904](https://github.com/angular/angular/issues/9904)
|
||||
* **TemplateParser:** report empty expression ([#10391](https://github.com/angular/angular/issues/10391)) ([e73d051](https://github.com/angular/angular/commit/e73d051)), closes [#3754](https://github.com/angular/angular/issues/3754)
|
||||
* **testing:** add an explicit doAsyncPrecompilation step ([#10015](https://github.com/angular/angular/issues/10015)) ([b43f954](https://github.com/angular/angular/commit/b43f954)), closes [#9975](https://github.com/angular/angular/issues/9975) [#9593](https://github.com/angular/angular/issues/9593)
|
||||
* **testing:** Add platform directives to the shim that keeps setBaseTestProviders running ([#10154](https://github.com/angular/angular/issues/10154)) ([979946c](https://github.com/angular/angular/commit/979946c))
|
||||
* **testing:** ComponentFixture - Avoid extra scheduleMicrotask ([#10223](https://github.com/angular/angular/issues/10223)) ([e34eb45](https://github.com/angular/angular/commit/e34eb45))
|
||||
* **testing:** correctly import NgMatchers ([#10077](https://github.com/angular/angular/issues/10077)) ([64fc464](https://github.com/angular/angular/commit/64fc464))
|
||||
* **testing:** Fix error message in test bed ([3b690b6](https://github.com/angular/angular/commit/3b690b6))
|
||||
* **testing:** reintroduce and deprecate setBaseTestProviders ([#9905](https://github.com/angular/angular/issues/9905)) ([2923187](https://github.com/angular/angular/commit/2923187))
|
||||
* **testing:** remove deprecated testing APIs ([#9923](https://github.com/angular/angular/issues/9923)) ([9af2d8b](https://github.com/angular/angular/commit/9af2d8b))
|
||||
* **typescript:** make router compile with typescript[@next](https://github.com/next) ([73f017b](https://github.com/angular/angular/commit/73f017b)), closes [#9731](https://github.com/angular/angular/issues/9731)
|
||||
* **UrlParser:** stop setting default value 'true' ([#10399](https://github.com/angular/angular/issues/10399)) ([0d6cc17](https://github.com/angular/angular/commit/0d6cc17))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **core:** change bootstrap of modules and names of platforms ([5a21f16](https://github.com/angular/angular/commit/5a21f16))
|
||||
* **core:** change module semantics ([46b2127](https://github.com/angular/angular/commit/46b2127)), closes [#10164](https://github.com/angular/angular/issues/10164)
|
||||
* **core:** clean up platform bootstrap and initTestEnvironment ([fa47890](https://github.com/angular/angular/commit/fa47890)), closes [#9922](https://github.com/angular/angular/issues/9922)
|
||||
* **core:** deprecate `coreBootstrap`, `PLATFORM_PIPES/DIRECTIVES` providers and `ComponentResolver` ([daa9da4](https://github.com/angular/angular/commit/daa9da4)), closes [#9726](https://github.com/angular/angular/issues/9726)
|
||||
* **core:** deprecate old methods on `ApplicationRef` ([34624b2](https://github.com/angular/angular/commit/34624b2))
|
||||
* **core:** introduce `APP_BOOTSTRAP_LISTENER` multi provider ([af2e80e](https://github.com/angular/angular/commit/af2e80e))
|
||||
* **core:** Introduce `AppInitStatus` ([6300283](https://github.com/angular/angular/commit/6300283))
|
||||
* **core:** introduce `NgModule.schemas` ([00b726f](https://github.com/angular/angular/commit/00b726f))
|
||||
* **core:** make `lockRunMode` a noop and deprecate it. ([98d49d4](https://github.com/angular/angular/commit/98d49d4)), closes [#9878](https://github.com/angular/angular/issues/9878)
|
||||
* **core:** remove `ViewResolver` and `ViewResolverMock` ([0988cc8](https://github.com/angular/angular/commit/0988cc8))
|
||||
* **core:** rename `precompile` into `entryComponents`. ([6f4e49e](https://github.com/angular/angular/commit/6f4e49e))
|
||||
* **core:** use `ngOnDestroy` in providers ([8e6091d](https://github.com/angular/angular/commit/8e6091d))
|
||||
* **testing:** introduce new testing api to support ng modules ([d0a95e3](https://github.com/angular/angular/commit/d0a95e3)), closes [#10354](https://github.com/angular/angular/issues/10354)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **animations:** allow animation integration support into host params ([806a254](https://github.com/angular/angular/commit/806a254)), closes [#9044](https://github.com/angular/angular/issues/9044) [#9933](https://github.com/angular/angular/issues/9933)
|
||||
* **browser:** use AppModules for bootstrap in the browser ([3f55aa6](https://github.com/angular/angular/commit/3f55aa6))
|
||||
* **compiler:** add `MockPipeResolver` ([4ad6bcc](https://github.com/angular/angular/commit/4ad6bcc))
|
||||
* **compiler:** Added support for conditional expressions. ([#10366](https://github.com/angular/angular/issues/10366)) ([20b03ba](https://github.com/angular/angular/commit/20b03ba))
|
||||
* **compiler:** Added support for references to static fields. ([#10334](https://github.com/angular/angular/issues/10334)) ([b58e9ea](https://github.com/angular/angular/commit/b58e9ea))
|
||||
* **compiler:** Allow calls to simple static methods ([#10289](https://github.com/angular/angular/issues/10289)) ([b449467](https://github.com/angular/angular/commit/b449467))
|
||||
* **compiler:** Expression span information and error correction ([#9772](https://github.com/angular/angular/issues/9772)) ([9a04fcd](https://github.com/angular/angular/commit/9a04fcd))
|
||||
* **compiler:** introduce `MockDirectiveResolver.setDirective` ([acc6c8d](https://github.com/angular/angular/commit/acc6c8d))
|
||||
* **compiler:** Support default parameters in static reflector ([#10370](https://github.com/angular/angular/issues/10370)) ([763ca60](https://github.com/angular/angular/commit/763ca60))
|
||||
* **core:** add AddModuleFactoryLoader ([6fcf962](https://github.com/angular/angular/commit/6fcf962))
|
||||
* **core:** allow to add precompiled tokens via a provider ([7073cf7](https://github.com/angular/angular/commit/7073cf7)), closes [#9874](https://github.com/angular/angular/issues/9874)
|
||||
* **core:** introduce `[@AppModule](https://github.com/AppModule)` ([17e4cfc](https://github.com/angular/angular/commit/17e4cfc)), closes [#9730](https://github.com/angular/angular/issues/9730)
|
||||
* **core:** introduce `ModuleWithProviders`. ([f02da4e](https://github.com/angular/angular/commit/f02da4e))
|
||||
* **core:** introduce `NgModuleRef.destroy` and call `ngOnDestroy` on all providers ([ecdaded](https://github.com/angular/angular/commit/ecdaded))
|
||||
* **core:** support `ngOnDestroy` on providers of a directive. ([c161ed4](https://github.com/angular/angular/commit/c161ed4))
|
||||
* **ExpressionChangedAfterItHasBeenCheckedException:** more meaningful error message ([2de8364](https://github.com/angular/angular/commit/2de8364)), closes [#9882](https://github.com/angular/angular/issues/9882)
|
||||
* **ExpressionParser:** add support for `this` ([0ca05ee](https://github.com/angular/angular/commit/0ca05ee))
|
||||
* **facade:** add support for all thenables ([#10278](https://github.com/angular/angular/issues/10278)) ([58d9e7f](https://github.com/angular/angular/commit/58d9e7f))
|
||||
* **forms:** add ability to reset forms ([#9974](https://github.com/angular/angular/issues/9974)) ([da8eb9f](https://github.com/angular/angular/commit/da8eb9f)), closes [#4914](https://github.com/angular/angular/issues/4914) [#4933](https://github.com/angular/angular/issues/4933)
|
||||
* **forms:** add get method for easy access to child controls ([#10428](https://github.com/angular/angular/issues/10428)) ([8d44999](https://github.com/angular/angular/commit/8d44999))
|
||||
* **forms:** add invalid prop to abstract controls ([#10439](https://github.com/angular/angular/issues/10439)) ([e0eea6c](https://github.com/angular/angular/commit/e0eea6c))
|
||||
* **forms:** add modules for forms and deprecatedForms ([#9859](https://github.com/angular/angular/issues/9859)) ([9d265b6](https://github.com/angular/angular/commit/9d265b6)), closes [#9732](https://github.com/angular/angular/issues/9732)
|
||||
* **forms:** allow both patching and strict setting of values ([#10537](https://github.com/angular/angular/issues/10537)) ([fcafdff](https://github.com/angular/angular/commit/fcafdff))
|
||||
* **forms:** updateValue() for form groups and form arrays ([#9901](https://github.com/angular/angular/issues/9901)) ([30a332e](https://github.com/angular/angular/commit/30a332e)), closes [#9553](https://github.com/angular/angular/issues/9553)
|
||||
* **HtmlLexer:** better hint on unclosed ICU message errors ([4117836](https://github.com/angular/angular/commit/4117836)), closes [#10227](https://github.com/angular/angular/issues/10227)
|
||||
* **http:** add content-type override support for http request ([#10211](https://github.com/angular/angular/issues/10211)) ([bdb5912](https://github.com/angular/angular/commit/bdb5912))
|
||||
* **http:** add options method to Http ([#10540](https://github.com/angular/angular/issues/10540)) ([0bd97ec](https://github.com/angular/angular/commit/0bd97ec)), closes [#10500](https://github.com/angular/angular/issues/10500) [#7918](https://github.com/angular/angular/issues/7918)
|
||||
* **http:** add support for ArrayBuffer ([1266460](https://github.com/angular/angular/commit/1266460))
|
||||
* **http:** add support for blob as a response type ([#10190](https://github.com/angular/angular/issues/10190)) ([76b8a49](https://github.com/angular/angular/commit/76b8a49))
|
||||
* **i18n:** merge translations ([7a8ef1e](https://github.com/angular/angular/commit/7a8ef1e))
|
||||
* **i18n:** xmb serializer ([cc5cfe8](https://github.com/angular/angular/commit/cc5cfe8))
|
||||
* **i18n:** xtb serializer ([0eee1d5](https://github.com/angular/angular/commit/0eee1d5))
|
||||
* **I18nAst:** introduce an intermediate AST ([48f230a](https://github.com/angular/angular/commit/48f230a))
|
||||
* **ICU:** enable ICU extraction even when when in is not used ([3050ae1](https://github.com/angular/angular/commit/3050ae1))
|
||||
* **ICU:** extract ICU messages ([28e8b2f](https://github.com/angular/angular/commit/28e8b2f))
|
||||
* **NgStyle:** add support for the style.unit notation ([#10496](https://github.com/angular/angular/issues/10496)) ([8b18ef4](https://github.com/angular/angular/commit/8b18ef4)), closes [#10326](https://github.com/angular/angular/issues/10326)
|
||||
* **ngUpgrade:** add support for NgModules ([6b564ec](https://github.com/angular/angular/commit/6b564ec))
|
||||
* **NumberPipe:** add string support ([#10163](https://github.com/angular/angular/issues/10163)) ([f3dd91e](https://github.com/angular/angular/commit/f3dd91e)), closes [#10159](https://github.com/angular/angular/issues/10159)
|
||||
* **router:** activateroute should expose its route config ([2fdb39e](https://github.com/angular/angular/commit/2fdb39e))
|
||||
* **router:** add a validation to make sure pathMatch is set correctly ([3c3e9dd](https://github.com/angular/angular/commit/3c3e9dd))
|
||||
* **router:** add activate and deactivate events to RouterOutlet ([245b091](https://github.com/angular/angular/commit/245b091))
|
||||
* **router:** add isActive to router ([5162fb6](https://github.com/angular/angular/commit/5162fb6))
|
||||
* **router:** add parent, children, firstChild to ActivatedRoute ([550ab31](https://github.com/angular/angular/commit/550ab31))
|
||||
* **router:** add queryParams and fragment to every activated route ([422d380](https://github.com/angular/angular/commit/422d380))
|
||||
* **router:** add RouterAppModule ([8aa2a0c](https://github.com/angular/angular/commit/8aa2a0c))
|
||||
* **router:** add RouterTestModule ([72544ba](https://github.com/angular/angular/commit/72544ba))
|
||||
* **router:** add support for canActivateChild ([9e3d13f](https://github.com/angular/angular/commit/9e3d13f))
|
||||
* **router:** add support for lazily loaded modules ([8ebb8e4](https://github.com/angular/angular/commit/8ebb8e4))
|
||||
* **router:** add the ANALYZE_FOR_PRECOMPILE provider to make dev ergonomics better ([96a9e66](https://github.com/angular/angular/commit/96a9e66))
|
||||
* **router:** Allow navigation without updating the URL ([#9608](https://github.com/angular/angular/issues/9608)) ([63b82cd](https://github.com/angular/angular/commit/63b82cd))
|
||||
* **router:** empty-path routes should inherit matrix params ([a77db44](https://github.com/angular/angular/commit/a77db44))
|
||||
* **router:** guards and data resolvers can now return promises ([a5dc570](https://github.com/angular/angular/commit/a5dc570))
|
||||
* **router:** implement canLoad ([62e7c0f](https://github.com/angular/angular/commit/62e7c0f))
|
||||
* **router:** rename UrlPathWithParams into UrlSegment ([6f68330](https://github.com/angular/angular/commit/6f68330))
|
||||
* **router:** support sibling modules providing routes ([29caa37](https://github.com/angular/angular/commit/29caa37))
|
||||
* **router:** update routerLink DSL to handle aux routes ([ded518d](https://github.com/angular/angular/commit/ded518d))
|
||||
* **router:** update the example app to use lazily-loaded modules ([6fbe56d](https://github.com/angular/angular/commit/6fbe56d))
|
||||
* **Router:** add extra validation for when route was passed as Array ([#9942](https://github.com/angular/angular/issues/9942)) ([aa88438](https://github.com/angular/angular/commit/aa88438))
|
||||
* **security:** categorize <track src> as a regular URL. ([a441b5b](https://github.com/angular/angular/commit/a441b5b)), closes [#10089](https://github.com/angular/angular/issues/10089)
|
||||
* **security:** only warn when actually sanitizing HTML. ([#10272](https://github.com/angular/angular/issues/10272)) ([482c019](https://github.com/angular/angular/commit/482c019)), closes [#10206](https://github.com/angular/angular/issues/10206)
|
||||
* **security:** trust resource URLs as URLs. ([#10220](https://github.com/angular/angular/issues/10220)) ([51f3d22](https://github.com/angular/angular/commit/51f3d22))
|
||||
* **testing:** add implicit test module ([8d746e3](https://github.com/angular/angular/commit/8d746e3)), closes [#9846](https://github.com/angular/angular/issues/9846)
|
||||
* **xmb/xtb:** support dtd ([e34a04d](https://github.com/angular/angular/commit/e34a04d))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* core:
|
||||
|
||||
## Bootstrap changes
|
||||
```
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule({
|
||||
declarations: […], // directives, components, and pipes owned by this NgModule
|
||||
imports: [BrowserModule],
|
||||
providers: […], // additional providers
|
||||
boostrap: [MainComponent],
|
||||
})
|
||||
class MyAppModule {}
|
||||
|
||||
// Ahead of Time compile
|
||||
import {platformBrowser} from ‘@angular/platform-browser’;
|
||||
|
||||
platformBrowser().bootstrapModuleFactory(MyAppModuleNgFactory);
|
||||
|
||||
// JIT compile long form
|
||||
import {platformBrowserDynamic} from ‘@angular/platform-browser-dynamic’;
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(MyAppModule);
|
||||
```
|
||||
* browser:
|
||||
- short form bootstrap does no longer allow
|
||||
to inject compiler internals (i.e. everything
|
||||
from `@angular/compiler`). Inject `Compiler` instead.
|
||||
* core:
|
||||
- `ApplicationRef.waitForAsyncInitializers` is deprecated. Use
|
||||
`AppInitStatus.donePromise` / `AppInitStatus.done` instead.
|
||||
* core:
|
||||
- `ApplicationRef.registerBootstrapListener` is deprecated. Provide a multi
|
||||
provider for the new token `APP_BOOTSTRAP_LISTENER` instead.
|
||||
* core:
|
||||
- `ApplicationRef.dispose` is deprecated. Destroy the module that was
|
||||
created during bootstrap instead by calling `NgModuleRef.destroy`.
|
||||
- `AplicationRef.registerDisposeListener` is deprecated.
|
||||
Use the `ngOnDestroy` lifecycle hook for providers or
|
||||
`NgModuleRef.onDestroy` instead.
|
||||
- `disposePlatform` is deprecated. Use `destroyPlatform` instead.
|
||||
- `PlatformRef.dipose()` is deprecated. Use `PlatformRef.destroy()`
|
||||
instead.
|
||||
- `PlatformRef.registerDisposeListener` is deprecated. Use
|
||||
`PlatformRef.onDestroy` instead.
|
||||
- `PlaformRef.diposed` is deprecated. Use `PlatformRef.destroyed`
|
||||
instead.
|
||||
* testing:
|
||||
* `withProviders`, use `TestBed.withModule` instead
|
||||
* `addProviders`, use `TestBed.configureTestingModule` instead
|
||||
* `TestComponentBuilder`, use `TestBed.configureTestModule` / `TestBed.override...` / `TestBed.createComponent` instead.
|
||||
* core:
|
||||
- ES5 users can no longer use the `View(…)` function to provide `ViewMetadata`.
|
||||
This mirrors the removal of the `@View` decorator a while ago.
|
||||
* core:
|
||||
- `bootstrapModule` and `bootstrapModuleFactory` have been moved to be members of `PlaformRef`.
|
||||
E.g. `platformBrowserDynamic().bootstrapModule(MyModule)`.
|
||||
* core:
|
||||
- By default, Angular will error during parsing
|
||||
on unknown properties,
|
||||
even if they are on elements with a `-` in their name
|
||||
(aka custom elements). If you application is using
|
||||
custom elements, fill the new parameter `@NgModule.schemas`
|
||||
with the value `[CUSTOM_ELEMENTS_SCHEMA]`.
|
||||
|
||||
E.g. :
|
||||
```
|
||||
@NgModule({
|
||||
declarations: [MyComponentThatUsesAWebComponent],
|
||||
imports: [BrowserModule],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
boostrap: [MyComponentThatUsesAWebComponent],
|
||||
})
|
||||
export class MyAppModule{}
|
||||
```
|
||||
* core:
|
||||
- `coreLoadAndBootstrap` and `coreBootstrap` can't be used any more (without migration support).
|
||||
Use `bootstrapModule` / `bootstrapModuleFactory` instead.
|
||||
- All Components listed in routes have to be part of the `declarations` of an NgModule.
|
||||
Either directly on the bootstrap module / lazy loaded module, or in an NgModule imported by them.
|
||||
* router:
|
||||
`UrlPathWithParams` => `UrlSegment`
|
||||
`UrlSegment` => `UrlSegmentGroup`
|
||||
* core:
|
||||
- `ApplicationRef.run` is deprecated. Use `NgZone.run` directly
|
||||
- `ApplicationRef.injector` is deprecated. Inject an `Injector` or
|
||||
use `NgModuleRef.injector` instead
|
||||
- `ApplicationRef.zone` is deprecated. Inject `NgZone` instead.
|
||||
* testing: `TestInjector` is now renamed to `TestBed`
|
||||
|
||||
Before:
|
||||
|
||||
```js
|
||||
import {TestInjector, getTestInjector} from '@angular/core/testing';
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```js
|
||||
import {TestBed, getTestBed} from '@angular/core/testing';
|
||||
```
|
||||
* http: The behavior in this commit is the same as before PR 7260 : the objects sent with the request are converted to a string, therefore there is no need for the user to take care of the serialization.
|
||||
* platform-browser: The following API was never intended to be public and is removed:
|
||||
|
||||
```js
|
||||
import {verifyNoBrowserErrors} from '@angular/platform-browser/testing_e2e';
|
||||
```
|
||||
|
||||
Consider using Protractor's console plugin: https://github.com/angular/protractor-console-plugin
|
||||
* ICU:
|
||||
|
||||
"{" is used a a delimiter for ICU messages then it could not be used in text nodes.
|
||||
"{" should be escaped as "{{ '{' }}"
|
||||
|
||||
Before:
|
||||
|
||||
<span>some { valid } text</span>
|
||||
|
||||
After:
|
||||
|
||||
<span>some { invalid } text<span> <!-- throw parse error -->
|
||||
<span>some {{ '{' }} valid } text</span>
|
||||
* core: (deprecations)
|
||||
|
||||
- Instead of `coreBootstrap`, create an `@NgModule` and use `bootstrapModule`.
|
||||
- Instead of `coreLoadAndBootstarp`, create an `@NgModule` and use `bootstrapModuleFactory`.
|
||||
- Instead of `bootstrapWorkerApp`, create an `@NgModule` that includes the `WorkerAppModule` and use `bootstrapModule` with the `workerAppPlatform()`.
|
||||
- Instead of `bootstrapWorkerUi`, create an @AppModule that includes the `WorkerUiModule` and use `bootstrapModule` with the `workerUiPlatform()` instead.
|
||||
- Instead of `serverBootstrap`, create an @AppModule and use `bootstrapModule` with the `serverDynamicPlatform()` instead.
|
||||
- Instead of `PLATFORM_PIPES` and `PLATFORM_DIRECTIVES`, provide platform directives/pipes via an `@NgModule`.
|
||||
- Instead of `ComponentResolver`:
|
||||
- use `ComponentFactoryResolver` together with `@NgModule.entryComponents` or `ANALYZE_FOR_ENTRY_COMPONENTS` provider for dynamic component creation.
|
||||
- use `NgModuleFactoryLoader` for lazy loading.
|
||||
- Instead of `SystemJsComponentResolver`, create an `@NgModule` and use `SystemJsNgModuleLoader`.
|
||||
- Instead of `SystemJsCmpFactoryResolver`, create an `@NgModule` and use `SystemJsNgModuleFactoryLoader`
|
||||
* core:
|
||||
- `lockRunMode` is deprecated and no more needed.
|
||||
* animations:
|
||||
- animation trigger expressions within the template that are assigned as
|
||||
an element attribute (e.g. `@prop`) are deprecated. Please use the
|
||||
Angular2 property binding syntax (e.g. `[@prop]`) when assigning
|
||||
properties.
|
||||
|
||||
```ts
|
||||
// this is now deprecated
|
||||
<div @trigger="expression"></div>
|
||||
|
||||
// do this instead
|
||||
<div [@trigger]="expression"></div>
|
||||
```
|
||||
|
||||
* forms:
|
||||
|
||||
We have removed the deprecated form directives from the built-in platform directive list, so apps are not required to package forms with their app. This also makes forms friendly to offline compilation.
|
||||
|
||||
Instead, we have exposed three modules:
|
||||
|
||||
OLD API:
|
||||
- `DeprecatedFormsModule`
|
||||
|
||||
NEW API:
|
||||
- `FormsModule`
|
||||
- `ReactiveFormsModule`
|
||||
|
||||
If you provide one of these modules, the default forms directives and providers from that module will be available to you app-wide. Note: You can provide both the `FormsModule` and the `ReactiveFormsModule` together if you like, but they are fully-functional separately.
|
||||
|
||||
**Before:**
|
||||
```ts
|
||||
import {disableDeprecatedForms, provideForms} from @angular/forms;
|
||||
|
||||
bootstrap(App, [
|
||||
disableDeprecatedForms(),
|
||||
provideForms()
|
||||
]);
|
||||
```
|
||||
|
||||
**After:**
|
||||
|
||||
```ts
|
||||
import {DeprecatedFormsModule} from @angular/common;
|
||||
|
||||
@NgModule({
|
||||
declarations: [MyComponent],
|
||||
imports: [BrowserModule, DeprecatedFormsModule],
|
||||
boostrap: [MyComponent],
|
||||
})
|
||||
export class MyAppModule{}
|
||||
```
|
||||
* testing:
|
||||
- Application providers can no longer inject compiler internals (i.e. everything
|
||||
from `@angular/compiler`). Inject `Compiler` instead. This reflects the
|
||||
changes to `bootstrap` for module support (3f55aa609f60f130f1d69188ed057214b1267cb3).
|
||||
- Compiler providers can no longer be added via `addProviders` / `withProviders`.
|
||||
Use the new method `configureCompiler` instead.
|
||||
- Platform directives / pipes need to be provided via
|
||||
`configureModule` and can no longer be provided via the
|
||||
`PLATFORM_PIPES` / `PLATFORM_DIRECTIVES` tokens.
|
||||
- `setBaseTestProviders()` was renamed into `initTestEnvironment` and
|
||||
now takes a `PlatformRef` and a factory for a
|
||||
`Compiler`.
|
||||
- E.g. for the browser platform:
|
||||
|
||||
BEFORE:
|
||||
```
|
||||
import {setBaseTestProviders} from ‘@angular/core/testing’;
|
||||
import {TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
|
||||
TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS} from ‘@angular/platform-browser-dynamic/testing’;
|
||||
|
||||
setBaseTestProviders(TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
|
||||
TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);
|
||||
```
|
||||
|
||||
AFTER:
|
||||
```
|
||||
import {TestBed} from ‘@angular/core/testing’;
|
||||
import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from ‘@angular/platform-browser-dynamic/testing’;
|
||||
|
||||
TestBed.initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
|
||||
```
|
||||
- E.g. for the server platform:
|
||||
|
||||
BEFORE:
|
||||
```
|
||||
import {setBaseTestProviders} from ‘@angular/core/testing’;
|
||||
import {TEST_SERVER_PLATFORM_PROVIDERS,
|
||||
TEST_SERVER_APPLICATION_PROVIDERS} from ‘@angular/platform-server/testing/server’;
|
||||
|
||||
setBaseTestProviders(TEST_SERVER_PLATFORM_PROVIDERS,
|
||||
TEST_SERVER_APPLICATION_PROVIDERS);
|
||||
```
|
||||
|
||||
AFTER:
|
||||
```
|
||||
import {TestBed} from ‘@angular/core/testing’;
|
||||
import {ServerTestingModule, serverTestingPlatform} from ‘@angular/platform-browser-dynamic/testing’;
|
||||
|
||||
TestBed.initTestEnvironment(
|
||||
ServerTestingModule,
|
||||
serverTestingPlatform()
|
||||
);
|
||||
```
|
||||
|
||||
Related to #9726
|
||||
* router: Previously both imperative (router.navigate) and declarative (routerLink) navigations
|
||||
would preserve the current query params and fragment. This behavior turned out to
|
||||
be confusing. This commit changes it.
|
||||
|
||||
Now, neither is preserved by default. To preserve them, you need to do the following:
|
||||
|
||||
`router.naviage("newUrl", {preserveQueryParams: true, preserveFragment: true})`
|
||||
|
||||
`<a routerLink="newUrl" preserveQueryParams preserveFragment></a>`
|
||||
* ngUpgrade: UpgradeAdapter.addProvider are now deprecated in favor of passing in an NgModule into the adapter's constructor
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
let upgradeAdapter = new UpgradeAdapter();
|
||||
upgradeAdapter.addProviders([myProvidersArray);
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
@NgModule({
|
||||
providers: myProvidersArray
|
||||
})
|
||||
class MyModule {}
|
||||
|
||||
let upgradeAdapter = new UpgradeAdapter(MyModule);
|
||||
```
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0-rc.4"></a>
|
||||
# [2.0.0-rc.4](https://github.com/angular/angular/compare/2.0.0-rc.3...2.0.0-rc.4) (2016-06-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** ensure void => * animations are triggered when an expression is omitted ([e0b0a59](https://github.com/angular/angular/commit/e0b0a59)), closes [#9327](https://github.com/angular/angular/issues/9327) [#9381](https://github.com/angular/angular/issues/9381)
|
||||
* **animations:** make sure the easing value is passed into the web-animations player ([c43aec2](https://github.com/angular/angular/commit/c43aec2)), closes [#9517](https://github.com/angular/angular/issues/9517) [#9523](https://github.com/angular/angular/issues/9523)
|
||||
* **common:** add license header to localization.ts ([5150344](https://github.com/angular/angular/commit/5150344))
|
||||
* **common/testing:** remove internal MockLocationStrategy from common/testing ([#9562](https://github.com/angular/angular/issues/9562)) ([dcf7512](https://github.com/angular/angular/commit/dcf7512))
|
||||
* **compiler:** don't inject `viewProviders` into content child elements ([9ed8f2d](https://github.com/angular/angular/commit/9ed8f2d))
|
||||
* **compiler:** make code easier to type check ([51d4c9d](https://github.com/angular/angular/commit/51d4c9d)), closes [#9701](https://github.com/angular/angular/issues/9701)
|
||||
* **compiler:** report not existing files as errors ([e81dea6](https://github.com/angular/angular/commit/e81dea6)), closes [#9690](https://github.com/angular/angular/issues/9690)
|
||||
* **Compiler:** relax childIsRecursive check ([#8705](https://github.com/angular/angular/issues/8705)) ([3d5bb23](https://github.com/angular/angular/commit/3d5bb23))
|
||||
* **core:** improve error message for broken bindings ([df759b8](https://github.com/angular/angular/commit/df759b8)), closes [#6820](https://github.com/angular/angular/issues/6820) [#9536](https://github.com/angular/angular/issues/9536)
|
||||
* **core:** properly report missing providers and viewProviders ([#9411](https://github.com/angular/angular/issues/9411)) ([f114dd3](https://github.com/angular/angular/commit/f114dd3)), closes [#8237](https://github.com/angular/angular/issues/8237)
|
||||
* **core:** report duplicate template bindings in templates ([098b461](https://github.com/angular/angular/commit/098b461)), closes [#7315](https://github.com/angular/angular/issues/7315) [#9462](https://github.com/angular/angular/issues/9462)
|
||||
* **core/testing:** clean up the core testing public API ([#9466](https://github.com/angular/angular/issues/9466)) ([8a9e9c7](https://github.com/angular/angular/commit/8a9e9c7))
|
||||
* **core/testing:** move ComponentFixture to core ([#9386](https://github.com/angular/angular/issues/9386)) ([1143b03](https://github.com/angular/angular/commit/1143b03))
|
||||
* **core/testing compiler/testing:** move TestComponentBuilder to core/testing ([#9590](https://github.com/angular/angular/issues/9590)) ([a33195d](https://github.com/angular/angular/commit/a33195d)), closes [#9585](https://github.com/angular/angular/issues/9585)
|
||||
* **forms:** add select multiple accessor as built-in accessor ([9f00a1b](https://github.com/angular/angular/commit/9f00a1b))
|
||||
* **forms:** async validator-directives process Observables correctly ([#8186](https://github.com/angular/angular/issues/8186)) ([eef9512](https://github.com/angular/angular/commit/eef9512))
|
||||
* **forms:** emit statusChange when child controls have async validator ([#9652](https://github.com/angular/angular/issues/9652)) ([797914e](https://github.com/angular/angular/commit/797914e))
|
||||
* **forms:** make radio button selection logic more flexible ([#9646](https://github.com/angular/angular/issues/9646)) ([ed0ade6](https://github.com/angular/angular/commit/ed0ade6)), closes [#9558](https://github.com/angular/angular/issues/9558)
|
||||
* **forms:** ngModel should emit valueChanges and statusChanges asynchronously ([97a2119](https://github.com/angular/angular/commit/97a2119))
|
||||
* **http:** add search param escaping for keys ([#9166](https://github.com/angular/angular/issues/9166)) ([a5f2e20](https://github.com/angular/angular/commit/a5f2e20))
|
||||
* **http:** don't encode values that are allowed in query ([#9651](https://github.com/angular/angular/issues/9651)) ([1620426](https://github.com/angular/angular/commit/1620426)), closes [#9348](https://github.com/angular/angular/issues/9348)
|
||||
* **ngc:** correct dependencies for compiler-cli ([826f89f](https://github.com/angular/angular/commit/826f89f)), closes [#9540](https://github.com/angular/angular/issues/9540)
|
||||
* **ngc:** work with typescript[@next](https://github.com/next) ([f463e09](https://github.com/angular/angular/commit/f463e09))
|
||||
* **NgSwitch:** display deprecation message only once ([398060d](https://github.com/angular/angular/commit/398060d))
|
||||
* **platform-browser/testing:** clean up public api for platform-browser/testing ([#9519](https://github.com/angular/angular/issues/9519)) ([3d8eb8c](https://github.com/angular/angular/commit/3d8eb8c))
|
||||
* **platform-browser/testing-e2e:** clean up unused exports from e2e testing helpers ([#9387](https://github.com/angular/angular/issues/9387)) ([894747c](https://github.com/angular/angular/commit/894747c))
|
||||
* **public API:** update golden files ([ae4fa56](https://github.com/angular/angular/commit/ae4fa56))
|
||||
* **security:** allow empty CSS values. ([#9675](https://github.com/angular/angular/issues/9675)) ([2d9d7f1](https://github.com/angular/angular/commit/2d9d7f1))
|
||||
* **security:** no warning when sanitizing escaped html ([#9392](https://github.com/angular/angular/issues/9392)) ([#9413](https://github.com/angular/angular/issues/9413)) ([98cef76](https://github.com/angular/angular/commit/98cef76))
|
||||
* **testing:** remove the `toMatchPattern` matcher (jasmine has `toMatch`) ([6420f75](https://github.com/angular/angular/commit/6420f75))
|
||||
* public api surface fixes + stability markers ([24eb838](https://github.com/angular/angular/commit/24eb838)), closes [#9236](https://github.com/angular/angular/issues/9236) [#9235](https://github.com/angular/angular/issues/9235)
|
||||
* support *directive on `<template>` ([#9691](https://github.com/angular/angular/issues/9691)) ([3fec279](https://github.com/angular/angular/commit/3fec279)), closes [#7315](https://github.com/angular/angular/issues/7315)
|
||||
* **testing:** remove the `toThrowErrorWith` matcher (jasmine has `toThrowError`) ([e1e5c40](https://github.com/angular/angular/commit/e1e5c40))
|
||||
* **typings:** don't test compiler-cli typings on TS 1.8 ([54dbed4](https://github.com/angular/angular/commit/54dbed4))
|
||||
* **upgrade:** add peerDependency on platform-browser-dynamic ([#9674](https://github.com/angular/angular/issues/9674)) ([e2116c5](https://github.com/angular/angular/commit/e2116c5)), closes [#9623](https://github.com/angular/angular/issues/9623)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** support sync runtime compile ([bf598d6](https://github.com/angular/angular/commit/bf598d6)), closes [#7084](https://github.com/angular/angular/issues/7084) [#9594](https://github.com/angular/angular/issues/9594)
|
||||
* **core:** add `[@Component](https://github.com/Component).precompile` and `ComponentFactoryResolver` ([6c5b653](https://github.com/angular/angular/commit/6c5b653)), closes [#9543](https://github.com/angular/angular/issues/9543)
|
||||
* **core:** split ChangeDetectorStrategy into ChangeDetectionStrategy and ChangeDetectorStatus ([e12b127](https://github.com/angular/angular/commit/e12b127))
|
||||
* **DomRenderer:** Adding support for document fragments in SVG foreign objects ([#9458](https://github.com/angular/angular/issues/9458)) ([3644eef](https://github.com/angular/angular/commit/3644eef))
|
||||
* **forms:** add support for formArrayName ([c03e1f2](https://github.com/angular/angular/commit/c03e1f2)), closes [#9251](https://github.com/angular/angular/issues/9251)
|
||||
* **forms:** add support for standalone ngModel dirs inside forms ([6edf047](https://github.com/angular/angular/commit/6edf047)), closes [#9230](https://github.com/angular/angular/issues/9230)
|
||||
* **forms:** expose ValidatorFn and AsyncValidatorFn ([17dcbf6](https://github.com/angular/angular/commit/17dcbf6)), closes [#8834](https://github.com/angular/angular/issues/8834)
|
||||
* **forms:** make valueChanges and statusChanges available on abstract control directives ([de12710](https://github.com/angular/angular/commit/de12710))
|
||||
* **forms:** support updating of validators on exiting controls ([#9516](https://github.com/angular/angular/issues/9516)) ([638fd74](https://github.com/angular/angular/commit/638fd74))
|
||||
* **forms:** use formControlName on radio buttons when name is absent ([#9681](https://github.com/angular/angular/issues/9681)) ([0961bd1](https://github.com/angular/angular/commit/0961bd1))
|
||||
* **QueryList:** implement some() ([#9464](https://github.com/angular/angular/issues/9464)) ([f6a410a](https://github.com/angular/angular/commit/f6a410a)), closes [#9443](https://github.com/angular/angular/issues/9443)
|
||||
* **router:** add pathMatch property to replace terminal ([fcfddbf](https://github.com/angular/angular/commit/fcfddbf))
|
||||
* **router:** implement data and resolve ([f2f1ec0](https://github.com/angular/angular/commit/f2f1ec0))
|
||||
* **router:** use componentFactoryResolver ([dc64e90](https://github.com/angular/angular/commit/dc64e90))
|
||||
* **security:** allow more HTML5 elements and attributes in sanitizers ([6605eb3](https://github.com/angular/angular/commit/6605eb3)), closes [#9438](https://github.com/angular/angular/issues/9438)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* http: The changes to Http's URLSearchParams serialization now
|
||||
prevent encoding of these characters inside query parameters
|
||||
which were previously converted to percent-encoded values `@ : $ , ; + ; ? /`
|
||||
|
||||
The default encoding behavior can be overridden by extending
|
||||
QueryEncoder, as documented in the URLSearchParams service.
|
||||
* Previously multiple template bindings on one element
|
||||
(ex. `<div *ngIf='..' *ngFor='...'>`) were allowed but most of the time
|
||||
were leading to undesired result. It is possible that a small number
|
||||
of applications will see template parse errors that should be fixed by
|
||||
nesting elements or using `<template>` tags explicitly.
|
||||
* DomEventsPlugin and KeyEventsPlugin previously exported from core are no longer public - these classes are implementation detail.
|
||||
|
||||
Previously deprecated BROWSER_PROVIDERS was completely removed from platform-browser.
|
||||
* core/testing compiler/testing: `TestComponentBuilder` is now imported from `@angular/core/testing`. Imports
|
||||
from `@angular/compiler/testing` are deprecated.
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
import {TestComponentBuilder, TestComponentRenderer, ComponentFixtureAutoDetect} from '@angular/compiler/testing';
|
||||
```
|
||||
|
||||
After:
|
||||
```
|
||||
import {TestComponentBuilder, TestComponentRenderer, ComponentFixtureAutoDetect} from '@angular/core/testing';
|
||||
```
|
||||
* common/testing: MockLocationStrategy was intended to be internal only and is now removed
|
||||
from the `@angular/common/testing` public api.
|
||||
|
||||
Use `SpyLocation` from `@angular/common/testing` for location testing.
|
||||
* compiler: `TestComponentBuilder.createSync` now takes a component type
|
||||
and throws if not all templates are either inlined
|
||||
are compiled before via `createAsync`.
|
||||
* core/testing: Remove the following APIs from `@angular/core/testing`, which have been deprecated or were
|
||||
never intended to be publicly exported:
|
||||
|
||||
```
|
||||
injectAsync
|
||||
clearPendingTimers
|
||||
Log
|
||||
MockAppliacationHref
|
||||
MockNgZone
|
||||
clearPendingTimers
|
||||
getTypeOf
|
||||
instantiateType
|
||||
```
|
||||
|
||||
Instead of `injectAsync`, use `async(inject())`.
|
||||
|
||||
`clearPendingTimers` is no longer required.
|
||||
* platform-browser/testing: The following are no longer publicly exported APIs. They were intended as internal
|
||||
utilities and you should use your own util:
|
||||
|
||||
```
|
||||
browserDetection,
|
||||
dispatchEvent,
|
||||
el,
|
||||
normalizeCSS,
|
||||
stringifyElement,
|
||||
expect (and custom matchers for Jasmine)
|
||||
```
|
||||
* testing:
|
||||
|
||||
Before:
|
||||
|
||||
expect(...).toThrowErrorWith(msg);
|
||||
|
||||
After:
|
||||
|
||||
expect(...).toThrowError(msg);
|
||||
|
||||
Before:
|
||||
|
||||
expect(...).toMatchPattern(pattern);
|
||||
|
||||
After:
|
||||
|
||||
expect(...).toMatch(pattern);
|
||||
* core/testing: `ComponentFixture` will be moving out of `@angular/compiler/testing` to `@angular/core/testing` in
|
||||
this release. For now, it is deprecated from `@angular/compiler/testing`.
|
||||
|
||||
|
||||
* The async function now determines whether it should return a promise
|
||||
or instead call a done function parameter. Importing Jasmine functions
|
||||
from `@angular/core/testing` is no longer necessary and is now deprecated.
|
||||
|
||||
Additionally, beforeEachProviders is also deprecated, as it is specific
|
||||
to the testing framework. Instead, use the new addProviders method directly.
|
||||
|
||||
Before:
|
||||
```js
|
||||
import {beforeEachProviders, it, describe, inject} from '@angular/core/testing';
|
||||
|
||||
describe('my code', () => {
|
||||
beforeEachProviders(() => [MyService]);
|
||||
|
||||
it('does stuff', inject([MyService], (service) => {
|
||||
// actual test
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
After:
|
||||
```js
|
||||
import {addProviders, inject} from '@angular/core/testing';
|
||||
|
||||
describe('my code', () => {
|
||||
beforeEach(() => {
|
||||
addProviders([MyService]);
|
||||
});
|
||||
|
||||
it('does stuff', inject([MyService], (service) => {
|
||||
// actual test
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
<a name="2.0.0-rc.3"></a>
|
||||
# 2.0.0-rc.3 (2016-06-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** ensure starting styles are applied when a delay is present ([ba46ca6](https://github.com/angular/angular/commit/ba46ca6)), closes [#9326](https://github.com/angular/angular/issues/9326) [#9328](https://github.com/angular/angular/issues/9328)
|
||||
* **BrowserUtil:** fix `supportsIntlApi()` ([76a4187](https://github.com/angular/angular/commit/76a4187))
|
||||
* **change_detection:** ChangeDetectorRef reattach should restore original mode ([773c349](https://github.com/angular/angular/commit/773c349)), closes [#7078](https://github.com/angular/angular/issues/7078) [#7080](https://github.com/angular/angular/issues/7080)
|
||||
* **compiler:** codegen view query generic types ([e157a06](https://github.com/angular/angular/commit/e157a06))
|
||||
* **compiler:** properly report unresolved dependencies ([fdf6bc1](https://github.com/angular/angular/commit/fdf6bc1)), closes [#9332](https://github.com/angular/angular/issues/9332) [#9341](https://github.com/angular/angular/issues/9341)
|
||||
* **compiler:** StaticReflector ignores unregistered decorators. (#9266) ([791153c](https://github.com/angular/angular/commit/791153c)), closes [#9265](https://github.com/angular/angular/issues/9265)
|
||||
* **core/testing:** show full error ([297f0fd](https://github.com/angular/angular/commit/297f0fd))
|
||||
* **forms:** ngModel should export as ngModel ([8e6e90e](https://github.com/angular/angular/commit/8e6e90e))
|
||||
* **guards:** Cancel in-flight guards if one returns false ([97cf0e4](https://github.com/angular/angular/commit/97cf0e4))
|
||||
* **HtmlParser:** add missing ; ([c60ef45](https://github.com/angular/angular/commit/c60ef45))
|
||||
* **HtmlParser:** consider `<ng-container>` when adding required parents ([9ba400d](https://github.com/angular/angular/commit/9ba400d))
|
||||
* **HtmlParser:** do not add required parents to template root elements ([e484c62](https://github.com/angular/angular/commit/e484c62)), closes [#5967](https://github.com/angular/angular/issues/5967)
|
||||
* **HTTP/XhrBackend:** correctly set the status code on errors (#9355) ([12c4904](https://github.com/angular/angular/commit/12c4904)), closes [angular/http#54](https://github.com/angular/http/issues/54)
|
||||
* **NumberPipe:** fix broken RegExp ([49bf3f5](https://github.com/angular/angular/commit/49bf3f5))
|
||||
* **partition:** fix partition when `<!-- i18n -->` is the only child ([58b18d7](https://github.com/angular/angular/commit/58b18d7))
|
||||
* **perf:** support prod mode again ([c0f2a22](https://github.com/angular/angular/commit/c0f2a22)), closes [#9318](https://github.com/angular/angular/issues/9318) [#8508](https://github.com/angular/angular/issues/8508) [#9318](https://github.com/angular/angular/issues/9318)
|
||||
* **platform-server:** cleanup public api of platform-server ([8675b8d](https://github.com/angular/angular/commit/8675b8d)), closes [#9237](https://github.com/angular/angular/issues/9237) [#9205](https://github.com/angular/angular/issues/9205)
|
||||
* **upgrade:** fix bundling issue and fix e2e test ([8c076d5](https://github.com/angular/angular/commit/8c076d5)), closes [#9244](https://github.com/angular/angular/issues/9244)
|
||||
* **XmbSerializer:** add meaning attribute, escape attribute values ([c9c81e1](https://github.com/angular/angular/commit/c9c81e1))
|
||||
|
||||
### Features
|
||||
|
||||
* **benchpress:** add custom user metric to benchpress ([6686bc6](https://github.com/angular/angular/commit/6686bc6)), closes [#9229](https://github.com/angular/angular/issues/9229)
|
||||
* **compiler:** make interpolation symbols configurable (`@Component` config) (#9367) ([1b28cf7](https://github.com/angular/angular/commit/1b28cf7)), closes [#9158](https://github.com/angular/angular/issues/9158)
|
||||
* **compiler-cli:** add a `debug` option to control the output ([1eaa193](https://github.com/angular/angular/commit/1eaa193)), closes [#9208](https://github.com/angular/angular/issues/9208)
|
||||
* **core:** ensure CSS parser tracks start/end values and understands complex pseudo selecto ([935c39a](https://github.com/angular/angular/commit/935c39a))
|
||||
* **datePipe:** numeric string support ([5c8d315](https://github.com/angular/angular/commit/5c8d315))
|
||||
* **DomElementSchemaRegistry:** add support for `<ng-content>` and `<ng-container>` ([b620f4f](https://github.com/angular/angular/commit/b620f4f))
|
||||
* **I18N Expander:** do not add extra `<ul>` & `<li>` around ICU messages (#9283) ([721f53f](https://github.com/angular/angular/commit/721f53f)), closes [#9072](https://github.com/angular/angular/issues/9072)
|
||||
* **MessageExtractor:** do not expand ICU messages before extraction ([04a50f5](https://github.com/angular/angular/commit/04a50f5))
|
||||
* **QueryList:** support index in callbacks ([5fe6075](https://github.com/angular/angular/commit/5fe6075)), closes [#9278](https://github.com/angular/angular/issues/9278)
|
||||
* **radio:** support radio button sharing a control ([39e0b49](https://github.com/angular/angular/commit/39e0b49))
|
||||
* **security:** fail more detectably when using a safe value in an interpolation. ([8879aa1](https://github.com/angular/angular/commit/8879aa1))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Parse5Adapter is no longer exported as public API, use serverBootstrap()
|
||||
Parse5Adapter is an implementation detail not a public API
|
||||
|
||||
* `containsRegexp` is no more exported from `@angular/core/testing`. It should not have been part of the public API in the first place.
|
||||
|
||||
|
||||
<a name="2.0.0-rc.2"></a>
|
||||
# 2.0.0-rc.2 (2016-06-15)
|
||||
|
||||
@ -714,7 +1440,7 @@ After:
|
||||
* **i18n:** add ngPlural directive ([df1f78e](https://github.com/angular/angular/commit/df1f78e))
|
||||
* **i18n:** implement a simple version of message extractor ([095db67](https://github.com/angular/angular/commit/095db67)), closes [#7454](https://github.com/angular/angular/issues/7454)
|
||||
* **shadow_css:** support `/deep/` and `>>>` ([cb38d72](https://github.com/angular/angular/commit/cb38d72)), closes [#7562](https://github.com/angular/angular/issues/7562) [#7563](https://github.com/angular/angular/issues/7563)
|
||||
* **TAG_DEFINITIONS:** include <meta> and <base> ([2c7c3e3](https://github.com/angular/angular/commit/2c7c3e3)), closes [#7455](https://github.com/angular/angular/issues/7455)
|
||||
* **TAG_DEFINITIONS:** include `<meta>` and `<base>` ([2c7c3e3](https://github.com/angular/angular/commit/2c7c3e3)), closes [#7455](https://github.com/angular/angular/issues/7455)
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
@ -903,7 +1629,7 @@ And install typings such as `jasmine`, `angular-protractor`, or `selenium-webdri
|
||||
to satisfy the type-checker.
|
||||
|
||||
If you rely on es6 APIs other than Promises and Collections, you will need to
|
||||
install the es6-shim typing instead of using the <reference> tag above.
|
||||
install the es6-shim typing instead of using the `<reference>` tag above.
|
||||
Angular previously exposed typings for the entire ES6 API.
|
||||
|
||||
<a name="2.0.0-beta.5"></a>
|
||||
@ -935,7 +1661,7 @@ This release was incorrect; replaced with beta.6.
|
||||
### Features
|
||||
|
||||
* **change_detection:** allow all legal programs in the dev mode ([42231f5](https://github.com/angular/angular/commit/42231f5))
|
||||
* **dart/transform:** Generate all code into <file>.template.dart ([8c36aa8](https://github.com/angular/angular/commit/8c36aa8))
|
||||
* **dart/transform:** Generate all code into `<file>.template.dart` ([8c36aa8](https://github.com/angular/angular/commit/8c36aa8))
|
||||
* **debug:** replace DebugElement with new Debug DOM ([e1bf3d3](https://github.com/angular/angular/commit/e1bf3d3))
|
||||
* **ngFor:** add custom trackBy function support ([cee2318](https://github.com/angular/angular/commit/cee2318)), closes [#6779](https://github.com/angular/angular/issues/6779)
|
||||
* **upgrade:** support bindToController with binding definitions ([99e6500](https://github.com/angular/angular/commit/99e6500)), closes [#4784](https://github.com/angular/angular/issues/4784)
|
||||
@ -1304,7 +2030,7 @@ Use imports from `angular2/compiler` instead.
|
||||
* **HtmlLexer:** tag name must follow "<" without space ([47f1d12](https://github.com/angular/angular/commit/47f1d12))
|
||||
* **HtmlParser:** Do not add parent element for template children ([3a43861](https://github.com/angular/angular/commit/3a43861)), closes [#5638](https://github.com/angular/angular/issues/5638)
|
||||
* **HtmlParser:** ignore LF immediately following pre, textarea & listing ([eb0ea93](https://github.com/angular/angular/commit/eb0ea93)), closes [#5630](https://github.com/angular/angular/issues/5630) [#5688](https://github.com/angular/angular/issues/5688)
|
||||
* **HtmlParser:** mark <source> elements as void ([50490b5](https://github.com/angular/angular/commit/50490b5)), closes [#5663](https://github.com/angular/angular/issues/5663) [#5668](https://github.com/angular/angular/issues/5668)
|
||||
* **HtmlParser:** mark `<source>` elements as void ([50490b5](https://github.com/angular/angular/commit/50490b5)), closes [#5663](https://github.com/angular/angular/issues/5663) [#5668](https://github.com/angular/angular/issues/5668)
|
||||
* **npm:** move es6-shim from devDependencies to dependencies ([21542ed](https://github.com/angular/angular/commit/21542ed))
|
||||
* **package:** relock RxJS to alpha.11 ([4b1618c](https://github.com/angular/angular/commit/4b1618c)), closes [#5643](https://github.com/angular/angular/issues/5643) [#5644](https://github.com/angular/angular/issues/5644)
|
||||
* **router:** set correct redirect/default URL from hashchange ([aa85856](https://github.com/angular/angular/commit/aa85856)), closes [#5590](https://github.com/angular/angular/issues/5590) [#5683](https://github.com/angular/angular/issues/5683)
|
||||
@ -2291,7 +3017,7 @@ class HelloCmp implements OnInit {
|
||||
* **http/http:** allow for commonjs as ngHttp ([16eb8ce](https://github.com/angular/angular/commit/16eb8ce)), closes [#3633](https://github.com/angular/angular/issues/3633)
|
||||
* **injector:** support getRootInjectors on dehydrated injectors. ([92da543](https://github.com/angular/angular/commit/92da543)), closes [#3760](https://github.com/angular/angular/issues/3760)
|
||||
* **injectors:** reset the construction counter in dynamic strategy. ([272ad61](https://github.com/angular/angular/commit/272ad61)), closes [#3635](https://github.com/angular/angular/issues/3635)
|
||||
* <template> tag for browsers that do not suppor them ([ddcfd46](https://github.com/angular/angular/commit/ddcfd46)), closes [#3636](https://github.com/angular/angular/issues/3636)
|
||||
* `<template>` tag for browsers that do not suppor them ([ddcfd46](https://github.com/angular/angular/commit/ddcfd46)), closes [#3636](https://github.com/angular/angular/issues/3636)
|
||||
* **karma:** corrected race condition with RX loading ([8dc509f](https://github.com/angular/angular/commit/8dc509f))
|
||||
* **parser:** detect and report interpolation in expressions ([b039ec3](https://github.com/angular/angular/commit/b039ec3)), closes [#3645](https://github.com/angular/angular/issues/3645) [#3750](https://github.com/angular/angular/issues/3750)
|
||||
* **router:** allow router-link to link to redirects ([72e0b8f](https://github.com/angular/angular/commit/72e0b8f)), closes [#3335](https://github.com/angular/angular/issues/3335) [#3624](https://github.com/angular/angular/issues/3624)
|
||||
|
@ -95,7 +95,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 for JS and Dart to ensure tests are still passing.
|
||||
* Re-run the Angular 2 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
|
||||
|
308
DEVELOPER.md
308
DEVELOPER.md
@ -5,15 +5,9 @@ JS and Dart versions. It also explains the basic mechanics of using `git`, `node
|
||||
|
||||
* [Prerequisite Software](#prerequisite-software)
|
||||
* [Getting the Sources](#getting-the-sources)
|
||||
* [Environment Variable Setup](#environment-variable-setup)
|
||||
* [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
|
||||
* [Build commands](#build-commands)
|
||||
* [Installing NPM Modules](#installing-npm-modules)
|
||||
* [Building](#building)
|
||||
* [Running Tests Locally](#running-tests-locally)
|
||||
* [Code Style](#code-style)
|
||||
* [Project Information](#project-information)
|
||||
* [CI using Travis](#ci-using-travis)
|
||||
* [Transforming Dart code](#transforming-dart-code)
|
||||
* [Debugging](#debugging)
|
||||
|
||||
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
|
||||
if you'd like to contribute to Angular.
|
||||
@ -32,15 +26,6 @@ following products on your development machine:
|
||||
(version `>=3.5.3 <4.0`), which comes with Node. Depending on your system, you can install Node either from
|
||||
source or as a pre-packaged bundle.
|
||||
|
||||
* *Optional*: [Dart](https://www.dartlang.org) (version `>=1.13.2 <2.0.0`), specifically the Dart SDK and
|
||||
Dartium (a version of [Chromium](http://www.chromium.org) with native support for Dart through
|
||||
the Dart VM). Visit Dart's [Downloads page](https://www.dartlang.org/downloads) page for
|
||||
instructions. You can also download both **stable** and **dev** channel versions from the
|
||||
[download archive](https://www.dartlang.org/downloads/archive/). In that case, on Windows, Dart
|
||||
must be added to the `PATH` (e.g. `path-to-dart-sdk-folder\bin`) and a new `DARTIUM_BIN`
|
||||
environment variable must be created, pointing to the executable (e.g.
|
||||
`path-to-dartium-folder\chrome.exe`).
|
||||
|
||||
* [Java Development Kit](http://www.oracle.com/technetwork/es/java/javase/downloads/index.html) which is used
|
||||
to execute the selenium standalone server for e2e testing.
|
||||
|
||||
@ -65,42 +50,7 @@ cd angular
|
||||
# Add the main Angular repository as an upstream remote to your repository:
|
||||
git remote add upstream https://github.com/angular/angular.git
|
||||
```
|
||||
|
||||
## Environment Variable Setup
|
||||
|
||||
Define the environment variables listed below. These are mainly needed for the testing. The
|
||||
notation shown here is for [`bash`](http://www.gnu.org/software/bash); adapt as appropriate for
|
||||
your favorite shell.
|
||||
|
||||
Examples given below of possible values for initializing the environment variables assume **Mac OS
|
||||
X** and that you have installed the Dart Editor in the directory named by
|
||||
`DART_EDITOR_DIR=/Applications/dart`. This is only for illustrative purposes.
|
||||
|
||||
```shell
|
||||
# DARTIUM_BIN: path to a Dartium browser executable; used by Karma to run Dart tests
|
||||
export DARTIUM_BIN="$DART_EDITOR_DIR/chromium/Chromium.app/Contents/MacOS/Chromium"
|
||||
```
|
||||
|
||||
Add the Dart SDK `bin` directory to your path and/or define `DART_SDK` (this is also detailed
|
||||
[here](https://www.dartlang.org/tools/pub/installing.html)):
|
||||
|
||||
```shell
|
||||
# DART_SDK: path to a Dart SDK directory
|
||||
export DART_SDK="$DART_EDITOR_DIR/dart-sdk"
|
||||
|
||||
# Update PATH to include the Dart SDK bin directory
|
||||
PATH+=":$DART_SDK/bin"
|
||||
```
|
||||
|
||||
And specify where the pub’s dependencies are downloaded. By default, this directory is located under .pub_cache
|
||||
in your home directory (on Mac and Linux), or in AppData\Roaming\Pub\Cache (on Windows).
|
||||
|
||||
```shell
|
||||
# PUB_CACHE: location of pub dependencies
|
||||
export PUB_CACHE="/Users/<user>/.pub-cache"
|
||||
```
|
||||
|
||||
## Installing NPM Modules and Dart Packages
|
||||
## Installing NPM Modules
|
||||
|
||||
Next, install the JavaScript modules and Dart packages needed to build and test Angular:
|
||||
|
||||
@ -124,243 +74,67 @@ use in these instructions.
|
||||
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
|
||||
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
|
||||
|
||||
## Build commands
|
||||
|
||||
To build Angular and prepare tests, run:
|
||||
## Windows only
|
||||
|
||||
In order to create the right symlinks, run **as administrator**:
|
||||
```shell
|
||||
$(npm bin)/gulp build
|
||||
./scripts/windows/create-symlinks.sh
|
||||
```
|
||||
|
||||
Notes:
|
||||
* Results are put in the `dist` folder.
|
||||
* This will also run `pub get` for the subfolders in `modules` and run `dartanalyzer` for
|
||||
every file that matches `<module>/src/<module>.dart`, e.g. `di/src/di.dart`.
|
||||
Before submitting a PR, do not forget to remove them:
|
||||
```shell
|
||||
./scripts/windows/remove-symlinks.sh
|
||||
```
|
||||
|
||||
You can selectively build either the JS or Dart versions as follows:
|
||||
## Building
|
||||
|
||||
* `$(npm bin)/gulp build.js`
|
||||
* `$(npm bin)/gulp build.dart`
|
||||
|
||||
To clean out the `dist` folder, run:
|
||||
To build Angular run:
|
||||
|
||||
```shell
|
||||
$(npm bin)/gulp clean
|
||||
./build.sh
|
||||
```
|
||||
|
||||
* Results are put in the dist folder.
|
||||
|
||||
## Running Tests Locally
|
||||
|
||||
### Full test suite
|
||||
|
||||
* `npm test`: full test suite for both JS and Dart versions of Angular. These are the same tests
|
||||
that run on Travis.
|
||||
|
||||
You can selectively run either the JS or Dart versions as follows:
|
||||
|
||||
* `$(npm bin)/gulp test.all.js`
|
||||
* `$(npm bin)/gulp test.all.dart`
|
||||
|
||||
### Unit tests
|
||||
|
||||
You can run just the unit tests as follows:
|
||||
|
||||
* `$(npm bin)/gulp test.unit.js`: JS tests in a browser; runs in **watch mode** (i.e.
|
||||
watches the test files for changes and re-runs tests when files are updated).
|
||||
* `$(npm bin)/gulp test.unit.cjs`: JS tests in NodeJS; runs in **watch mode**.
|
||||
* `$(npm bin)/gulp test.unit.dart`: Dart tests in Dartium; runs in **watch mode**.
|
||||
|
||||
If you prefer running tests in "single-run" mode rather than watch mode use:
|
||||
|
||||
* `$(npm bin)/gulp test.unit.js/ci`
|
||||
* `$(npm bin)/gulp test.unit.cjs/ci`
|
||||
* `$(npm bin)/gulp test.unit.dart/ci`
|
||||
|
||||
The task updates the dist folder with transpiled code whenever a source or test file changes, and
|
||||
Karma is run against the new output.
|
||||
|
||||
**Note**: If you want to only run a single test you can alter the test you wish to run by changing
|
||||
`it` to `iit` or `describe` to `ddescribe`. This will only run that individual test and make it
|
||||
much easier to debug. `xit` and `xdescribe` can also be useful to exclude a test and a group of
|
||||
tests respectively.
|
||||
|
||||
**Note**: **watch mode** needs symlinks to work, so if you're using Windows, ensure you have the
|
||||
rights to built them in your operating system. On Windows, only administrators can create symbolic links by default, but you may change the policy. (see [here](https://technet.microsoft.com/library/cc766301.aspx?f=255&MSPPError=-2147217396).)
|
||||
|
||||
### Unit tests with Sauce Labs or Browser Stack
|
||||
|
||||
First, in a terminal, create a tunnel with [Sauce Connect](https://docs.saucelabs.com/reference/sauce-connect/) or [Browser Stack Local](https://www.browserstack.com/local-testing#command-line), and valid credentials.
|
||||
|
||||
Then, in another terminal:
|
||||
- Define the credentials as environment variables, e.g.:
|
||||
```
|
||||
export SAUCE_USERNAME='my_user'; export SAUCE_ACCESS_KEY='my_key';
|
||||
export BROWSER_STACK_USERNAME='my_user'; export BROWSER_STACK_ACCESS_KEY='my_key';
|
||||
```
|
||||
- Then run `gulp test.unit.js.(sauce|browserstack) --browsers=option1,option2,..,optionN`
|
||||
The options are any mix of browsers and aliases which are defined in the [browser-providers.conf.js](https://github.com/angular/angular/blob/master/browser-providers.conf.js) file.
|
||||
They are case insensitive, and the `SL_` or `BS_` prefix must not be added for browsers.
|
||||
|
||||
Some examples of commands:
|
||||
```
|
||||
gulp test.unit.js.sauce --browsers=Safari8,ie11 //run in Sauce Labs with Safari 8 and IE11
|
||||
gulp test.unit.js.browserstack --browsers=Safari,IE //run in Browser Stack with Safari 7, Safari 8, Safari 9, IE 9, IE 10 and IE 11
|
||||
gulp test.unit.js.sauce --browsers=IOS,safari8,android5.1 //run in Sauce Labs with iOS 7, iOS 8, iOs 9, Safari 8 and Android 5.1
|
||||
```
|
||||
|
||||
### E2E tests
|
||||
|
||||
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder).
|
||||
2. `$(npm bin)/gulp serve.js.prod serve.dart` (runs a local webserver).
|
||||
3. `$(npm bin)/protractor protractor-js.conf.js`: JS e2e tests.
|
||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js`: dart2js e2e tests.
|
||||
|
||||
Angular specific command line options when running protractor:
|
||||
- `$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||
|
||||
### Performance tests
|
||||
|
||||
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder)
|
||||
2. `$(npm bin)/gulp serve.js.prod serve.dart` (runs a local webserver)
|
||||
3. `$(npm bin)/protractor protractor-js.conf.js --benchmark`: JS performance tests
|
||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js --benchmark`: dart2js performance tests
|
||||
|
||||
Angular specific command line options when running protractor (e.g. force gc, ...):
|
||||
`$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||
|
||||
## Code Style
|
||||
|
||||
### Formatting with <a name="clang-format">clang-format</a>
|
||||
|
||||
We use [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to automatically enforce code
|
||||
style for our TypeScript code. This allows us to focus our code reviews more on the content, and
|
||||
less on style nit-picking. It also lets us encode our style guide in the `.clang-format` file in the
|
||||
repository, allowing many tools and editors to share our settings.
|
||||
|
||||
To check the formatting of your code, run
|
||||
|
||||
gulp lint
|
||||
|
||||
Note that the continuous build on CircleCI will fail the build if files aren't formatted according
|
||||
to the style guide.
|
||||
|
||||
Your life will be easier if you include the formatter in your standard workflow. Otherwise, you'll
|
||||
likely forget to check the formatting, and waste time waiting for a build on Travis that fails due
|
||||
to some whitespace difference.
|
||||
|
||||
* Use `gulp format` to format everything.
|
||||
* Use `gulp lint` to check if your code is `clang-format` clean. This also gives
|
||||
you a command line to format your code.
|
||||
* `clang-format` also includes a git hook, run `git clang-format` to format all files you
|
||||
touched.
|
||||
* You can run this as a **git pre-commit hook** to automatically format your delta regions when you
|
||||
commit a change. In the angular repo, run
|
||||
|
||||
```
|
||||
$ echo -e '#!/bin/sh\nexec git clang-format --style file' > .git/hooks/pre-commit
|
||||
$ chmod u+x !$
|
||||
```
|
||||
|
||||
**NOTE**: To use ```git clang-format``` use have to make sure that ```git-clang-format``` is in your
|
||||
```PATH```. The easiest way is probably to just ```npm install -g clang-format``` as it comes with
|
||||
```git-clang-format```.
|
||||
|
||||
* **WebStorm** can run clang-format on the current file.
|
||||
1. Under Preferences, open Tools > External Tools.
|
||||
1. Plus icon to Create Tool
|
||||
1. Fill in the form:
|
||||
- Name: clang-format
|
||||
- Description: Format
|
||||
- Synchronize files after execution: checked
|
||||
- Open console: not checked
|
||||
- Show in: Editor menu
|
||||
- Program: `$ProjectFileDir$/node_modules/.bin/clang-format`
|
||||
- Parameters: `-i -style=file $FilePath$`
|
||||
- Working directory: `$ProjectFileDir$`
|
||||
* `clang-format` integrations are also available for many popular editors (`vim`, `emacs`,
|
||||
`Sublime Text`, etc.).
|
||||
|
||||
### Linting
|
||||
|
||||
We use [tslint](https://github.com/palantir/tslint) for linting. See linting rules in [gulpfile](gulpfile.js). To lint, run
|
||||
To run tests:
|
||||
|
||||
```shell
|
||||
$ gulp lint
|
||||
$ ./test.sh node # Run all angular tests on node
|
||||
|
||||
$ ./test.sh browser # Run all angular tests in browser
|
||||
$ ./test.sh browserNoRouter # Optionally run all angular tests without router in browser
|
||||
|
||||
$ ./test.sh tools # Run angular tooling (not framework) tests
|
||||
```
|
||||
|
||||
## Generating the API documentation
|
||||
You should execute the 3 test suites before submitting a PR to github.
|
||||
|
||||
The following gulp task will generate the API docs in the `dist/angular.io/partials/api/angular2`:
|
||||
All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass.
|
||||
|
||||
```shell
|
||||
$(npm bin)/gulp docs/angular.io
|
||||
- CircleCI fails if your code is not formatted properly,
|
||||
- Travis CI fails if any of the test suite describe above fails.
|
||||
|
||||
## Update the public API tests
|
||||
|
||||
If you happen to modify the public API of Angular, API golden files must be updated using:
|
||||
|
||||
``` shell
|
||||
$ gulp public-api:update
|
||||
```
|
||||
|
||||
You can serve the generated documentation to check how it would render on [angular.io](https://angular.io/):
|
||||
- check out the [angular.io repo](https://github.com/angular/angular.io) locally,
|
||||
- install dependencies as described in the [angular.io README](https://github.com/angular/angular.io/blob/master/README.md),
|
||||
- copy the generated documentation from your local angular repo at `angular/dist/angular.io/partials/api/angular2` to your local angular.io repo at `angular.io/public/docs/js/latest/api`,
|
||||
- run `harp compile` at the root of the angular.io repo to check the generated documentation for errors,
|
||||
- run `harp server` and open a browser at `http://localhost:9000/docs/js/latest/api/` to check the rendered documentation.
|
||||
Note: The command `./test.sh tools` fails when the API doesn't match the golden files.
|
||||
|
||||
## Project Information
|
||||
## Formatting your source code
|
||||
|
||||
### Folder structure
|
||||
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
|
||||
is not properly formatted, the CI will fail and the PR can not be merged.
|
||||
|
||||
* `modules/*`: modules that will be loaded in the browser
|
||||
* `tools/*`: tools that are needed to build Angular
|
||||
* `dist/*`: build files are placed here.
|
||||
You can automatically format your code by running:
|
||||
|
||||
### File suffixes
|
||||
``` shell
|
||||
$ gulp format
|
||||
```
|
||||
|
||||
* `*.ts`: TypeScript files that get transpiled to Dart and EcmaScript 5/6
|
||||
* `*.dart`: Dart files that don't get transpiled
|
||||
|
||||
## CI using Travis
|
||||
|
||||
For instructions on setting up Continuous Integration using Travis, see the instructions given
|
||||
[here](https://github.com/angular/angular.dart/blob/master/travis.md).
|
||||
|
||||
## Transforming Dart code
|
||||
|
||||
See the [wiki](//github.com/angular/angular/wiki/Angular-2-Dart-Transformer).
|
||||
|
||||
## Debugging
|
||||
|
||||
### Debug the transpiler
|
||||
|
||||
If you need to debug the transpiler:
|
||||
|
||||
- add a `debugger;` statement in the transpiler code,
|
||||
- from the root folder, execute `node debug $(npm bin)/gulp build` to enter the node
|
||||
debugger
|
||||
- press "c" to execute the program until you reach the `debugger;` statement,
|
||||
- you can then type "repl" to enter the REPL and inspect variables in the context.
|
||||
|
||||
See the [Node.js manual](http://nodejs.org/api/debugger.html) for more information.
|
||||
|
||||
Notes:
|
||||
- You can also execute `node $(npm bin)/karma start karma-dart.conf.js` depending on which
|
||||
code you want to debug (the former will process the "modules" folder while the later processes
|
||||
the transpiler specs).
|
||||
- You can also add `debugger;` statements in the specs (JavaScript). The execution will halt when
|
||||
the developer tools are opened in the browser running Karma.
|
||||
|
||||
### Debug the tests
|
||||
|
||||
If you need to debug the tests:
|
||||
|
||||
- add a `debugger;` statement to the test you want to debug (or the source code),
|
||||
- execute karma `$(npm bin)/gulp test.js`,
|
||||
- press the top right "DEBUG" button,
|
||||
- open the DevTools and press F5,
|
||||
- the execution halts at the `debugger;` statement
|
||||
|
||||
**Note (WebStorm users)**:
|
||||
|
||||
1. Create a Karma run config from WebStorm.
|
||||
2. Then in the "Run" menu, press "Debug 'karma-js.conf.js'", and WebStorm will stop in the generated
|
||||
code on the `debugger;` statement.
|
||||
3. You can then step into the code and add watches.
|
||||
|
||||
The `debugger;` statement is needed because WebStorm will stop in a transpiled file. Breakpoints in
|
||||
the original source files are not supported at the moment.
|
||||
|
@ -12,7 +12,9 @@ Angular
|
||||
=========
|
||||
|
||||
Angular is a development platform for building mobile and desktop web applications. This is the
|
||||
repository for [Angular 2][ng2], both the JavaScript (JS) and [Dart][dart] versions.
|
||||
repository for [Angular 2][ng2] Typescript/JavaScript (JS).
|
||||
|
||||
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
|
||||
|
||||
Angular 2 is currently in **Release Candidate**.
|
||||
|
||||
@ -34,3 +36,4 @@ guidelines for [contributing][contributing] and then check out one of our issues
|
||||
[ng2]: http://angular.io
|
||||
[ngDart]: http://angulardart.org
|
||||
[ngJS]: http://angularjs.org
|
||||
[ng2dart]: https://github.com/dart-lang/angular2
|
||||
|
376
TOOLS_DART.md
376
TOOLS_DART.md
@ -1,376 +0,0 @@
|
||||
# Developer Tools for Dart
|
||||
|
||||
Use these tools and techniques to increase your app's performance
|
||||
and reliability.
|
||||
|
||||
* [Angular debugging tools](#angular-debugging-tools)
|
||||
* [Code size](#code-size)
|
||||
* [Performance](#performance)
|
||||
|
||||
|
||||
## Angular debugging tools
|
||||
|
||||
Starting with alpha.38, Angular provides a set of debugging tools
|
||||
that are accessible from any browser's developer console.
|
||||
In Chrome, you can get to the dev console by pressing
|
||||
Ctrl + Shift + J (on Mac: Cmd + Opt + J).
|
||||
|
||||
### Enabling the debugging tools
|
||||
|
||||
By default the debugging tools are disabled.
|
||||
Enable the debugging tools as follows:
|
||||
|
||||
```dart
|
||||
import 'package:angular2/platform/browser.dart';
|
||||
|
||||
main() async {
|
||||
var appRef = await bootstrap(Application);
|
||||
enableDebugTools(appRef);
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Change function name to enableDebuggingTools? -->
|
||||
|
||||
|
||||
### Using the debugging tools
|
||||
|
||||
In the browser, open the dev console. The top-level object is called `ng` and
|
||||
contains more specific tools inside it.
|
||||
|
||||
For example, to run the change detection profiler on your app:
|
||||
|
||||
```javascript
|
||||
// In the dev console:
|
||||
ng.profiler.timeChangeDetection();
|
||||
```
|
||||
|
||||
The [Change detection profiler](#change-detection-profiler) section
|
||||
has more details.
|
||||
<!-- Point to API docs when they're published, if they're useful.
|
||||
They should be under
|
||||
http://www.dartdocs.org/documentation/angular2/latest
|
||||
and/or
|
||||
https://angular.io/docs/js/latest/api/. -->
|
||||
|
||||
|
||||
## Code size
|
||||
|
||||
Code must be downloaded, parsed, and executed. Too much code can lead to
|
||||
slow application start-up time, especially on slow networks and low-end devices.
|
||||
The tools and techniques in this section can help you to identify
|
||||
unnecessarily large code and to reduce code size.
|
||||
|
||||
### Finding contributors to code size
|
||||
|
||||
Options for investigating code size include the `--dump-info` dart2js option,
|
||||
ng2soyc, `reflector.trackUsage()`, and code coverage information
|
||||
from the Dart VM.
|
||||
|
||||
#### Use --dump-info
|
||||
|
||||
The `--dump-info` option of `dart2js` outputs information about what happened
|
||||
during compilation. You can specify `--dump-info` in `pubspec.yaml`:
|
||||
|
||||
```yaml
|
||||
transformers:
|
||||
...
|
||||
- $dart2js:
|
||||
commandLineOptions:
|
||||
- --dump-info
|
||||
```
|
||||
|
||||
The [Dump Info Visualizer](https://github.com/dart-lang/dump-info-visualizer)
|
||||
can help you analyze the output.
|
||||
For more information, see the
|
||||
[dart2js_info API reference](http://dart-lang.github.io/dart2js_info/doc/api/).
|
||||
|
||||
#### Use ng2soyc.dart
|
||||
|
||||
[ng2soyc](https://github.com/angular/ng2soyc.dart) is a utility for analyzing
|
||||
code size contributors in Angular 2 applications. It groups code size by
|
||||
library and, assuming your library names follow
|
||||
[standard naming conventions](https://www.dartlang.org/articles/style-guide/#do-prefix-library-names-with-the-package-name-and-a-dot-separated-path)
|
||||
(package.library.sublibrary...), gives the code size breakdown at
|
||||
each level. To reduce noise in the output of very large apps, ng2soyc provides
|
||||
an option to hide libraries that are too small, so you can focus on the biggest
|
||||
contributors.
|
||||
|
||||
#### Find unused reflection data
|
||||
|
||||
Your app might have types that are annotated with `@Component` or `@Injectable`
|
||||
but never used.
|
||||
To find these unused types, use `reflector.trackUsage()` and then,
|
||||
after exercising your app, `reflector.listUnusedKeys()`.
|
||||
For example:
|
||||
|
||||
```
|
||||
import 'package:angular2/src/core/reflection/reflection.dart';
|
||||
...
|
||||
main() async {
|
||||
reflector.trackUsage();
|
||||
await bootstrap(AppComponent);
|
||||
print('Unused keys: ${reflector.listUnusedKeys()}');
|
||||
}
|
||||
```
|
||||
|
||||
When you run that code (in Dartium or another browser),
|
||||
you'll see a list of types that Angular _can_ inject but hasn't needed to.
|
||||
Consider removing those types or their `@Component`/`@Injectable` annotation
|
||||
to decrease your app's code size.
|
||||
|
||||
Three conditions must be true for `listUnusedKeys()` to return helpful data:
|
||||
|
||||
1. The angular2 transformer must run on the app.
|
||||
2. If you're running a JavaScript version of the app,
|
||||
the app must not be minified, so that the names are readable.
|
||||
3. You must exercise your app in as many ways as possible
|
||||
before calling `listUnusedKeys()`.
|
||||
Otherwise, you might get false positives:
|
||||
keys that haven't been used only because you didn't exercise
|
||||
the relevant feature of the app.
|
||||
|
||||
To run the angular2 transformer, first specify it in `pubspec.yaml`:
|
||||
|
||||
```
|
||||
name: hello_world
|
||||
...
|
||||
transformers:
|
||||
- angular2:
|
||||
entry_points: web/main.dart
|
||||
```
|
||||
|
||||
Then use pub to run the transformer. If you use `pub serve`,
|
||||
it provides both Dart and unminified (by default) JavaScript versions.
|
||||
If you want to serve actual files, then use `pub build` in debug mode
|
||||
to generate Dart and unminified JavaScript files:
|
||||
`pub build --mode=debug`.
|
||||
|
||||
The `reflector.trackUsage()` method makes Angular track the reflection
|
||||
information used by the app. Reflection information (`ReflectionInfo`) is a data
|
||||
structure that stores information that Angular uses for locating DI factories
|
||||
and for generating change detectors and other code related to a
|
||||
given type.
|
||||
|
||||
#### Use code coverage to find dead code
|
||||
|
||||
When running in Dartium (or in the Dart VM, in general) you can request code
|
||||
coverage information from the VM. You can either use
|
||||
[observatory](https://www.dartlang.org/tools/observatory/) or download
|
||||
the coverage file and use your own tools to inspect it. Lines of code that are
|
||||
not covered are top candidates for dead code.
|
||||
|
||||
Keep in mind, however, that uncovered code is not sufficient evidence of dead
|
||||
code, only necessary evidence. It is perfectly possible that you simply didn't
|
||||
exercise your application in a way that triggers the execution of uncovered
|
||||
code. A common example is error handling code. Just because your testing never
|
||||
encountered an error does not mean the error won't happen in production. You
|
||||
therefore don't have to rush and remove all the `catch` blocks.
|
||||
|
||||
### Reducing code size
|
||||
|
||||
To reduce code size, you can disable reflection,
|
||||
enable minification, and manually remove dead code.
|
||||
You can also try less safe options such as
|
||||
telling dart2js to trust type annotations.
|
||||
|
||||
|
||||
#### Disable reflection
|
||||
|
||||
`dart:mirrors` allows discovering program metadata at runtime. However, this
|
||||
means that `dart2js` needs to retain that metadata and thus increase the size
|
||||
of resulting JS output. In practice, however, it is possible to extract most
|
||||
metadata necessary for your metaprogramming tasks statically using a
|
||||
transformer and `package:analyzer`, and act on it before compiling to JS.
|
||||
|
||||
#### Enable minification
|
||||
|
||||
Minification shortens all your `longMethodNames` into 2- or 3-letter long
|
||||
symbols. `dart2js` ensures that this kind of renaming is done safely, without
|
||||
breaking the functionality of your programs. You can enable it in `pubspec.yaml`
|
||||
under `$dart2js` transformer:
|
||||
|
||||
```yaml
|
||||
transformers:
|
||||
...
|
||||
- $dart2js:
|
||||
minify: true
|
||||
```
|
||||
|
||||
#### Manually remove dead code
|
||||
|
||||
`dart2js` comes with dead code elimination out-of-the-box. However, it may not
|
||||
always be able to tell if a piece of code could be used. Consider the following
|
||||
example:
|
||||
|
||||
```dart
|
||||
/// This function decides which serialization format to use
|
||||
void setupSerializers() {
|
||||
if (server.doYouSupportProtocolBuffers()) {
|
||||
useProtobufSerializers();
|
||||
} else {
|
||||
useJsonSerializers();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example the application asks the server what kind of serialization
|
||||
format it uses and dynamically chooses one or the other. `dart2js` can't
|
||||
tell whether the server responds with yes or no, so it must retain both
|
||||
kinds of serializers. However, if you know that your server supports
|
||||
protocol buffers, you can remove that `if` block entirely and default to
|
||||
protocol buffers.
|
||||
|
||||
Code coverage (see above) is a good way to find dead code in your app.
|
||||
|
||||
#### Unsafe options
|
||||
|
||||
Dart also provides more aggressive optimization options. However, you have to
|
||||
be careful when using them and as of today the benefits aren't that clear. If
|
||||
your type annotations are inaccurate you may end up with non-Darty runtime
|
||||
behavior, including the classic "undefined is not a function" tautology, as
|
||||
well as the "keep on truckin'" behavior, e.g. `null + 1 == 1` and
|
||||
`{} + [] == 0`.
|
||||
|
||||
`--trust-type-annotations` tells `dart2js` to trust that your type annotations
|
||||
are correct. So if you have a function `foo(Bar bar)` the compiler can omit the
|
||||
check that `bar` is truly `Bar` when calling methods on it.
|
||||
|
||||
`--trust-primitives` tells `dart2js` that primitive types, such as numbers and
|
||||
booleans are never `null` when performing arithmetic, and that your program
|
||||
does not run into range error when operating on lists, letting the compiler
|
||||
remove some of the error checking code.
|
||||
|
||||
Specify these options in `pubspec.yaml`.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
transformers:
|
||||
...
|
||||
- $dart2js:
|
||||
commandLineOptions:
|
||||
- --trust-type-annotations
|
||||
- --trust-primitives
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
### Change detection profiler
|
||||
|
||||
If your application is janky (it misses frames) or is slow according to other
|
||||
metrics, you need to find out why. This tool helps by measuring the average
|
||||
speed of _change detection_, a phase in Angular's
|
||||
lifecycle that detects changes in values that are bound to the UI.
|
||||
Janky UI updates can result from slowness either in _computing_ the changes or
|
||||
in _applying_ those changes to the UI.
|
||||
|
||||
For your app to be performant, the process of _computing_ changes must be very
|
||||
fast—preferably **under 3 milliseconds**.
|
||||
Fast change computation leaves room for
|
||||
the application logic, UI updates, and browser rendering pipeline
|
||||
to fit within a 16 ms frame (assuming a target frame rate of 60 FPS).
|
||||
|
||||
The change detection profiler repeatedly performs change detection
|
||||
without invoking any user actions, such as clicking buttons or entering
|
||||
text in input fields. It then computes the average amount of time
|
||||
(in milliseconds) to perform a single cycle of change detection and
|
||||
prints that to the console. This number depends on the current state of the UI. You are likely to see different numbers
|
||||
as you go from one screen in your application to another.
|
||||
|
||||
#### Running the profiler
|
||||
|
||||
Before running the profiler, enable the debugging tools
|
||||
and put the app into the state you want to measure:
|
||||
|
||||
1. If you haven't already done so,
|
||||
[enable the debugging tools](#enabling-the-debugging-tools).
|
||||
2. Navigate the app to a screen whose performance you want to profile.
|
||||
3. Make sure the screen is in a state that you want to measure.
|
||||
For example, you might want to profile the screen several times,
|
||||
with different amounts and kinds of data.
|
||||
|
||||
To run the profiler, enter the following in the dev console:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection();
|
||||
```
|
||||
|
||||
The results are visible in the console.
|
||||
|
||||
|
||||
#### Recording CPU profiles
|
||||
|
||||
To record a profile, pass `{record: true}` to `timeChangeDetection()`:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection({record: true});
|
||||
```
|
||||
|
||||
Then open the **Profiles** tab. The recorded profile has the title
|
||||
**Change Detection**. In Chrome, if you record the profile repeatedly, all the
|
||||
profiles are nested under Change Detection.
|
||||
|
||||
|
||||
#### Interpreting the numbers
|
||||
|
||||
In a properly designed application, repeated attempts to detect changes without
|
||||
any user actions result in no changes to the UI. It is
|
||||
also desirable to have the cost of a user action be proportional to the amount
|
||||
of UI changes required. For example, popping up a menu with 5 items should be
|
||||
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
|
||||
change detection with no UI updates should be as fast as possible.
|
||||
|
||||
#### Investigating slow change detection
|
||||
|
||||
So you found a screen in your application on which the profiler reports a very
|
||||
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
|
||||
recording while profiling:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection({record: true});
|
||||
```
|
||||
|
||||
Then look for hot spots using
|
||||
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
|
||||
|
||||
#### Reducing change detection cost
|
||||
|
||||
There are many reasons for slow change detection. To gain intuition about
|
||||
possible causes it helps to understand how change detection works. Such a
|
||||
discussion is outside the scope of this document,
|
||||
but here are some key concepts.
|
||||
|
||||
<!-- TODO: link to change detection docs -->
|
||||
|
||||
By default, Angular uses a _dirty checking_ mechanism to find model changes.
|
||||
This mechanism involves evaluating every bound expression that's active on the
|
||||
UI. These usually include text interpolation via `{{expression}}` and property
|
||||
bindings via `[prop]="expression"`. If any of the evaluated expressions are
|
||||
costly to compute, they might contribute to slow change detection. A good way to
|
||||
speed things up is to use plain class fields in your expressions and avoid any
|
||||
kind of computation. For example:
|
||||
|
||||
```dart
|
||||
@View(
|
||||
template: '<button [enabled]="isEnabled">{{title}}</button>'
|
||||
)
|
||||
class FancyButton {
|
||||
// GOOD: no computation, just returns the value
|
||||
bool isEnabled;
|
||||
|
||||
// BAD: computes the final value upon request
|
||||
String _title;
|
||||
String get title => _title.trim().toUpperCase();
|
||||
}
|
||||
```
|
||||
|
||||
Most cases like these can be solved by precomputing the value and storing the
|
||||
final value in a field.
|
||||
|
||||
Angular also supports a second type of change detection: the _push_ model. In
|
||||
this model, Angular does not poll your component for changes. Instead, the
|
||||
component tells Angular when it changes, and only then does Angular perform
|
||||
the update. This model is suitable in situations when your data model uses
|
||||
observable or immutable objects.
|
||||
|
||||
<!-- TODO: link to discussion of push model -->
|
@ -13,7 +13,7 @@ var CIconfiguration = {
|
||||
'IE9': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'IE10': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'IE11': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Edge': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Edge': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.1': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.2': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.3': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
@ -38,7 +38,7 @@ var customLaunchers = {
|
||||
'SL_CHROME': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: '50'
|
||||
version: '52'
|
||||
},
|
||||
'SL_CHROMEBETA': {
|
||||
base: 'SauceLabs',
|
||||
@ -53,7 +53,7 @@ var customLaunchers = {
|
||||
'SL_FIREFOX': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '45'
|
||||
version: '46'
|
||||
},
|
||||
'SL_FIREFOXBETA': {
|
||||
base: 'SauceLabs',
|
||||
@ -69,13 +69,13 @@ var customLaunchers = {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.9',
|
||||
version: '7'
|
||||
version: '7.0'
|
||||
},
|
||||
'SL_SAFARI8': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.10',
|
||||
version: '8'
|
||||
version: '8.0'
|
||||
},
|
||||
'SL_SAFARI9': {
|
||||
base: 'SauceLabs',
|
||||
@ -99,7 +99,7 @@ var customLaunchers = {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'iphone',
|
||||
platform: 'OS X 10.10',
|
||||
version: '9.1'
|
||||
version: '9.3'
|
||||
},
|
||||
'SL_IE9': {
|
||||
base: 'SauceLabs',
|
||||
@ -202,7 +202,7 @@ var customLaunchers = {
|
||||
base: 'BrowserStack',
|
||||
device: 'iPhone 6S',
|
||||
os: 'ios',
|
||||
os_version: '9.0'
|
||||
os_version: '9.1'
|
||||
},
|
||||
'BS_IE9': {
|
||||
base: 'BrowserStack',
|
||||
|
49
build.sh
49
build.sh
@ -35,7 +35,7 @@ cd -
|
||||
TSCONFIG=./modules/tsconfig.json
|
||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||
# compile ts code
|
||||
TSC="node dist/tools/@angular/tsc-wrapped/src/main"
|
||||
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
||||
$TSC -p modules/tsconfig.json
|
||||
|
||||
rm -rf ./dist/packages-dist
|
||||
@ -73,12 +73,12 @@ do
|
||||
|
||||
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
|
||||
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
|
||||
if [[ ${TRAVIS} ]]; then
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -e 's/\(^ *(static |private )*\)*readonly */\1/g'
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -e 's/\(^ *(static |private )*\)*readonly */\1/g'
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||
else
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -e 's/\(^ *(static |private )*\)*readonly */\1/g'
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -e 's/\(^ *(static |private )*\)*readonly */\1/g'
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||
fi
|
||||
|
||||
if [[ ${PACKAGE} != compiler-cli ]]; then
|
||||
@ -94,27 +94,28 @@ do
|
||||
echo "====== BUNDLING: ${SRCDIR} ====="
|
||||
mkdir ${DESTDIR}/bundles
|
||||
|
||||
if [[ ${PACKAGE} != router ]]; then
|
||||
(
|
||||
cd ${SRCDIR}
|
||||
echo "..." # here just to have grep match something and not exit with 1
|
||||
../../../node_modules/.bin/rollup -c rollup.config.js
|
||||
) 2>&1 | grep -v "as external dependency"
|
||||
(
|
||||
cd ${SRCDIR}
|
||||
echo "..." # here just to have grep match something and not exit with 1
|
||||
../../../node_modules/.bin/rollup -c rollup.config.js
|
||||
) 2>&1 | grep -v "as external dependency"
|
||||
|
||||
$(npm bin)/tsc \
|
||||
--out ${UMD_ES5_PATH} \
|
||||
--target es5 \
|
||||
--lib "es6,dom" \
|
||||
--allowJs \
|
||||
${UMD_ES6_PATH}
|
||||
$(npm bin)/tsc \
|
||||
--out ${UMD_ES5_PATH} \
|
||||
--target es5 \
|
||||
--lib "es6,dom" \
|
||||
--allowJs \
|
||||
${UMD_ES6_PATH}
|
||||
|
||||
rm ${UMD_ES6_PATH}
|
||||
rm ${UMD_ES6_PATH}
|
||||
|
||||
cat ./modules/@angular/license-banner.txt > ${UMD_ES5_PATH}.tmp
|
||||
cat ${UMD_ES5_PATH} >> ${UMD_ES5_PATH}.tmp
|
||||
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
|
||||
cat ./modules/@angular/license-banner.txt > ${UMD_ES5_PATH}.tmp
|
||||
cat ${UMD_ES5_PATH} >> ${UMD_ES5_PATH}.tmp
|
||||
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
|
||||
|
||||
$(npm bin)/uglifyjs -c --screw-ie8 -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
|
||||
fi
|
||||
$(npm bin)/uglifyjs -c --screw-ie8 -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
|
||||
fi
|
||||
done
|
||||
|
||||
echo "====== COMPILING: \$(npm bin)/tsc -p benchpress/tsconfig.json ====="
|
||||
$(npm bin)/tsc -p ./modules/benchpress/tsconfig.json
|
||||
|
113
gulpfile.js
113
gulpfile.js
@ -8,8 +8,11 @@ require('./tools/check-environment')(
|
||||
|
||||
const gulp = require('gulp');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
const srcsToFmt = ['tools/**/*.ts', 'modules/@angular/**/*.ts'];
|
||||
const srcsToFmt =
|
||||
['tools/**/*.ts', 'modules/@angular/**/*.ts', '!tools/public_api_guard/**/*.d.ts',
|
||||
'modules/benchpress/**/*.ts', 'modules/playground/**/*.ts'];
|
||||
|
||||
gulp.task('format:enforce', () => {
|
||||
const format = require('gulp-clang-format');
|
||||
@ -25,12 +28,77 @@ gulp.task('format', () => {
|
||||
format.format('file', clangFormat)).pipe(gulp.dest('.'));
|
||||
});
|
||||
|
||||
const entrypoints = [
|
||||
'dist/packages-dist/core/index.d.ts',
|
||||
'dist/packages-dist/core/testing.d.ts',
|
||||
'dist/packages-dist/common/index.d.ts',
|
||||
'dist/packages-dist/common/testing.d.ts',
|
||||
// The API surface of the compiler is currently unstable - all of the important APIs are exposed
|
||||
// via @angular/core, @angular/platform-browser or @angular/platform-browser-dynamic instead.
|
||||
//'dist/packages-dist/compiler/index.d.ts',
|
||||
//'dist/packages-dist/compiler/testing.d.ts',
|
||||
'dist/packages-dist/upgrade/index.d.ts',
|
||||
'dist/packages-dist/platform-browser/index.d.ts',
|
||||
'dist/packages-dist/platform-browser/testing.d.ts',
|
||||
'dist/packages-dist/platform-browser-dynamic/index.d.ts',
|
||||
'dist/packages-dist/platform-browser-dynamic/testing.d.ts',
|
||||
'dist/packages-dist/platform-server/index.d.ts',
|
||||
'dist/packages-dist/platform-server/testing.d.ts',
|
||||
'dist/packages-dist/http/index.d.ts',
|
||||
'dist/packages-dist/http/testing.d.ts',
|
||||
'dist/packages-dist/forms/index.d.ts',
|
||||
'dist/packages-dist/router/index.d.ts'
|
||||
];
|
||||
const publicApiDir = path.normalize('tools/public_api_guard');
|
||||
const publicApiArgs = [
|
||||
'--rootDir', 'dist/packages-dist',
|
||||
'--stripExportPattern', '^__',
|
||||
'--allowModuleIdentifiers', 'jasmine',
|
||||
'--allowModuleIdentifiers', 'protractor',
|
||||
'--allowModuleIdentifiers', 'angular',
|
||||
'--onStabilityMissing', 'error'
|
||||
].concat(entrypoints);
|
||||
|
||||
gulp.task('build.sh', (done) => {
|
||||
const childProcess = require('child_process');
|
||||
|
||||
childProcess.exec(path.join(__dirname, 'build.sh'), error => done(error));
|
||||
});
|
||||
|
||||
// Note that these two commands work on built d.ts files instead of the source
|
||||
gulp.task('public-api:enforce', (done) => {
|
||||
const childProcess = require('child_process');
|
||||
|
||||
childProcess
|
||||
.spawn(
|
||||
path.join(__dirname, `/node_modules/.bin/ts-api-guardian${/^win/.test(os.platform()) ? '.cmd' : ''}`),
|
||||
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', (errorCode) => {
|
||||
if (errorCode !== 0) {
|
||||
done(new Error(
|
||||
'Public API differs from golden file. Please run `gulp public-api:update`.'));
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('public-api:update', ['build.sh'], (done) => {
|
||||
const childProcess = require('child_process');
|
||||
|
||||
childProcess
|
||||
.spawn(
|
||||
path.join(__dirname, `/node_modules/.bin/ts-api-guardian${/^win/.test(os.platform()) ? '.cmd' : ''}`),
|
||||
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', (errorCode) => done(errorCode));
|
||||
});
|
||||
|
||||
gulp.task('lint', ['format:enforce', 'tools:build'], () => {
|
||||
const tslint = require('gulp-tslint');
|
||||
// Built-in rules are at
|
||||
// https://github.com/palantir/tslint#supported-rules
|
||||
const tslintConfig = require('./tslint.json');
|
||||
return gulp.src(['modules/@angular/**/*.ts', '!modules/@angular/*/test/**'])
|
||||
return gulp.src(['modules/@angular/**/*.ts', 'modules/benchpress/**/*.ts'])
|
||||
.pipe(tslint({
|
||||
tslint: require('tslint').default,
|
||||
configuration: tslintConfig,
|
||||
@ -41,6 +109,22 @@ gulp.task('lint', ['format:enforce', 'tools:build'], () => {
|
||||
|
||||
gulp.task('tools:build', (done) => { tsc('tools/', done); });
|
||||
|
||||
gulp.task('check-cycle', (done) => {
|
||||
const madge = require('madge');
|
||||
|
||||
var dependencyObject = madge(['dist/all/'], {
|
||||
format: 'cjs',
|
||||
extensions: ['.js'],
|
||||
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); }
|
||||
});
|
||||
var circularDependencies = dependencyObject.circular().getArray();
|
||||
if (circularDependencies.length > 0) {
|
||||
console.log('Found circular dependencies!');
|
||||
console.log(circularDependencies);
|
||||
process.exit(1);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task('serve', () => {
|
||||
let connect = require('gulp-connect');
|
||||
@ -56,12 +140,29 @@ gulp.task('serve', () => {
|
||||
});
|
||||
|
||||
|
||||
function tsc(projectPath, done) {
|
||||
let child_process = require('child_process');
|
||||
gulp.task('changelog', () => {
|
||||
const conventionalChangelog = require('gulp-conventional-changelog');
|
||||
|
||||
child_process
|
||||
return gulp.src('CHANGELOG.md')
|
||||
.pipe(conventionalChangelog({
|
||||
preset: 'angular',
|
||||
releaseCount: 1
|
||||
}, {
|
||||
// Conventional Changelog Context
|
||||
// We have to manually set version number so it doesn't get prefixed with `v`
|
||||
// See https://github.com/conventional-changelog/conventional-changelog-core/issues/10
|
||||
currentTag: require('./package.json').version
|
||||
}))
|
||||
.pipe(gulp.dest('./'));
|
||||
});
|
||||
|
||||
function tsc(projectPath, done) {
|
||||
const childProcess = require('child_process');
|
||||
|
||||
childProcess
|
||||
.spawn(
|
||||
`${__dirname}/node_modules/.bin/tsc`, ['-p', path.join(__dirname, projectPath)],
|
||||
path.normalize(`${__dirname}/node_modules/.bin/tsc`) + (/^win/.test(os.platform()) ? '.cmd' : ''),
|
||||
['-p', path.join(__dirname, projectPath)],
|
||||
{stdio: 'inherit'})
|
||||
.on('close', (errorCode) => done(errorCode));
|
||||
}
|
||||
|
1583
gulpfile.js.old
1583
gulpfile.js.old
File diff suppressed because it is too large
Load Diff
@ -38,6 +38,7 @@ module.exports = function(config) {
|
||||
exclude: [
|
||||
'dist/all/@angular/**/e2e_test/**',
|
||||
'dist/all/@angular/examples/**',
|
||||
'dist/all/@angular/router/**',
|
||||
'dist/all/@angular/compiler-cli/**',
|
||||
'dist/all/angular1_router.js',
|
||||
'dist/all/@angular/platform-browser/testing/e2e_util.js'
|
||||
|
@ -1,6 +0,0 @@
|
||||
Angular2
|
||||
=========
|
||||
|
||||
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. This is the repository for the upcoming 2.0 version. If you're looking for the current official version of Angular you should go to [angular/angular.js](https://github.com/angular/angular.js)
|
||||
|
||||
License: Apache MIT 2.0
|
@ -1 +0,0 @@
|
||||
export 'index.dart';
|
@ -1,5 +1,30 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgModule} from '@angular/core';
|
||||
import {COMMON_DIRECTIVES} from './src/common_directives';
|
||||
import {COMMON_PIPES} from './src/pipes';
|
||||
|
||||
export * from './src/pipes';
|
||||
export * from './src/directives';
|
||||
export * from './src/forms-deprecated';
|
||||
export * from './src/common_directives';
|
||||
export * from './src/location';
|
||||
export {NgLocalization} from './src/localization';
|
||||
|
||||
// Note: This does not contain the location providers,
|
||||
// as they need some platform specific implementations to work.
|
||||
/**
|
||||
* The module that includes all the basic Angular directives like {@link NgIf}, ${link NgFor}, ...
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@NgModule(
|
||||
{declarations: [COMMON_DIRECTIVES, COMMON_PIPES], exports: [COMMON_DIRECTIVES, COMMON_PIPES]})
|
||||
export class CommonModule {
|
||||
}
|
||||
|
@ -1,8 +1,14 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Type} from '@angular/core';
|
||||
|
||||
import {CORE_DIRECTIVES} from './directives';
|
||||
import {FORM_DIRECTIVES} from './forms-deprecated';
|
||||
|
||||
|
||||
/**
|
||||
* A collection of Angular core directives that are likely to be used in each and every Angular
|
||||
@ -49,4 +55,4 @@ import {FORM_DIRECTIVES} from './forms-deprecated';
|
||||
*
|
||||
* @experimental Contains forms which are experimental.
|
||||
*/
|
||||
export const COMMON_DIRECTIVES: Type[][] = /*@ts2dart_const*/[CORE_DIRECTIVES, FORM_DIRECTIVES];
|
||||
export const COMMON_DIRECTIVES: Type[][] = [CORE_DIRECTIVES];
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
@ -7,7 +15,7 @@ export {CORE_DIRECTIVES} from './directives/core_directives';
|
||||
export {NgClass} from './directives/ng_class';
|
||||
export {NgFor} from './directives/ng_for';
|
||||
export {NgIf} from './directives/ng_if';
|
||||
export {NgLocalization, NgPlural, NgPluralCase} from './directives/ng_plural';
|
||||
export {NgPlural, NgPluralCase} from './directives/ng_plural';
|
||||
export {NgStyle} from './directives/ng_style';
|
||||
export {NgSwitch, NgSwitchCase, NgSwitchDefault} from './directives/ng_switch';
|
||||
export {NgTemplateOutlet} from './directives/ng_template_outlet';
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Type} from '../facade/lang';
|
||||
|
||||
import {NgClass} from './ng_class';
|
||||
@ -8,8 +16,6 @@ import {NgStyle} from './ng_style';
|
||||
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from './ng_switch';
|
||||
import {NgTemplateOutlet} from './ng_template_outlet';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A collection of Angular core directives that are likely to be used in each and every Angular
|
||||
* application.
|
||||
@ -52,7 +58,7 @@ import {NgTemplateOutlet} from './ng_template_outlet';
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export const CORE_DIRECTIVES: Type[] = /*@ts2dart_const*/[
|
||||
export const CORE_DIRECTIVES: Type[] = [
|
||||
NgClass,
|
||||
NgFor,
|
||||
NgIf,
|
||||
|
@ -1,9 +1,18 @@
|
||||
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, OnDestroy, Renderer} from '@angular/core';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||
|
||||
import {StringMapWrapper, isListLikeIterable} from '../facade/collection';
|
||||
import {isArray, isPresent, isString} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `NgClass` directive conditionally adds and removes CSS classes on an HTML element based on
|
||||
* an expression's evaluation result.
|
||||
@ -65,8 +74,8 @@ import {isArray, isPresent, isString} from '../facade/lang';
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngClass]', inputs: ['rawClass: ngClass', 'initialClasses: class']})
|
||||
export class NgClass implements DoCheck, OnDestroy {
|
||||
@Directive({selector: '[ngClass]'})
|
||||
export class NgClass implements DoCheck {
|
||||
private _iterableDiffer: IterableDiffer;
|
||||
private _keyValueDiffer: KeyValueDiffer;
|
||||
private _initialClasses: string[] = [];
|
||||
@ -76,6 +85,8 @@ export class NgClass implements DoCheck, OnDestroy {
|
||||
private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
||||
private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||
|
||||
|
||||
@Input('class')
|
||||
set initialClasses(v: string) {
|
||||
this._applyInitialClasses(true);
|
||||
this._initialClasses = isPresent(v) && isString(v) ? v.split(' ') : [];
|
||||
@ -83,7 +94,8 @@ export class NgClass implements DoCheck, OnDestroy {
|
||||
this._applyClasses(this._rawClass, false);
|
||||
}
|
||||
|
||||
set rawClass(v: string|string[]|Set<string>|{[key: string]: any}) {
|
||||
@Input()
|
||||
set ngClass(v: string|string[]|Set<string>|{[key: string]: any}) {
|
||||
this._cleanupClasses(this._rawClass);
|
||||
|
||||
if (isString(v)) {
|
||||
@ -117,8 +129,6 @@ export class NgClass implements DoCheck, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void { this._cleanupClasses(this._rawClass); }
|
||||
|
||||
private _cleanupClasses(rawClassVal: string[]|Set<string>|{[key: string]: any}): void {
|
||||
this._applyClasses(rawClassVal, true);
|
||||
this._applyInitialClasses(false);
|
||||
|
@ -1,4 +1,12 @@
|
||||
import {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, IterableDiffer, IterableDiffers, TemplateRef, TrackByFn, ViewContainerRef} from '@angular/core';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {BaseException} from '../facade/exceptions';
|
||||
import {getTypeNameForDebugging, isBlank, isPresent} from '../facade/lang';
|
||||
@ -68,76 +76,76 @@ export class NgForRow {
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngFor][ngForOf]', inputs: ['ngForTrackBy', 'ngForOf', 'ngForTemplate']})
|
||||
export class NgFor implements DoCheck {
|
||||
/** @internal */
|
||||
_ngForOf: any;
|
||||
/** @internal */
|
||||
_ngForTrackBy: TrackByFn;
|
||||
@Directive({selector: '[ngFor][ngForOf]'})
|
||||
export class NgFor implements DoCheck, OnChanges {
|
||||
@Input() ngForOf: any;
|
||||
@Input() ngForTrackBy: TrackByFn;
|
||||
|
||||
private _differ: IterableDiffer;
|
||||
|
||||
constructor(
|
||||
private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<NgForRow>,
|
||||
private _iterableDiffers: IterableDiffers, private _cdr: ChangeDetectorRef) {}
|
||||
|
||||
set ngForOf(value: any) {
|
||||
this._ngForOf = value;
|
||||
if (isBlank(this._differ) && isPresent(value)) {
|
||||
try {
|
||||
this._differ = this._iterableDiffers.find(value).create(this._cdr, this._ngForTrackBy);
|
||||
} catch (e) {
|
||||
throw new BaseException(
|
||||
`Cannot find a differ supporting object '${value}' of type '${getTypeNameForDebugging(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngForTemplate(value: TemplateRef<NgForRow>) {
|
||||
if (isPresent(value)) {
|
||||
this._templateRef = value;
|
||||
}
|
||||
}
|
||||
|
||||
set ngForTrackBy(value: TrackByFn) { this._ngForTrackBy = value; }
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if ('ngForOf' in changes) {
|
||||
// React on ngForOf changes only once all inputs have been initialized
|
||||
const value = changes['ngForOf'].currentValue;
|
||||
if (isBlank(this._differ) && isPresent(value)) {
|
||||
try {
|
||||
this._differ = this._iterableDiffers.find(value).create(this._cdr, this.ngForTrackBy);
|
||||
} catch (e) {
|
||||
throw new BaseException(
|
||||
`Cannot find a differ supporting object '${value}' of type '${getTypeNameForDebugging(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck() {
|
||||
if (isPresent(this._differ)) {
|
||||
var changes = this._differ.diff(this._ngForOf);
|
||||
const changes = this._differ.diff(this.ngForOf);
|
||||
if (isPresent(changes)) this._applyChanges(changes);
|
||||
}
|
||||
}
|
||||
|
||||
private _applyChanges(changes: DefaultIterableDiffer) {
|
||||
// TODO(rado): check if change detection can produce a change record that is
|
||||
// easier to consume than current.
|
||||
var recordViewTuples: RecordViewTuple[] = [];
|
||||
changes.forEachRemovedItem(
|
||||
(removedRecord: CollectionChangeRecord) =>
|
||||
recordViewTuples.push(new RecordViewTuple(removedRecord, null)));
|
||||
const insertTuples: RecordViewTuple[] = [];
|
||||
changes.forEachOperation(
|
||||
(item: CollectionChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => {
|
||||
if (item.previousIndex == null) {
|
||||
let view = this._viewContainer.createEmbeddedView(
|
||||
this._templateRef, new NgForRow(null, null, null), currentIndex);
|
||||
let tuple = new RecordViewTuple(item, view);
|
||||
insertTuples.push(tuple);
|
||||
} else if (currentIndex == null) {
|
||||
this._viewContainer.remove(adjustedPreviousIndex);
|
||||
} else {
|
||||
let view = this._viewContainer.get(adjustedPreviousIndex);
|
||||
this._viewContainer.move(view, currentIndex);
|
||||
let tuple = new RecordViewTuple(item, <EmbeddedViewRef<NgForRow>>view);
|
||||
insertTuples.push(tuple);
|
||||
}
|
||||
});
|
||||
|
||||
changes.forEachMovedItem(
|
||||
(movedRecord: CollectionChangeRecord) =>
|
||||
recordViewTuples.push(new RecordViewTuple(movedRecord, null)));
|
||||
|
||||
var insertTuples = this._bulkRemove(recordViewTuples);
|
||||
|
||||
changes.forEachAddedItem(
|
||||
(addedRecord: CollectionChangeRecord) =>
|
||||
insertTuples.push(new RecordViewTuple(addedRecord, null)));
|
||||
|
||||
this._bulkInsert(insertTuples);
|
||||
|
||||
for (var i = 0; i < insertTuples.length; i++) {
|
||||
for (let i = 0; i < insertTuples.length; i++) {
|
||||
this._perViewChange(insertTuples[i].view, insertTuples[i].record);
|
||||
}
|
||||
|
||||
for (var i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
||||
viewRef.context.index = i;
|
||||
viewRef.context.count = ilen;
|
||||
}
|
||||
|
||||
changes.forEachIdentityChange((record: any /** TODO #9100 */) => {
|
||||
changes.forEachIdentityChange((record: any) => {
|
||||
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
||||
viewRef.context.$implicit = record.item;
|
||||
});
|
||||
@ -146,46 +154,8 @@ export class NgFor implements DoCheck {
|
||||
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: CollectionChangeRecord) {
|
||||
view.context.$implicit = record.item;
|
||||
}
|
||||
|
||||
private _bulkRemove(tuples: RecordViewTuple[]): RecordViewTuple[] {
|
||||
tuples.sort(
|
||||
(a: RecordViewTuple, b: RecordViewTuple) =>
|
||||
a.record.previousIndex - b.record.previousIndex);
|
||||
var movedTuples: RecordViewTuple[] = [];
|
||||
for (var i = tuples.length - 1; i >= 0; i--) {
|
||||
var tuple = tuples[i];
|
||||
// separate moved views from removed views.
|
||||
if (isPresent(tuple.record.currentIndex)) {
|
||||
tuple.view =
|
||||
<EmbeddedViewRef<NgForRow>>this._viewContainer.detach(tuple.record.previousIndex);
|
||||
movedTuples.push(tuple);
|
||||
} else {
|
||||
this._viewContainer.remove(tuple.record.previousIndex);
|
||||
}
|
||||
}
|
||||
return movedTuples;
|
||||
}
|
||||
|
||||
private _bulkInsert(tuples: RecordViewTuple[]): RecordViewTuple[] {
|
||||
tuples.sort((a, b) => a.record.currentIndex - b.record.currentIndex);
|
||||
for (var i = 0; i < tuples.length; i++) {
|
||||
var tuple = tuples[i];
|
||||
if (isPresent(tuple.view)) {
|
||||
this._viewContainer.insert(tuple.view, tuple.record.currentIndex);
|
||||
} else {
|
||||
tuple.view = this._viewContainer.createEmbeddedView(
|
||||
this._templateRef, new NgForRow(null, null, null), tuple.record.currentIndex);
|
||||
}
|
||||
}
|
||||
return tuples;
|
||||
}
|
||||
}
|
||||
|
||||
class RecordViewTuple {
|
||||
view: EmbeddedViewRef<NgForRow>;
|
||||
record: any;
|
||||
constructor(record: any, view: EmbeddedViewRef<NgForRow>) {
|
||||
this.record = record;
|
||||
this.view = view;
|
||||
}
|
||||
constructor(public record: any, public view: EmbeddedViewRef<NgForRow>) {}
|
||||
}
|
||||
|
@ -1,8 +1,17 @@
|
||||
import {Directive, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {isBlank} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Removes or recreates a portion of the DOM tree based on an {expression}.
|
||||
*
|
||||
@ -27,14 +36,15 @@ import {isBlank} from '../facade/lang';
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngIf]', inputs: ['ngIf']})
|
||||
@Directive({selector: '[ngIf]'})
|
||||
export class NgIf {
|
||||
private _prevCondition: boolean = null;
|
||||
|
||||
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<Object>) {
|
||||
}
|
||||
|
||||
set ngIf(newCondition: any /* boolean */) {
|
||||
@Input()
|
||||
set ngIf(newCondition: any) {
|
||||
if (newCondition && (isBlank(this._prevCondition) || !this._prevCondition)) {
|
||||
this._prevCondition = true;
|
||||
this._viewContainer.createEmbeddedView(this._templateRef);
|
||||
|
@ -1,16 +1,18 @@
|
||||
import {AfterContentInit, Attribute, ContentChildren, Directive, Input, QueryList, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Map} from '../facade/collection';
|
||||
import {NumberWrapper, isPresent} from '../facade/lang';
|
||||
import {Attribute, Directive, Host, Input, OnInit, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {NgLocalization, getPluralCategory} from '../localization';
|
||||
|
||||
import {SwitchView} from './ng_switch';
|
||||
|
||||
const _CATEGORY_DEFAULT = 'other';
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export abstract class NgLocalization { abstract getPluralCategory(value: any): string; }
|
||||
|
||||
/**
|
||||
* `ngPlural` is an i18n directive that displays DOM sub-trees that match the switch expression
|
||||
@ -27,9 +29,6 @@ export abstract class NgLocalization { abstract getPluralCategory(value: any): s
|
||||
* value matches aren't found and the value maps to its category using the `getPluralCategory`
|
||||
* function provided.
|
||||
*
|
||||
* If no matching views are found for a switch expression, inner elements marked
|
||||
* `[ngPluralCase]="other"` will be displayed.
|
||||
*
|
||||
* ```typescript
|
||||
* class MyLocalization extends NgLocalization {
|
||||
* getPluralCategory(value: any) {
|
||||
@ -68,28 +67,11 @@ export abstract class NgLocalization { abstract getPluralCategory(value: any): s
|
||||
* ```
|
||||
* @experimental
|
||||
*/
|
||||
|
||||
@Directive({selector: '[ngPluralCase]'})
|
||||
export class NgPluralCase {
|
||||
/** @internal */
|
||||
_view: SwitchView;
|
||||
constructor(
|
||||
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
|
||||
viewContainer: ViewContainerRef) {
|
||||
this._view = new SwitchView(viewContainer, template);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngPlural]'})
|
||||
export class NgPlural implements AfterContentInit {
|
||||
export class NgPlural {
|
||||
private _switchValue: number;
|
||||
private _activeView: SwitchView;
|
||||
private _caseViews = new Map<any, SwitchView>();
|
||||
@ContentChildren(NgPluralCase) cases: QueryList<NgPluralCase> = null;
|
||||
private _caseViews: {[k: string]: SwitchView} = {};
|
||||
|
||||
constructor(private _localization: NgLocalization) {}
|
||||
|
||||
@ -99,21 +81,15 @@ export class NgPlural implements AfterContentInit {
|
||||
this._updateView();
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
this.cases.forEach((pluralCase: NgPluralCase): void => {
|
||||
this._caseViews.set(this._formatValue(pluralCase), pluralCase._view);
|
||||
});
|
||||
this._updateView();
|
||||
}
|
||||
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
|
||||
|
||||
/** @internal */
|
||||
_updateView(): void {
|
||||
this._clearViews();
|
||||
|
||||
var view: SwitchView = this._caseViews.get(this._switchValue);
|
||||
if (!isPresent(view)) view = this._getCategoryView(this._switchValue);
|
||||
|
||||
this._activateView(view);
|
||||
var key =
|
||||
getPluralCategory(this._switchValue, Object.keys(this._caseViews), this._localization);
|
||||
this._activateView(this._caseViews[key]);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@ -127,22 +103,16 @@ export class NgPlural implements AfterContentInit {
|
||||
this._activeView = view;
|
||||
this._activeView.create();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getCategoryView(value: number): SwitchView {
|
||||
var category: string = this._localization.getPluralCategory(value);
|
||||
var categoryView: SwitchView = this._caseViews.get(category);
|
||||
return isPresent(categoryView) ? categoryView : this._caseViews.get(_CATEGORY_DEFAULT);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_isValueView(pluralCase: NgPluralCase): boolean { return pluralCase.value[0] === '='; }
|
||||
|
||||
/** @internal */
|
||||
_formatValue(pluralCase: NgPluralCase): any {
|
||||
return this._isValueView(pluralCase) ? this._stripValue(pluralCase.value) : pluralCase.value;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_stripValue(value: string): number { return NumberWrapper.parseInt(value.substring(1), 10); }
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngPluralCase]'})
|
||||
export class NgPluralCase {
|
||||
constructor(
|
||||
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
|
||||
viewContainer: ViewContainerRef, @Host() ngPlural: NgPlural) {
|
||||
ngPlural.addCase(value, new SwitchView(viewContainer, template));
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,17 @@
|
||||
import {Directive, DoCheck, ElementRef, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `NgStyle` directive changes styles based on a result of expression evaluation.
|
||||
*
|
||||
@ -12,7 +21,8 @@ import {isBlank, isPresent} from '../facade/lang';
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<div [ngStyle]="{'font-style': style}"></div>`
|
||||
* - `<div [ngStyle]="{'font-style': styleExp}"></div>`
|
||||
* - `<div [ngStyle]="{'max-width.px': widthExp}"></div>`
|
||||
* - `<div [ngStyle]="styleExp"></div>` - here the `styleExp` must evaluate to an object
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/YamGS6GkUh9GqWNQhCyM?p=preview)):
|
||||
@ -56,26 +66,27 @@ import {isBlank, isPresent} from '../facade/lang';
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngStyle]', inputs: ['rawStyle: ngStyle']})
|
||||
@Directive({selector: '[ngStyle]'})
|
||||
export class NgStyle implements DoCheck {
|
||||
/** @internal */
|
||||
_rawStyle: {[key: string]: string};
|
||||
_ngStyle: {[key: string]: string};
|
||||
/** @internal */
|
||||
_differ: KeyValueDiffer;
|
||||
|
||||
constructor(
|
||||
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||
|
||||
set rawStyle(v: {[key: string]: string}) {
|
||||
this._rawStyle = v;
|
||||
@Input()
|
||||
set ngStyle(v: {[key: string]: string}) {
|
||||
this._ngStyle = v;
|
||||
if (isBlank(this._differ) && isPresent(v)) {
|
||||
this._differ = this._differs.find(this._rawStyle).create(null);
|
||||
this._differ = this._differs.find(this._ngStyle).create(null);
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck() {
|
||||
if (isPresent(this._differ)) {
|
||||
var changes = this._differ.diff(this._rawStyle);
|
||||
var changes = this._differ.diff(this._ngStyle);
|
||||
if (isPresent(changes)) {
|
||||
this._applyChanges(changes);
|
||||
}
|
||||
@ -83,15 +94,19 @@ export class NgStyle implements DoCheck {
|
||||
}
|
||||
|
||||
private _applyChanges(changes: any): void {
|
||||
changes.forEachRemovedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, null); });
|
||||
changes.forEachAddedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, record.currentValue); });
|
||||
changes.forEachRemovedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, null); });
|
||||
}
|
||||
|
||||
private _setStyle(name: string, val: string): void {
|
||||
this._renderer.setElementStyle(this._ngEl.nativeElement, name, val);
|
||||
const nameParts = name.split('.');
|
||||
const nameToSet = nameParts[0];
|
||||
const valToSet = isPresent(val) && nameParts.length === 2 ? `${val}${nameParts[1]}` : val;
|
||||
|
||||
this._renderer.setElementStyle(this._ngEl.nativeElement, nameToSet, valToSet);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,20 @@
|
||||
import {Directive, Host, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ListWrapper, Map} from '../facade/collection';
|
||||
import {Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {isBlank, isPresent, normalizeBlank} from '../facade/lang';
|
||||
|
||||
const _CASE_DEFAULT = /*@ts2dart_const*/ new Object();
|
||||
const _CASE_DEFAULT = new Object();
|
||||
|
||||
// TODO: remove when fully deprecated
|
||||
let _warned: boolean = false;
|
||||
|
||||
export class SwitchView {
|
||||
constructor(
|
||||
@ -17,7 +28,7 @@ export class SwitchView {
|
||||
/**
|
||||
* Adds or removes DOM sub-trees when their match expressions match the switch expression.
|
||||
*
|
||||
* Elements within `NgSwitch` but without `ngSwitchCase` or `NgSwitchDefault` directives will be
|
||||
* Elements within `NgSwitch` but without `NgSwitchCase` or `NgSwitchDefault` directives will be
|
||||
* preserved at the location as specified in the template.
|
||||
*
|
||||
* `NgSwitch` simply inserts nested elements based on which match expression matches the value
|
||||
@ -57,7 +68,7 @@ export class SwitchView {
|
||||
* <template ngSwitchDefault>> 2, STOP!</template>
|
||||
* </p>
|
||||
* `,
|
||||
* directives: [NgSwitch, ngSwitchCase, NgSwitchDefault]
|
||||
* directives: [NgSwitch, NgSwitchCase, NgSwitchDefault]
|
||||
* })
|
||||
* export class App {
|
||||
* value = 'init';
|
||||
@ -72,13 +83,14 @@ export class SwitchView {
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngSwitch]', inputs: ['ngSwitch']})
|
||||
@Directive({selector: '[ngSwitch]'})
|
||||
export class NgSwitch {
|
||||
private _switchValue: any;
|
||||
private _useDefault: boolean = false;
|
||||
private _valueViews = new Map<any, SwitchView[]>();
|
||||
private _activeViews: SwitchView[] = [];
|
||||
|
||||
@Input()
|
||||
set ngSwitch(value: any) {
|
||||
// Empty the currently active ViewContainers
|
||||
this._emptyAllActiveViews();
|
||||
@ -172,16 +184,13 @@ export class NgSwitch {
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngSwitchCase],[ngSwitchWhen]', inputs: ['ngSwitchCase', 'ngSwitchWhen']})
|
||||
@Directive({selector: '[ngSwitchCase],[ngSwitchWhen]'})
|
||||
export class NgSwitchCase {
|
||||
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
|
||||
/** @internal */
|
||||
_value: any = _CASE_DEFAULT;
|
||||
/** @internal */
|
||||
_view: SwitchView;
|
||||
// TODO: remove when fully deprecated
|
||||
/** @internal */
|
||||
_warned: boolean;
|
||||
private _switch: NgSwitch;
|
||||
|
||||
constructor(
|
||||
@ -191,14 +200,16 @@ export class NgSwitchCase {
|
||||
this._view = new SwitchView(viewContainer, templateRef);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngSwitchCase(value: any) {
|
||||
this._switch._onCaseValueChanged(this._value, value, this._view);
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngSwitchWhen(value: any) {
|
||||
if (!this._warned) {
|
||||
this._warned = true;
|
||||
if (!_warned) {
|
||||
_warned = true;
|
||||
console.warn('*ngSwitchWhen is deprecated and will be removed. Use *ngSwitchCase instead');
|
||||
}
|
||||
this._switch._onCaseValueChanged(this._value, value, this._view);
|
||||
|
@ -1,8 +1,12 @@
|
||||
import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {isPresent} from '../facade/lang';
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, EmbeddedViewRef, Input, OnChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
/**
|
||||
* Creates and inserts an embedded view based on a prepared `TemplateRef`.
|
||||
@ -13,13 +17,17 @@ import {isPresent} from '../facade/lang';
|
||||
* Note: using the key `$implicit` in the context object will set it's value as default.
|
||||
*
|
||||
* ### Syntax
|
||||
* - `<template [ngTemplateOutlet]="templateRefExpression"
|
||||
* [ngOutletContext]="objectExpression"></template>`
|
||||
*
|
||||
* ```
|
||||
* <template [ngTemplateOutlet]="templateRefExpression"
|
||||
* [ngOutletContext]="objectExpression">
|
||||
* </template>
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngTemplateOutlet]'})
|
||||
export class NgTemplateOutlet {
|
||||
export class NgTemplateOutlet implements OnChanges {
|
||||
private _viewRef: EmbeddedViewRef<any>;
|
||||
private _context: Object;
|
||||
private _templateRef: TemplateRef<any>;
|
||||
@ -27,29 +35,17 @@ export class NgTemplateOutlet {
|
||||
constructor(private _viewContainerRef: ViewContainerRef) {}
|
||||
|
||||
@Input()
|
||||
set ngOutletContext(context: Object) {
|
||||
if (this._context !== context) {
|
||||
this._context = context;
|
||||
if (isPresent(this._viewRef)) {
|
||||
this.createView();
|
||||
}
|
||||
}
|
||||
}
|
||||
set ngOutletContext(context: Object) { this._context = context; }
|
||||
|
||||
@Input()
|
||||
set ngTemplateOutlet(templateRef: TemplateRef<Object>) {
|
||||
if (this._templateRef !== templateRef) {
|
||||
this._templateRef = templateRef;
|
||||
this.createView();
|
||||
}
|
||||
}
|
||||
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
|
||||
|
||||
private createView() {
|
||||
if (isPresent(this._viewRef)) {
|
||||
ngOnChanges() {
|
||||
if (this._viewRef) {
|
||||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
||||
}
|
||||
|
||||
if (isPresent(this._templateRef)) {
|
||||
if (this._templateRef) {
|
||||
this._viewRef = this._viewContainerRef.createEmbeddedView(this._templateRef, this._context);
|
||||
}
|
||||
}
|
||||
|
@ -1,63 +0,0 @@
|
||||
library angular2.directives.observable_list_iterable_diff;
|
||||
|
||||
import 'package:observe/observe.dart' show ObservableList;
|
||||
import 'package:angular2/core.dart';
|
||||
import 'package:angular2/src/core/change_detection/differs/default_iterable_differ.dart';
|
||||
import 'dart:async';
|
||||
|
||||
class ObservableListDiff extends DefaultIterableDiffer {
|
||||
ChangeDetectorRef _ref;
|
||||
ObservableListDiff(this._ref);
|
||||
|
||||
bool _updated = true;
|
||||
ObservableList _collection;
|
||||
StreamSubscription _subscription;
|
||||
|
||||
onDestroy() {
|
||||
if (this._subscription != null) {
|
||||
this._subscription.cancel();
|
||||
this._subscription = null;
|
||||
this._collection = null;
|
||||
}
|
||||
}
|
||||
|
||||
DefaultIterableDiffer diff(ObservableList collection) {
|
||||
if (collection is! ObservableList) {
|
||||
throw "Cannot change the type of a collection";
|
||||
}
|
||||
|
||||
// A new collection instance is passed in.
|
||||
// - We need to set up a listener.
|
||||
// - We need to diff collection.
|
||||
if (!identical(_collection, collection)) {
|
||||
_collection = collection;
|
||||
|
||||
if (_subscription != null) _subscription.cancel();
|
||||
_subscription = collection.changes.listen((_) {
|
||||
_updated = true;
|
||||
_ref.markForCheck();
|
||||
});
|
||||
_updated = false;
|
||||
return super.diff(collection);
|
||||
|
||||
// An update has been registered since the last change detection check.
|
||||
// - We reset the flag.
|
||||
// - We diff the collection.
|
||||
} else if (_updated) {
|
||||
_updated = false;
|
||||
return super.diff(collection);
|
||||
|
||||
// No updates has been registered.
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ObservableListDiffFactory implements IterableDifferFactory {
|
||||
const ObservableListDiffFactory();
|
||||
bool supports(obj) => obj is ObservableList;
|
||||
IterableDiffer create(ChangeDetectorRef cdRef, [Function trackByFn]) {
|
||||
return new ObservableListDiff(cdRef);
|
||||
}
|
||||
}
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
@ -10,8 +18,9 @@
|
||||
* Forms providers are not included in default providers; you must import these providers
|
||||
* explicitly.
|
||||
*/
|
||||
import {Type} from '@angular/core';
|
||||
import {NgModule, Type} from '@angular/core';
|
||||
|
||||
import {FORM_DIRECTIVES} from './forms-deprecated/directives';
|
||||
import {RadioControlRegistry} from './forms-deprecated/directives/radio_control_value_accessor';
|
||||
import {FormBuilder} from './forms-deprecated/form_builder';
|
||||
|
||||
@ -48,4 +57,19 @@ export {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from './forms-deprecated
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export const FORM_PROVIDERS: Type[] = /*@ts2dart_const*/[FormBuilder, RadioControlRegistry];
|
||||
export const FORM_PROVIDERS: Type[] = [FormBuilder, RadioControlRegistry];
|
||||
|
||||
|
||||
/**
|
||||
* The ng module for the deprecated forms API.
|
||||
* @deprecated
|
||||
*/
|
||||
@NgModule({
|
||||
providers: [
|
||||
FORM_PROVIDERS,
|
||||
],
|
||||
declarations: FORM_DIRECTIVES,
|
||||
exports: FORM_DIRECTIVES
|
||||
})
|
||||
export class DeprecatedFormsModule {
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Type} from '@angular/core';
|
||||
|
||||
import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
|
||||
@ -50,7 +58,7 @@ export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValida
|
||||
* ```
|
||||
* @experimental
|
||||
*/
|
||||
export const FORM_DIRECTIVES: Type[] = /*@ts2dart_const*/[
|
||||
export const FORM_DIRECTIVES: Type[] = [
|
||||
NgControlName,
|
||||
NgControlGroup,
|
||||
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {unimplemented} from '../../facade/exceptions';
|
||||
import {isPresent} from '../../facade/lang';
|
||||
import {AbstractControl} from '../model';
|
||||
|
@ -1,8 +1,16 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
|
||||
export const CHECKBOX_VALUE_ACCESSOR: any = /*@ts2dart_const*/ {
|
||||
export const CHECKBOX_VALUE_ACCESSOR: any = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => CheckboxControlValueAccessor),
|
||||
multi: true
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AbstractControlDirective} from './abstract_control_directive';
|
||||
import {Form} from './form_interface';
|
||||
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {OpaqueToken} from '@angular/core';
|
||||
|
||||
/**
|
||||
@ -33,5 +41,4 @@ export interface ControlValueAccessor {
|
||||
* See {@link DefaultValueAccessor} for how to implement one.
|
||||
* @experimental
|
||||
*/
|
||||
export const NG_VALUE_ACCESSOR: OpaqueToken =
|
||||
/*@ts2dart_const*/ new OpaqueToken('NgValueAccessor');
|
||||
export const NG_VALUE_ACCESSOR: OpaqueToken = new OpaqueToken('NgValueAccessor');
|
||||
|
@ -1,15 +1,22 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core';
|
||||
|
||||
import {isBlank} from '../../facade/lang';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
|
||||
export const DEFAULT_VALUE_ACCESSOR: any = /*@ts2dart_const*/
|
||||
/* @ts2dart_Provider */ {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => DefaultValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
export const DEFAULT_VALUE_ACCESSOR: any = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => DefaultValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* The default accessor for writing a value and listening to changes that is used by the
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Control, ControlGroup} from '../model';
|
||||
|
||||
import {NgControl} from './ng_control';
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {unimplemented} from '../../facade/exceptions';
|
||||
|
||||
import {AbstractControlDirective} from './abstract_control_directive';
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Host, Inject, OnDestroy, OnInit, Optional, Self, SkipSelf, forwardRef} from '@angular/core';
|
||||
|
||||
import {ControlGroup} from '../model';
|
||||
@ -8,11 +16,10 @@ import {Form} from './form_interface';
|
||||
import {composeAsyncValidators, composeValidators, controlPath} from './shared';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
export const controlGroupProvider: any =
|
||||
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => NgControlGroup)
|
||||
};
|
||||
export const controlGroupProvider: any = {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => NgControlGroup)
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and binds a control group to a DOM element.
|
||||
|
@ -1,6 +1,14 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Host, Inject, OnChanges, OnDestroy, Optional, Self, SimpleChanges, SkipSelf, forwardRef} from '@angular/core';
|
||||
|
||||
import {EventEmitter, ObservableWrapper} from '../../facade/async';
|
||||
import {EventEmitter} from '../../facade/async';
|
||||
import {Control} from '../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
|
||||
|
||||
@ -11,11 +19,10 @@ import {composeAsyncValidators, composeValidators, controlPath, isPropertyUpdate
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
|
||||
export const controlNameBinding: any =
|
||||
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgControlName)
|
||||
};
|
||||
export const controlNameBinding: any = {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgControlName)
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and binds a control with a specified name to a DOM element.
|
||||
@ -115,7 +122,7 @@ export class NgControlName extends NgControl implements OnChanges,
|
||||
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
ObservableWrapper.callEmit(this.update, newValue);
|
||||
this.update.emit(newValue);
|
||||
}
|
||||
|
||||
get path(): string[] { return controlPath(this.name, this._parent); }
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Self} from '@angular/core';
|
||||
|
||||
import {isPresent} from '../../facade/lang';
|
||||
|
@ -1,6 +1,14 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Inject, Optional, Self, forwardRef} from '@angular/core';
|
||||
|
||||
import {EventEmitter, ObservableWrapper, PromiseWrapper} from '../../facade/async';
|
||||
import {EventEmitter} from '../../facade/async';
|
||||
import {ListWrapper} from '../../facade/collection';
|
||||
import {isPresent} from '../../facade/lang';
|
||||
import {AbstractControl, Control, ControlGroup} from '../model';
|
||||
@ -12,18 +20,22 @@ import {NgControl} from './ng_control';
|
||||
import {NgControlGroup} from './ng_control_group';
|
||||
import {composeAsyncValidators, composeValidators, setUpControl, setUpControlGroup} from './shared';
|
||||
|
||||
export const formDirectiveProvider: any =
|
||||
/*@ts2dart_const*/ {provide: ControlContainer, useExisting: forwardRef(() => NgForm)};
|
||||
export const formDirectiveProvider: any = {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => NgForm)
|
||||
};
|
||||
|
||||
let _formWarningDisplayed: boolean = false;
|
||||
|
||||
const resolvedPromise = Promise.resolve(null);
|
||||
|
||||
/**
|
||||
* If `NgForm` is bound in a component, `<form>` elements in that component will be
|
||||
* upgraded to use the Angular form system.
|
||||
*
|
||||
* ### Typical Use
|
||||
*
|
||||
* Include `FORM_DIRECTIVES` in the `directives` section of a {@link View} annotation
|
||||
* Include `FORM_DIRECTIVES` in the `directives` section of a {@link Component} annotation
|
||||
* to use `NgForm` and its associated controls.
|
||||
*
|
||||
* ### Structure
|
||||
@ -110,7 +122,7 @@ export class NgForm extends ControlContainer implements Form {
|
||||
console.warn(`
|
||||
*It looks like you're using the old forms module. This will be opt-in in the next RC, and
|
||||
will eventually be removed in favor of the new forms module. For more information, see:
|
||||
https://docs.google.com/document/u/1/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/pub
|
||||
https://docs.google.com/document/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/preview
|
||||
`);
|
||||
}
|
||||
}
|
||||
@ -126,7 +138,7 @@ export class NgForm extends ControlContainer implements Form {
|
||||
get controls(): {[key: string]: AbstractControl} { return this.form.controls; }
|
||||
|
||||
addControl(dir: NgControl): void {
|
||||
PromiseWrapper.scheduleMicrotask(() => {
|
||||
resolvedPromise.then(() => {
|
||||
var container = this._findContainer(dir.path);
|
||||
var ctrl = new Control();
|
||||
setUpControl(ctrl, dir);
|
||||
@ -138,7 +150,7 @@ export class NgForm extends ControlContainer implements Form {
|
||||
getControl(dir: NgControl): Control { return <Control>this.form.find(dir.path); }
|
||||
|
||||
removeControl(dir: NgControl): void {
|
||||
PromiseWrapper.scheduleMicrotask(() => {
|
||||
resolvedPromise.then(() => {
|
||||
var container = this._findContainer(dir.path);
|
||||
if (isPresent(container)) {
|
||||
container.removeControl(dir.name);
|
||||
@ -147,7 +159,7 @@ export class NgForm extends ControlContainer implements Form {
|
||||
}
|
||||
|
||||
addControlGroup(dir: NgControlGroup): void {
|
||||
PromiseWrapper.scheduleMicrotask(() => {
|
||||
resolvedPromise.then(() => {
|
||||
var container = this._findContainer(dir.path);
|
||||
var group = new ControlGroup({});
|
||||
setUpControlGroup(group, dir);
|
||||
@ -157,7 +169,7 @@ export class NgForm extends ControlContainer implements Form {
|
||||
}
|
||||
|
||||
removeControlGroup(dir: NgControlGroup): void {
|
||||
PromiseWrapper.scheduleMicrotask(() => {
|
||||
resolvedPromise.then(() => {
|
||||
var container = this._findContainer(dir.path);
|
||||
if (isPresent(container)) {
|
||||
container.removeControl(dir.name);
|
||||
@ -170,7 +182,7 @@ export class NgForm extends ControlContainer implements Form {
|
||||
}
|
||||
|
||||
updateModel(dir: NgControl, value: any): void {
|
||||
PromiseWrapper.scheduleMicrotask(() => {
|
||||
resolvedPromise.then(() => {
|
||||
var ctrl = <Control>this.form.find(dir.path);
|
||||
ctrl.updateValue(value);
|
||||
});
|
||||
@ -178,7 +190,7 @@ export class NgForm extends ControlContainer implements Form {
|
||||
|
||||
onSubmit(): boolean {
|
||||
this._submitted = true;
|
||||
ObservableWrapper.callEmit(this.ngSubmit, null);
|
||||
this.ngSubmit.emit(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,14 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Inject, OnChanges, Optional, Self, SimpleChanges, forwardRef} from '@angular/core';
|
||||
|
||||
import {EventEmitter, ObservableWrapper} from '../../facade/async';
|
||||
import {EventEmitter} from '../../facade/async';
|
||||
import {StringMapWrapper} from '../../facade/collection';
|
||||
import {Control} from '../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
|
||||
@ -10,11 +18,10 @@ import {NgControl} from './ng_control';
|
||||
import {composeAsyncValidators, composeValidators, isPropertyUpdated, selectValueAccessor, setUpControl} from './shared';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
export const formControlBinding: any =
|
||||
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgFormControl)
|
||||
};
|
||||
export const formControlBinding: any = {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgFormControl)
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds an existing {@link Control} to a DOM element.
|
||||
@ -111,7 +118,7 @@ export class NgFormControl extends NgControl implements OnChanges {
|
||||
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
ObservableWrapper.callEmit(this.update, newValue);
|
||||
this.update.emit(newValue);
|
||||
}
|
||||
|
||||
private _isControlChanged(changes: {[key: string]: any}): boolean {
|
||||
|
@ -1,6 +1,14 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Inject, OnChanges, Optional, Self, SimpleChanges, forwardRef} from '@angular/core';
|
||||
|
||||
import {EventEmitter, ObservableWrapper} from '../../facade/async';
|
||||
import {EventEmitter} from '../../facade/async';
|
||||
import {ListWrapper, StringMapWrapper} from '../../facade/collection';
|
||||
import {BaseException} from '../../facade/exceptions';
|
||||
import {isBlank} from '../../facade/lang';
|
||||
@ -13,11 +21,10 @@ import {NgControl} from './ng_control';
|
||||
import {NgControlGroup} from './ng_control_group';
|
||||
import {composeAsyncValidators, composeValidators, setUpControl, setUpControlGroup} from './shared';
|
||||
|
||||
export const formDirectiveProvider: any =
|
||||
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => NgFormModel)
|
||||
};
|
||||
export const formDirectiveProvider: any = {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => NgFormModel)
|
||||
};
|
||||
|
||||
let _formModelWarningDisplayed: boolean = false;
|
||||
|
||||
@ -126,7 +133,7 @@ export class NgFormModel extends ControlContainer implements Form,
|
||||
console.warn(`
|
||||
*It looks like you're using the old forms module. This will be opt-in in the next RC, and
|
||||
will eventually be removed in favor of the new forms module. For more information, see:
|
||||
https://docs.google.com/document/u/1/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/pub
|
||||
https://docs.google.com/document/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/preview
|
||||
`);
|
||||
}
|
||||
}
|
||||
@ -184,7 +191,7 @@ export class NgFormModel extends ControlContainer implements Form,
|
||||
|
||||
onSubmit(): boolean {
|
||||
this._submitted = true;
|
||||
ObservableWrapper.callEmit(this.ngSubmit, null);
|
||||
this.ngSubmit.emit(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,14 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Inject, OnChanges, Optional, Self, SimpleChanges, forwardRef} from '@angular/core';
|
||||
|
||||
import {EventEmitter, ObservableWrapper} from '../../facade/async';
|
||||
import {EventEmitter} from '../../facade/async';
|
||||
import {Control} from '../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
|
||||
|
||||
@ -9,11 +17,10 @@ import {NgControl} from './ng_control';
|
||||
import {composeAsyncValidators, composeValidators, isPropertyUpdated, selectValueAccessor, setUpControl} from './shared';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
export const formControlBinding: any =
|
||||
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgModel)
|
||||
};
|
||||
export const formControlBinding: any = {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgModel)
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds a domain model to a form control.
|
||||
@ -87,6 +94,6 @@ export class NgModel extends NgControl implements OnChanges {
|
||||
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
ObservableWrapper.callEmit(this.update, newValue);
|
||||
this.update.emit(newValue);
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
library angular2.core.forms.normalize_validators;
|
||||
|
||||
import 'package:angular2/src/common/forms/directives/validators.dart' show Validator;
|
||||
|
||||
Function normalizeValidator(dynamic validator){
|
||||
if (validator is Validator) {
|
||||
return (c) => validator.validate(c);
|
||||
} else {
|
||||
return validator;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Function normalizeAsyncValidator(dynamic validator){
|
||||
if (validator is Validator) {
|
||||
return (c) => validator.validate(c);
|
||||
} else {
|
||||
return validator;
|
||||
}
|
||||
}
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AbstractControl} from '../model';
|
||||
|
||||
import {AsyncValidatorFn, Validator, ValidatorFn} from './validators';
|
||||
@ -12,7 +20,7 @@ export function normalizeValidator(validator: ValidatorFn | Validator): Validato
|
||||
|
||||
export function normalizeAsyncValidator(validator: AsyncValidatorFn | Validator): AsyncValidatorFn {
|
||||
if ((<Validator>validator).validate !== undefined) {
|
||||
return (c: AbstractControl) => Promise.resolve((<Validator>validator).validate(c));
|
||||
return (c: AbstractControl) => (<Validator>validator).validate(c);
|
||||
} else {
|
||||
return <AsyncValidatorFn>validator;
|
||||
}
|
||||
|
@ -1,10 +1,18 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core';
|
||||
|
||||
import {NumberWrapper} from '../../facade/lang';
|
||||
import {NumberWrapper, isBlank} from '../../facade/lang';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
|
||||
export const NUMBER_VALUE_ACCESSOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
export const NUMBER_VALUE_ACCESSOR: any = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => NumberValueAccessor),
|
||||
multi: true
|
||||
@ -36,7 +44,9 @@ export class NumberValueAccessor implements ControlValueAccessor {
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: number): void {
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', value);
|
||||
// The value needs to be normalized for IE9, otherwise it is set to 'null' when null
|
||||
const normalizedValue = isBlank(value) ? '' : value;
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: number) => void): void {
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, ElementRef, Injectable, Injector, Input, OnDestroy, OnInit, Renderer, forwardRef} from '@angular/core';
|
||||
|
||||
import {ListWrapper} from '../../facade/collection';
|
||||
@ -6,7 +14,7 @@ import {isPresent} from '../../facade/lang';
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
import {NgControl} from './ng_control';
|
||||
|
||||
export const RADIO_VALUE_ACCESSOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
export const RADIO_VALUE_ACCESSOR: any = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => RadioControlValueAccessor),
|
||||
multi: true
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core';
|
||||
|
||||
import {MapWrapper} from '../../facade/collection';
|
||||
@ -5,7 +13,7 @@ import {StringWrapper, isBlank, isPresent, isPrimitive, looseIdentical} from '..
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
|
||||
export const SELECT_VALUE_ACCESSOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
export const SELECT_VALUE_ACCESSOR: any = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => SelectControlValueAccessor),
|
||||
multi: true
|
||||
|
@ -1,11 +1,19 @@
|
||||
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {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 {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
|
||||
const SELECT_MULTIPLE_VALUE_ACCESSOR = {
|
||||
export const SELECT_MULTIPLE_VALUE_ACCESSOR = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => SelectMultipleControlValueAccessor),
|
||||
multi: true
|
||||
@ -39,7 +47,7 @@ abstract class HTMLCollection {
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'select[multiple][ngControl],select[multiple][ngFormControl],select[multiple][ngModel]',
|
||||
host: {'(input)': 'onChange($event.target)', '(blur)': 'onTouched()'},
|
||||
host: {'(change)': 'onChange($event.target)', '(blur)': 'onTouched()'},
|
||||
providers: [SELECT_MULTIPLE_VALUE_ACCESSOR]
|
||||
})
|
||||
export class SelectMultipleControlValueAccessor implements ControlValueAccessor {
|
||||
@ -174,4 +182,4 @@ export class NgSelectMultipleOption implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
export const SELECT_DIRECTIVES = [SelectMultipleControlValueAccessor, NgSelectMultipleOption];
|
||||
export const SELECT_DIRECTIVES = [SelectMultipleControlValueAccessor, NgSelectMultipleOption];
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ListWrapper, StringMapWrapper} from '../../facade/collection';
|
||||
import {BaseException} from '../../facade/exceptions';
|
||||
import {hasConstructor, isBlank, isPresent, looseIdentical} from '../../facade/lang';
|
||||
@ -15,6 +23,7 @@ import {normalizeAsyncValidator, normalizeValidator} from './normalize_validator
|
||||
import {NumberValueAccessor} from './number_value_accessor';
|
||||
import {RadioControlValueAccessor} from './radio_control_value_accessor';
|
||||
import {SelectControlValueAccessor} from './select_control_value_accessor';
|
||||
import {SelectMultipleControlValueAccessor} from './select_multiple_control_value_accessor';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
|
||||
@ -25,8 +34,8 @@ export function controlPath(name: string, parent: ControlContainer): string[] {
|
||||
}
|
||||
|
||||
export function setUpControl(control: Control, dir: NgControl): void {
|
||||
if (isBlank(control)) _throwError(dir, 'Cannot find control');
|
||||
if (isBlank(dir.valueAccessor)) _throwError(dir, 'No value accessor for');
|
||||
if (isBlank(control)) _throwError(dir, 'Cannot find control with');
|
||||
if (isBlank(dir.valueAccessor)) _throwError(dir, 'No value accessor for form control with');
|
||||
|
||||
control.validator = Validators.compose([control.validator, dir.validator]);
|
||||
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);
|
||||
@ -47,14 +56,21 @@ export function setUpControl(control: Control, dir: NgControl): void {
|
||||
}
|
||||
|
||||
export function setUpControlGroup(control: ControlGroup, dir: NgControlGroup) {
|
||||
if (isBlank(control)) _throwError(dir, 'Cannot find control');
|
||||
if (isBlank(control)) _throwError(dir, 'Cannot find control with');
|
||||
control.validator = Validators.compose([control.validator, dir.validator]);
|
||||
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);
|
||||
}
|
||||
|
||||
function _throwError(dir: AbstractControlDirective, message: string): void {
|
||||
var path = dir.path.join(' -> ');
|
||||
throw new BaseException(`${message} '${path}'`);
|
||||
let messageEnd: string;
|
||||
if (dir.path.length > 1) {
|
||||
messageEnd = `path: '${dir.path.join(' -> ')}'`;
|
||||
} else if (dir.path[0]) {
|
||||
messageEnd = `name: '${dir.path}'`;
|
||||
} else {
|
||||
messageEnd = 'unspecified name';
|
||||
}
|
||||
throw new BaseException(`${message} ${messageEnd}`);
|
||||
}
|
||||
|
||||
export function composeValidators(validators: /* Array<Validator|Function> */ any[]): ValidatorFn {
|
||||
@ -90,14 +106,15 @@ export function selectValueAccessor(
|
||||
} else if (
|
||||
hasConstructor(v, CheckboxControlValueAccessor) || hasConstructor(v, NumberValueAccessor) ||
|
||||
hasConstructor(v, SelectControlValueAccessor) ||
|
||||
hasConstructor(v, SelectMultipleControlValueAccessor) ||
|
||||
hasConstructor(v, RadioControlValueAccessor)) {
|
||||
if (isPresent(builtinAccessor))
|
||||
_throwError(dir, 'More than one built-in value accessor matches');
|
||||
_throwError(dir, 'More than one built-in value accessor matches form control with');
|
||||
builtinAccessor = v;
|
||||
|
||||
} else {
|
||||
if (isPresent(customAccessor))
|
||||
_throwError(dir, 'More than one custom value accessor matches');
|
||||
_throwError(dir, 'More than one custom value accessor matches form control with');
|
||||
customAccessor = v;
|
||||
}
|
||||
});
|
||||
@ -106,6 +123,6 @@ export function selectValueAccessor(
|
||||
if (isPresent(builtinAccessor)) return builtinAccessor;
|
||||
if (isPresent(defaultAccessor)) return defaultAccessor;
|
||||
|
||||
_throwError(dir, 'No valid value accessor for');
|
||||
_throwError(dir, 'No valid value accessor for form control with');
|
||||
return null;
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Attribute, Directive, forwardRef} from '@angular/core';
|
||||
|
||||
import {NumberWrapper} from '../../facade/lang';
|
||||
@ -22,12 +30,14 @@ import {NG_VALIDATORS, Validators} from '../validators';
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export interface Validator { validate(c: AbstractControl): {[key: string]: any}; }
|
||||
|
||||
const REQUIRED = /*@ts2dart_const*/ Validators.required;
|
||||
export const REQUIRED = Validators.required;
|
||||
|
||||
export const REQUIRED_VALIDATOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
export const REQUIRED_VALIDATOR: any = {
|
||||
provide: NG_VALIDATORS,
|
||||
useValue: REQUIRED,
|
||||
multi: true
|
||||
@ -64,7 +74,7 @@ export interface AsyncValidatorFn {
|
||||
*
|
||||
* {@example common/forms/ts/validators/validators.ts region='min'}
|
||||
*/
|
||||
export const MIN_LENGTH_VALIDATOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
export const MIN_LENGTH_VALIDATOR: any = {
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => MinLengthValidator),
|
||||
multi: true
|
||||
@ -97,7 +107,7 @@ export class MinLengthValidator implements Validator {
|
||||
*
|
||||
* {@example common/forms/ts/validators/validators.ts region='max'}
|
||||
*/
|
||||
export const MAX_LENGTH_VALIDATOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
export const MAX_LENGTH_VALIDATOR: any = {
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => MaxLengthValidator),
|
||||
multi: true
|
||||
@ -124,7 +134,7 @@ export class MaxLengthValidator implements Validator {
|
||||
}
|
||||
|
||||
|
||||
export const PATTERN_VALIDATOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
export const PATTERN_VALIDATOR: any = {
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => PatternValidator),
|
||||
multi: true
|
||||
|
@ -1,10 +1,18 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {isArray, isPresent} from '../facade/lang';
|
||||
|
||||
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
|
||||
import * as modelModule from './model';
|
||||
import {AbstractControl, Control, ControlArray, ControlGroup} from './model';
|
||||
|
||||
|
||||
|
||||
@ -59,22 +67,21 @@ export class FormBuilder {
|
||||
*
|
||||
* See the {@link ControlGroup} constructor for more details.
|
||||
*/
|
||||
group(controlsConfig: {[key: string]: any}, extra: {[key: string]: any} = null):
|
||||
modelModule.ControlGroup {
|
||||
group(controlsConfig: {[key: string]: any}, extra: {[key: string]: any} = null): ControlGroup {
|
||||
var controls = this._reduceControls(controlsConfig);
|
||||
var optionals = <{[key: string]: boolean}>(
|
||||
isPresent(extra) ? StringMapWrapper.get(extra, 'optionals') : null);
|
||||
var validator: ValidatorFn = isPresent(extra) ? StringMapWrapper.get(extra, 'validator') : null;
|
||||
var asyncValidator: AsyncValidatorFn =
|
||||
isPresent(extra) ? StringMapWrapper.get(extra, 'asyncValidator') : null;
|
||||
return new modelModule.ControlGroup(controls, optionals, validator, asyncValidator);
|
||||
return new ControlGroup(controls, optionals, validator, asyncValidator);
|
||||
}
|
||||
/**
|
||||
* Construct a new {@link Control} with the given `value`,`validator`, and `asyncValidator`.
|
||||
*/
|
||||
control(value: Object, validator: ValidatorFn = null, asyncValidator: AsyncValidatorFn = null):
|
||||
modelModule.Control {
|
||||
return new modelModule.Control(value, validator, asyncValidator);
|
||||
Control {
|
||||
return new Control(value, validator, asyncValidator);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,15 +90,14 @@ export class FormBuilder {
|
||||
*/
|
||||
array(
|
||||
controlsConfig: any[], validator: ValidatorFn = null,
|
||||
asyncValidator: AsyncValidatorFn = null): modelModule.ControlArray {
|
||||
asyncValidator: AsyncValidatorFn = null): ControlArray {
|
||||
var controls = controlsConfig.map(c => this._createControl(c));
|
||||
return new modelModule.ControlArray(controls, validator, asyncValidator);
|
||||
return new ControlArray(controls, validator, asyncValidator);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_reduceControls(controlsConfig: {[k: string]: any}):
|
||||
{[key: string]: modelModule.AbstractControl} {
|
||||
var controls: {[key: string]: modelModule.AbstractControl} = {};
|
||||
_reduceControls(controlsConfig: {[k: string]: any}): {[key: string]: AbstractControl} {
|
||||
var controls: {[key: string]: AbstractControl} = {};
|
||||
StringMapWrapper.forEach(controlsConfig, (controlConfig: any, controlName: string) => {
|
||||
controls[controlName] = this._createControl(controlConfig);
|
||||
});
|
||||
@ -99,10 +105,9 @@ export class FormBuilder {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_createControl(controlConfig: any): modelModule.AbstractControl {
|
||||
if (controlConfig instanceof modelModule.Control ||
|
||||
controlConfig instanceof modelModule.ControlGroup ||
|
||||
controlConfig instanceof modelModule.ControlArray) {
|
||||
_createControl(controlConfig: any): AbstractControl {
|
||||
if (controlConfig instanceof Control || controlConfig instanceof ControlGroup ||
|
||||
controlConfig instanceof ControlArray) {
|
||||
return controlConfig;
|
||||
|
||||
} else if (isArray(controlConfig)) {
|
||||
|
@ -1,8 +1,20 @@
|
||||
import {EventEmitter, Observable, ObservableWrapper} from '../facade/async';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {PromiseObservable} from 'rxjs/observable/PromiseObservable';
|
||||
|
||||
import {EventEmitter, Observable} from '../facade/async';
|
||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
||||
import {isBlank, isPresent, isPromise, normalizeBool} from '../facade/lang';
|
||||
|
||||
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
|
||||
|
||||
|
||||
/**
|
||||
* Indicates that a Control is valid, i.e. that no errors exist in the input value.
|
||||
*/
|
||||
@ -44,7 +56,7 @@ function _find(control: AbstractControl, path: Array<string|number>| string) {
|
||||
}
|
||||
|
||||
function toObservable(r: any): Observable<any> {
|
||||
return isPromise(r) ? ObservableWrapper.fromPromise(r) : r;
|
||||
return isPromise(r) ? PromiseObservable.create(r) : r;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,8 +139,8 @@ export abstract class AbstractControl {
|
||||
}
|
||||
|
||||
if (emitEvent) {
|
||||
ObservableWrapper.callEmit(this._valueChanges, this._value);
|
||||
ObservableWrapper.callEmit(this._statusChanges, this._status);
|
||||
this._valueChanges.emit(this._value);
|
||||
this._statusChanges.emit(this._status);
|
||||
}
|
||||
|
||||
if (isPresent(this._parent) && !onlySelf) {
|
||||
@ -145,14 +157,14 @@ export abstract class AbstractControl {
|
||||
this._status = PENDING;
|
||||
this._cancelExistingSubscription();
|
||||
var obs = toObservable(this.asyncValidator(this));
|
||||
this._asyncValidationSubscription = ObservableWrapper.subscribe(
|
||||
obs, (res: {[key: string]: any}) => this.setErrors(res, {emitEvent: emitEvent}));
|
||||
this._asyncValidationSubscription = obs.subscribe(
|
||||
{next: (res: {[key: string]: any}) => this.setErrors(res, {emitEvent: emitEvent})});
|
||||
}
|
||||
}
|
||||
|
||||
private _cancelExistingSubscription(): void {
|
||||
if (isPresent(this._asyncValidationSubscription)) {
|
||||
ObservableWrapper.dispose(this._asyncValidationSubscription);
|
||||
this._asyncValidationSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,7 +198,7 @@ export abstract class AbstractControl {
|
||||
this._status = this._calculateStatus();
|
||||
|
||||
if (emitEvent) {
|
||||
ObservableWrapper.callEmit(this._statusChanges, this._status);
|
||||
this._statusChanges.emit(this._status);
|
||||
}
|
||||
|
||||
if (isPresent(this._parent)) {
|
||||
|
@ -1,10 +1,20 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {OpaqueToken} from '@angular/core';
|
||||
import {ObservableWrapper} from '../facade/async';
|
||||
import {toPromise} from 'rxjs/operator/toPromise';
|
||||
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {isBlank, isPresent, isPromise, isString} from '../facade/lang';
|
||||
import {PromiseWrapper} from '../facade/promise';
|
||||
|
||||
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
|
||||
import * as modelModule from './model';
|
||||
import {AbstractControl} from './model';
|
||||
|
||||
|
||||
/**
|
||||
* Providers for validators to be used for {@link Control}s in a form.
|
||||
@ -16,7 +26,7 @@ import * as modelModule from './model';
|
||||
* {@example core/forms/ts/ng_validators/ng_validators.ts region='ng_validators'}
|
||||
* @experimental
|
||||
*/
|
||||
export const NG_VALIDATORS: OpaqueToken = /*@ts2dart_const*/ new OpaqueToken('NgValidators');
|
||||
export const NG_VALIDATORS: OpaqueToken = new OpaqueToken('NgValidators');
|
||||
|
||||
/**
|
||||
* Providers for asynchronous validators to be used for {@link Control}s
|
||||
@ -28,8 +38,7 @@ export const NG_VALIDATORS: OpaqueToken = /*@ts2dart_const*/ new OpaqueToken('Ng
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export const NG_ASYNC_VALIDATORS: OpaqueToken =
|
||||
/*@ts2dart_const*/ new OpaqueToken('NgAsyncValidators');
|
||||
export const NG_ASYNC_VALIDATORS: OpaqueToken = new OpaqueToken('NgAsyncValidators');
|
||||
|
||||
/**
|
||||
* Provides a set of validators used by form controls.
|
||||
@ -49,7 +58,7 @@ export class Validators {
|
||||
/**
|
||||
* Validator that requires controls to have a non-empty value.
|
||||
*/
|
||||
static required(control: modelModule.AbstractControl): {[key: string]: boolean} {
|
||||
static required(control: AbstractControl): {[key: string]: boolean} {
|
||||
return isBlank(control.value) || (isString(control.value) && control.value == '') ?
|
||||
{'required': true} :
|
||||
null;
|
||||
@ -59,7 +68,7 @@ export class Validators {
|
||||
* Validator that requires controls to have a value of a minimum length.
|
||||
*/
|
||||
static minLength(minLength: number): ValidatorFn {
|
||||
return (control: modelModule.AbstractControl): {[key: string]: any} => {
|
||||
return (control: AbstractControl): {[key: string]: any} => {
|
||||
if (isPresent(Validators.required(control))) return null;
|
||||
var v: string = control.value;
|
||||
return v.length < minLength ?
|
||||
@ -72,7 +81,7 @@ export class Validators {
|
||||
* Validator that requires controls to have a value of a maximum length.
|
||||
*/
|
||||
static maxLength(maxLength: number): ValidatorFn {
|
||||
return (control: modelModule.AbstractControl): {[key: string]: any} => {
|
||||
return (control: AbstractControl): {[key: string]: any} => {
|
||||
if (isPresent(Validators.required(control))) return null;
|
||||
var v: string = control.value;
|
||||
return v.length > maxLength ?
|
||||
@ -85,7 +94,7 @@ export class Validators {
|
||||
* Validator that requires a control to match a regex to its value.
|
||||
*/
|
||||
static pattern(pattern: string): ValidatorFn {
|
||||
return (control: modelModule.AbstractControl): {[key: string]: any} => {
|
||||
return (control: AbstractControl): {[key: string]: any} => {
|
||||
if (isPresent(Validators.required(control))) return null;
|
||||
let regex = new RegExp(`^${pattern}$`);
|
||||
let v: string = control.value;
|
||||
@ -97,7 +106,7 @@ export class Validators {
|
||||
/**
|
||||
* No-op validator.
|
||||
*/
|
||||
static nullValidator(c: modelModule.AbstractControl): {[key: string]: boolean} { return null; }
|
||||
static nullValidator(c: AbstractControl): {[key: string]: boolean} { return null; }
|
||||
|
||||
/**
|
||||
* Compose multiple validators into a single function that returns the union
|
||||
@ -108,7 +117,7 @@ export class Validators {
|
||||
var presentValidators = validators.filter(isPresent);
|
||||
if (presentValidators.length == 0) return null;
|
||||
|
||||
return function(control: modelModule.AbstractControl) {
|
||||
return function(control: AbstractControl) {
|
||||
return _mergeErrors(_executeValidators(control, presentValidators));
|
||||
};
|
||||
}
|
||||
@ -118,24 +127,22 @@ export class Validators {
|
||||
var presentValidators = validators.filter(isPresent);
|
||||
if (presentValidators.length == 0) return null;
|
||||
|
||||
return function(control: modelModule.AbstractControl) {
|
||||
return function(control: AbstractControl) {
|
||||
let promises = _executeAsyncValidators(control, presentValidators).map(_convertToPromise);
|
||||
return PromiseWrapper.all(promises).then(_mergeErrors);
|
||||
return Promise.all(promises).then(_mergeErrors);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function _convertToPromise(obj: any): Promise<any> {
|
||||
return isPromise(obj) ? obj : ObservableWrapper.toPromise(obj);
|
||||
return isPromise(obj) ? obj : toPromise.call(obj);
|
||||
}
|
||||
|
||||
function _executeValidators(
|
||||
control: modelModule.AbstractControl, validators: ValidatorFn[]): any[] {
|
||||
function _executeValidators(control: AbstractControl, validators: ValidatorFn[]): any[] {
|
||||
return validators.map(v => v(control));
|
||||
}
|
||||
|
||||
function _executeAsyncValidators(
|
||||
control: modelModule.AbstractControl, validators: AsyncValidatorFn[]): any[] {
|
||||
function _executeAsyncValidators(control: AbstractControl, validators: AsyncValidatorFn[]): any[] {
|
||||
return validators.map(v => v(control));
|
||||
}
|
||||
|
||||
|
26
modules/@angular/common/src/localization.ts
Normal file
26
modules/@angular/common/src/localization.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export abstract class NgLocalization { abstract getPluralCategory(value: any): string; }
|
||||
|
||||
/**
|
||||
* Returns the plural category for a given value.
|
||||
* - "=value" when the case exists,
|
||||
* - the plural category otherwise
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function getPluralCategory(
|
||||
value: number, cases: string[], ngLocalization: NgLocalization): string {
|
||||
const nbCase = `=${value}`;
|
||||
|
||||
return cases.indexOf(nbCase) > -1 ? nbCase : ngLocalization.getPluralCategory(value);
|
||||
}
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @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 * from './location/platform_location';
|
||||
export * from './location/location_strategy';
|
||||
export * from './location/hash_location_strategy';
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, Injectable, Optional} from '@angular/core';
|
||||
|
||||
import {isPresent} from '../facade/lang';
|
||||
@ -69,16 +77,13 @@ export class HashLocationStrategy extends LocationStrategy {
|
||||
|
||||
getBaseHref(): string { return this._baseHref; }
|
||||
|
||||
path(): string {
|
||||
path(includeHash: boolean = false): string {
|
||||
// the hash value is always prefixed with a `#`
|
||||
// and if it is empty then it will stay empty
|
||||
var path = this._platformLocation.hash;
|
||||
if (!isPresent(path)) path = '#';
|
||||
|
||||
// Dart will complain if a call to substring is
|
||||
// executed with a position value that extends the
|
||||
// length of string.
|
||||
return (path.length > 0 ? path.substring(1) : path);
|
||||
return path.length > 0 ? path.substring(1) : path;
|
||||
}
|
||||
|
||||
prepareExternalUrl(internal: string): string {
|
||||
|
@ -1,6 +1,12 @@
|
||||
import {EventEmitter, Injectable} from '@angular/core';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ObservableWrapper} from '../facade/async';
|
||||
import {EventEmitter, Injectable} from '@angular/core';
|
||||
|
||||
import {LocationStrategy} from './location_strategy';
|
||||
|
||||
@ -61,15 +67,18 @@ export class Location {
|
||||
this._platformStrategy = platformStrategy;
|
||||
var browserBaseHref = this._platformStrategy.getBaseHref();
|
||||
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
|
||||
this._platformStrategy.onPopState((ev) => {
|
||||
ObservableWrapper.callEmit(this._subject, {'url': this.path(), 'pop': true, 'type': ev.type});
|
||||
});
|
||||
this._platformStrategy.onPopState(
|
||||
(ev) => { this._subject.emit({'url': this.path(true), 'pop': true, 'type': ev.type}); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the normalized URL path.
|
||||
*/
|
||||
path(): string { return this.normalize(this._platformStrategy.path()); }
|
||||
// TODO: vsavkin. Remove the boolean flag and always include hash once the deprecated router is
|
||||
// removed.
|
||||
path(includeHash: boolean = false): string {
|
||||
return this.normalize(this._platformStrategy.path(includeHash));
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the given path and compares to the current normalized path.
|
||||
@ -132,7 +141,7 @@ export class Location {
|
||||
subscribe(
|
||||
onNext: (value: any) => void, onThrow: (exception: any) => void = null,
|
||||
onReturn: () => void = null): Object {
|
||||
return ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn);
|
||||
return this._subject.subscribe({next: onNext, error: onThrow, complete: onReturn});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {OpaqueToken} from '@angular/core';
|
||||
import {UrlChangeListener} from './platform_location';
|
||||
|
||||
@ -20,7 +28,7 @@ import {UrlChangeListener} from './platform_location';
|
||||
* @stable
|
||||
*/
|
||||
export abstract class LocationStrategy {
|
||||
abstract path(): string;
|
||||
abstract path(includeHash?: boolean): string;
|
||||
abstract prepareExternalUrl(internal: string): string;
|
||||
abstract pushState(state: any, title: string, url: string, queryParams: string): void;
|
||||
abstract replaceState(state: any, title: string, url: string, queryParams: string): void;
|
||||
@ -61,4 +69,4 @@ export abstract class LocationStrategy {
|
||||
* ```
|
||||
* @stable
|
||||
*/
|
||||
export const APP_BASE_HREF: OpaqueToken = /*@ts2dart_const*/ new OpaqueToken('appBaseHref');
|
||||
export const APP_BASE_HREF: OpaqueToken = new OpaqueToken('appBaseHref');
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, Injectable, Optional} from '@angular/core';
|
||||
|
||||
import {BaseException} from '../facade/exceptions';
|
||||
@ -17,44 +25,17 @@ import {PlatformLocation, UrlChangeListener} from './platform_location';
|
||||
* `PathLocationStrategy` is the default binding for {@link LocationStrategy}
|
||||
* provided in {@link ROUTER_PROVIDERS}.
|
||||
*
|
||||
* If you're using `PathLocationStrategy`, you must provide a provider for
|
||||
* {@link APP_BASE_HREF} to a string representing the URL prefix that should
|
||||
* be preserved when generating and recognizing URLs.
|
||||
* If you're using `PathLocationStrategy`, you must provide a {@link APP_BASE_HREF}
|
||||
* or add a base element to the document. This URL prefix that will be preserved
|
||||
* when generating and recognizing URLs.
|
||||
*
|
||||
* For instance, if you provide an `APP_BASE_HREF` of `'/my/app'` and call
|
||||
* `location.go('/foo')`, the browser's URL will become
|
||||
* `example.com/my/app/foo`.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* import {Component} from '@angular/core';
|
||||
* import {bootstrap} from '@angular/platform-browser/browser';
|
||||
* import {
|
||||
* Location,
|
||||
* APP_BASE_HREF
|
||||
* } from '@angular/common';
|
||||
* import {
|
||||
* ROUTER_DIRECTIVES,
|
||||
* ROUTER_PROVIDERS,
|
||||
* RouteConfig
|
||||
* } from '@angular/router';
|
||||
*
|
||||
* @Component({directives: [ROUTER_DIRECTIVES]})
|
||||
* @RouteConfig([
|
||||
* {...},
|
||||
* ])
|
||||
* class AppCmp {
|
||||
* constructor(location: Location) {
|
||||
* location.go('/foo');
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* bootstrap(AppCmp, [
|
||||
* ROUTER_PROVIDERS, // includes binding to PathLocationStrategy
|
||||
* {provide: APP_BASE_HREF, useValue: '/my/app'}
|
||||
* ]);
|
||||
* ```
|
||||
* Similarly, if you add `<base href='/my/app'/>` to the document and call
|
||||
* `location.go('/foo')`, the browser's URL will become
|
||||
* `example.com/my/app/foo`.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@ -90,9 +71,11 @@ export class PathLocationStrategy extends LocationStrategy {
|
||||
return Location.joinWithSlash(this._baseHref, internal);
|
||||
}
|
||||
|
||||
path(): string {
|
||||
return this._platformLocation.pathname +
|
||||
path(includeHash: boolean = false): string {
|
||||
const pathname = this._platformLocation.pathname +
|
||||
Location.normalizeQueryParams(this._platformLocation.search);
|
||||
const hash = this._platformLocation.hash;
|
||||
return hash && includeHash ? `${pathname}${hash}` : pathname;
|
||||
}
|
||||
|
||||
pushState(state: any, title: string, url: string, queryParams: string) {
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class should not be used directly by an application developer. Instead, use
|
||||
* {@link Location}.
|
||||
@ -45,8 +53,11 @@ export abstract class PlatformLocation {
|
||||
/**
|
||||
* A serializable version of the event from onPopState or onHashChange
|
||||
*
|
||||
* @stable
|
||||
* @experimental
|
||||
*/
|
||||
export interface UrlChangeEvent { type: string; }
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export interface UrlChangeListener { (e: UrlChangeEvent): any; }
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
|
@ -1,5 +1,13 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectorRef, OnDestroy, Pipe, WrappedValue} from '@angular/core';
|
||||
import {EventEmitter, Observable, ObservableWrapper} from '../facade/async';
|
||||
import {EventEmitter, Observable} from '../facade/async';
|
||||
import {isBlank, isPresent, isPromise} from '../facade/lang';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
@ -11,12 +19,12 @@ interface SubscriptionStrategy {
|
||||
|
||||
class ObservableStrategy implements SubscriptionStrategy {
|
||||
createSubscription(async: any, updateLatestValue: any): any {
|
||||
return ObservableWrapper.subscribe(async, updateLatestValue, e => { throw e; });
|
||||
return async.subscribe({next: updateLatestValue, error: (e: any) => { throw e; }});
|
||||
}
|
||||
|
||||
dispose(subscription: any): void { ObservableWrapper.dispose(subscription); }
|
||||
dispose(subscription: any): void { subscription.unsubscribe(); }
|
||||
|
||||
onDestroy(subscription: any): void { ObservableWrapper.dispose(subscription); }
|
||||
onDestroy(subscription: any): void { subscription.unsubscribe(); }
|
||||
}
|
||||
|
||||
class PromiseStrategy implements SubscriptionStrategy {
|
||||
@ -117,7 +125,7 @@ export class AsyncPipe implements OnDestroy {
|
||||
_selectStrategy(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
|
||||
if (isPromise(obj)) {
|
||||
return _promiseStrategy;
|
||||
} else if (ObservableWrapper.isObservable(obj)) {
|
||||
} else if ((<any>obj).subscribe) {
|
||||
return _observableStrategy;
|
||||
} else {
|
||||
throw new InvalidPipeArgumentException(AsyncPipe, obj);
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
@ -24,7 +32,7 @@ import {UpperCasePipe} from './uppercase_pipe';
|
||||
*
|
||||
* @experimental Contains i18n pipes which are experimental
|
||||
*/
|
||||
export const COMMON_PIPES = /*@ts2dart_const*/[
|
||||
export const COMMON_PIPES = [
|
||||
AsyncPipe,
|
||||
UpperCasePipe,
|
||||
LowerCasePipe,
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {DateFormatter} from '../facade/intl';
|
||||
@ -43,9 +51,6 @@ var defaultLocale: string = 'en-US';
|
||||
*
|
||||
* In javascript, only the components specified will be respected (not the ordering,
|
||||
* punctuations, ...) and details of the formatting will be dependent on the locale.
|
||||
* On the other hand in Dart version, you can also include quoted text as well as some extra
|
||||
* date/time components such as quarter. For more information see:
|
||||
* https://www.dartdocs.org/documentation/intl/0.13.0/intl/DateFormat-class.html
|
||||
*
|
||||
* `format` can also be one of the following predefined formats:
|
||||
*
|
||||
|
@ -1,31 +1,52 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {StringWrapper, isPresent, isStringMap} from '../facade/lang';
|
||||
import {StringWrapper, isBlank, isStringMap} from '../facade/lang';
|
||||
import {NgLocalization, getPluralCategory} from '../localization';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
const _INTERPOLATION_REGEXP: RegExp = /#/g;
|
||||
|
||||
/**
|
||||
*
|
||||
* Maps a value to a string that pluralizes the value properly.
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* expression | i18nPlural:mapping
|
||||
*
|
||||
* where `expression` is a number and `mapping` is an object that indicates the proper text for
|
||||
* when the `expression` evaluates to 0, 1, or some other number. You can interpolate the actual
|
||||
* value into the text using the `#` sign.
|
||||
* where `expression` is a number and `mapping` is an object that mimics the ICU format,
|
||||
* see http://userguide.icu-project.org/formatparse/messages
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* <div>
|
||||
* {{ messages.length | i18nPlural: messageMapping }}
|
||||
* </div>
|
||||
* class MyLocalization extends NgLocalization {
|
||||
* getPluralCategory(value: any) {
|
||||
* if(value > 1) {
|
||||
* return 'other';
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <div>
|
||||
* {{ messages.length | i18nPlural: messageMapping }}
|
||||
* </div>
|
||||
* `,
|
||||
* providers: [{provide: NgLocalization, useClass: MyLocalization}]
|
||||
* })
|
||||
*
|
||||
* class MyApp {
|
||||
* messages: any[];
|
||||
* messageMapping: any = {
|
||||
* messageMapping: {[k:string]: string} = {
|
||||
* '=0': 'No messages.',
|
||||
* '=1': 'One message.',
|
||||
* 'other': '# messages.'
|
||||
@ -38,17 +59,17 @@ const _INTERPOLATION_REGEXP: RegExp = /#/g;
|
||||
*/
|
||||
@Pipe({name: 'i18nPlural', pure: true})
|
||||
export class I18nPluralPipe implements PipeTransform {
|
||||
constructor(private _localization: NgLocalization) {}
|
||||
|
||||
transform(value: number, pluralMap: {[count: string]: string}): string {
|
||||
var key: string;
|
||||
var valueStr: string;
|
||||
if (isBlank(value)) return '';
|
||||
|
||||
if (!isStringMap(pluralMap)) {
|
||||
throw new InvalidPipeArgumentException(I18nPluralPipe, pluralMap);
|
||||
}
|
||||
|
||||
key = value === 0 || value === 1 ? `=${value}` : 'other';
|
||||
valueStr = isPresent(value) ? value.toString() : '';
|
||||
const key = getPluralCategory(value, Object.keys(pluralMap), this._localization);
|
||||
|
||||
return StringWrapper.replaceAll(pluralMap[key], _INTERPOLATION_REGEXP, valueStr);
|
||||
return StringWrapper.replaceAll(pluralMap[key], _INTERPOLATION_REGEXP, value.toString());
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,14 @@
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {isStringMap} from '../facade/lang';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {isBlank, isStringMap} from '../facade/lang';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
/**
|
||||
*
|
||||
@ -25,8 +31,8 @@ import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
* class MyApp {
|
||||
* gender: string = 'male';
|
||||
* inviteMap: any = {
|
||||
* 'male': 'Invite her.',
|
||||
* 'female': 'Invite him.',
|
||||
* 'male': 'Invite him.',
|
||||
* 'female': 'Invite her.',
|
||||
* 'other': 'Invite them.'
|
||||
* }
|
||||
* ...
|
||||
@ -38,10 +44,12 @@ import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
@Pipe({name: 'i18nSelect', pure: true})
|
||||
export class I18nSelectPipe implements PipeTransform {
|
||||
transform(value: string, mapping: {[key: string]: string}): string {
|
||||
if (isBlank(value)) return '';
|
||||
|
||||
if (!isStringMap(mapping)) {
|
||||
throw new InvalidPipeArgumentException(I18nSelectPipe, mapping);
|
||||
}
|
||||
|
||||
return StringMapWrapper.contains(mapping, value) ? mapping[value] : mapping['other'];
|
||||
return mapping.hasOwnProperty(value) ? mapping[value] : '';
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {BaseException} from '../facade/exceptions';
|
||||
import {Type, stringify} from '../facade/lang';
|
||||
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
import {Json} from '../facade/lang';
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {isBlank, isString} from '../facade/lang';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
@ -1,27 +1,44 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {BaseException} from '../facade/exceptions';
|
||||
|
||||
import {NumberFormatStyle, NumberFormatter} from '../facade/intl';
|
||||
import {NumberWrapper, RegExpWrapper, Type, isBlank, isNumber, isPresent} from '../facade/lang';
|
||||
import {NumberWrapper, Type, isBlank, isNumber, isPresent, isString} from '../facade/lang';
|
||||
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
var defaultLocale: string = 'en-US';
|
||||
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(\-(\d+))?)?$/g;
|
||||
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(\-(\d+))?)?$/;
|
||||
|
||||
/**
|
||||
* Internal function to format numbers used by Decimal, Percent and Date pipes.
|
||||
*/
|
||||
function formatNumber(
|
||||
pipe: Type, value: number, style: NumberFormatStyle, digits: string, currency: string = null,
|
||||
currencyAsSymbol: boolean = false): string {
|
||||
pipe: Type, value: number | string, style: NumberFormatStyle, digits: string,
|
||||
currency: string = null, currencyAsSymbol: boolean = false): string {
|
||||
if (isBlank(value)) return null;
|
||||
// Convert strings to numbers
|
||||
value = isString(value) && NumberWrapper.isNumeric(value) ? +value : value;
|
||||
if (!isNumber(value)) {
|
||||
throw new InvalidPipeArgumentException(pipe, value);
|
||||
}
|
||||
var minInt = 1, minFraction = 0, maxFraction = 3;
|
||||
let minInt: number;
|
||||
let minFraction: number;
|
||||
let maxFraction: number;
|
||||
if (style !== NumberFormatStyle.Currency) {
|
||||
// rely on Intl default for currency
|
||||
minInt = 1;
|
||||
minFraction = 0;
|
||||
maxFraction = 3;
|
||||
}
|
||||
|
||||
if (isPresent(digits)) {
|
||||
var parts = RegExpWrapper.firstMatch(_NUMBER_FORMAT_REGEXP, digits);
|
||||
if (isBlank(parts)) {
|
||||
throw new BaseException(`${digits} is not a valid digit info for number pipes`);
|
||||
var parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
||||
if (parts === null) {
|
||||
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
||||
}
|
||||
if (isPresent(parts[1])) { // min integer digits
|
||||
minInt = NumberWrapper.parseIntAutoRadix(parts[1]);
|
||||
@ -33,7 +50,7 @@ function formatNumber(
|
||||
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
|
||||
}
|
||||
}
|
||||
return NumberFormatter.format(value, defaultLocale, style, {
|
||||
return NumberFormatter.format(value as number, defaultLocale, style, {
|
||||
minimumIntegerDigits: minInt,
|
||||
minimumFractionDigits: minFraction,
|
||||
maximumFractionDigits: maxFraction,
|
||||
|
@ -1,5 +1,13 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {RegExpWrapper, StringWrapper, isBlank, isFunction, isNumber, isString} from '../facade/lang';
|
||||
import {StringWrapper, isBlank, isFunction, isNumber, isString} from '../facade/lang';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
/**
|
||||
@ -53,7 +61,7 @@ export class ReplacePipe implements PipeTransform {
|
||||
}
|
||||
|
||||
if (isFunction(replacement)) {
|
||||
const rgxPattern = isString(pattern) ? RegExpWrapper.create(pattern) : pattern;
|
||||
const rgxPattern = isString(pattern) ? new RegExp(pattern, 'g') : pattern;
|
||||
|
||||
return StringWrapper.replaceAllMapped(
|
||||
input, rgxPattern, <(m: string[]) => string>replacement);
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {StringWrapper, isArray, isBlank, isString} from '../facade/lang';
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {isBlank, isString} from '../facade/lang';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
@ -1,9 +1,16 @@
|
||||
import {beforeEach, beforeEachProviders, ddescribe, xdescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {ComponentFixture, TestComponentBuilder} from '@angular/compiler/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {ListWrapper, StringMapWrapper, SetWrapper} from '../../src/facade/collection';
|
||||
import {Component, provide} from '@angular/core';
|
||||
import {NgFor, NgClass} from '@angular/common';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgClass, NgFor} from '@angular/common';
|
||||
import {Component} from '@angular/core';
|
||||
import {ComponentFixture, TestComponentBuilder} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
||||
import {ListWrapper, StringMapWrapper} from '../../src/facade/collection';
|
||||
|
||||
function detectChangesAndCheck(fixture: ComponentFixture<any>, classes: string) {
|
||||
fixture.detectChanges();
|
||||
@ -20,7 +27,7 @@ export function main() {
|
||||
var template = '<div *ngFor="let item of items" [ngClass]="item"></div>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
.then((fixture: ComponentFixture<TestComponent>) => {
|
||||
fixture.debugElement.componentInstance.items = [['0']];
|
||||
fixture.detectChanges();
|
||||
fixture.debugElement.componentInstance.items = [['1']];
|
||||
@ -574,7 +581,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp', directives: [NgClass, NgFor], template: ''})
|
||||
|
@ -1,18 +1,25 @@
|
||||
import {beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgFor, NgIf} from '@angular/common';
|
||||
import {Component, ContentChild, TemplateRef} from '@angular/core';
|
||||
import {TestComponentBuilder} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
import {ListWrapper} from '../../src/facade/collection';
|
||||
import {IS_DART} from '../../src/facade/lang';
|
||||
import {Component, TemplateRef, ContentChild} from '@angular/core';
|
||||
import {NgFor} from '@angular/common';
|
||||
import {NgIf} from '@angular/common';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
|
||||
let thisArg: any;
|
||||
|
||||
export function main() {
|
||||
describe('ngFor', () => {
|
||||
var TEMPLATE =
|
||||
const TEMPLATE =
|
||||
'<div><copy-me template="ngFor let item of items">{{item.toString()}};</copy-me></div>';
|
||||
|
||||
it('should reflect initial elements',
|
||||
@ -102,7 +109,7 @@ export function main() {
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
const template =
|
||||
'<ul><li template="ngFor let item of items">{{item["name"]}};</li></ul>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
@ -135,7 +142,7 @@ export function main() {
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
|
||||
const template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
@ -166,25 +173,18 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
if (!IS_DART) {
|
||||
it('should throw on non-iterable ref and suggest using an array',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = 'whaaa';
|
||||
try {
|
||||
fixture.detectChanges()
|
||||
} catch (e) {
|
||||
expect(e.message).toContain(
|
||||
`Cannot find a differ supporting object 'whaaa' of type 'string'. NgFor only supports binding to Iterables such as Arrays.`);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
it('should throw on non-iterable ref and suggest using an array',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, TEMPLATE).createAsync(TestComponent).then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = 'whaaa';
|
||||
expect(() => fixture.detectChanges())
|
||||
.toThrowError(
|
||||
/Cannot find a differ supporting object 'whaaa' of type 'string'. NgFor only supports binding to Iterables such as Arrays/);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw on ref changing to string',
|
||||
inject(
|
||||
@ -221,7 +221,7 @@ export function main() {
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
const template = '<div>' +
|
||||
'<div template="ngFor let item of items">' +
|
||||
'<div template="ngFor let subitem of item">' +
|
||||
'{{subitem}}-{{item.length}};' +
|
||||
@ -250,7 +250,7 @@ export function main() {
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div><template ngFor let-item [ngForOf]="items">' +
|
||||
const template = '<div><template ngFor let-item [ngForOf]="items">' +
|
||||
'<div template="ngFor let subitem of item">' +
|
||||
'{{subitem}}-{{item.length}};' +
|
||||
'</div></template></div>';
|
||||
@ -273,15 +273,15 @@ export function main() {
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
const template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" let-i="index"><div>{{i}}|</div>` +
|
||||
`<div *ngIf="i % 2 == 0">even|</div></template></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
var el = fixture.debugElement.nativeElement;
|
||||
var items = [1];
|
||||
const el = fixture.debugElement.nativeElement;
|
||||
const items = [1];
|
||||
fixture.debugElement.componentInstance.items = items;
|
||||
fixture.detectChanges();
|
||||
expect(el).toHaveText('0|even|');
|
||||
@ -302,7 +302,7 @@ export function main() {
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
const template =
|
||||
'<div><copy-me template="ngFor: let item of items; let i=index">{{i.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
@ -323,7 +323,7 @@ export function main() {
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
const template =
|
||||
'<div><copy-me template="ngFor: let item of items; let isFirst=first">{{isFirst.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
@ -344,7 +344,7 @@ export function main() {
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
const template =
|
||||
'<div><copy-me template="ngFor: let item of items; let isLast=last">{{isLast.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
@ -365,7 +365,7 @@ export function main() {
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
const template =
|
||||
'<div><copy-me template="ngFor: let item of items; let isEven=even">{{isEven.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
@ -386,7 +386,7 @@ export function main() {
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
const template =
|
||||
'<div><copy-me template="ngFor: let item of items; let isOdd=odd">{{isOdd.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
@ -415,7 +415,7 @@ export function main() {
|
||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>')
|
||||
.createAsync(ComponentUsingTestComponent)
|
||||
.then((fixture) => {
|
||||
var testComponent = fixture.debugElement.children[0];
|
||||
const testComponent = fixture.debugElement.children[0];
|
||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
||||
@ -433,7 +433,7 @@ export function main() {
|
||||
.overrideTemplate(ComponentUsingTestComponent, '<test-cmp></test-cmp>')
|
||||
.createAsync(ComponentUsingTestComponent)
|
||||
.then((fixture) => {
|
||||
var testComponent = fixture.debugElement.children[0];
|
||||
const testComponent = fixture.debugElement.children[0];
|
||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
||||
@ -453,7 +453,7 @@ export function main() {
|
||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>')
|
||||
.createAsync(ComponentUsingTestComponent)
|
||||
.then((fixture) => {
|
||||
var testComponent = fixture.debugElement.children[0];
|
||||
const testComponent = fixture.debugElement.children[0];
|
||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
||||
@ -462,24 +462,39 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
describe('track by', function() {
|
||||
describe('track by', () => {
|
||||
it('should set the context to the component instance',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
const template =
|
||||
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByContext.bind(this)"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
thisArg = null;
|
||||
fixture.detectChanges();
|
||||
expect(thisArg).toBe(fixture.debugElement.componentInstance);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not replace tracked items',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
const template =
|
||||
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById" let-i="index">
|
||||
<p>{{items[i]}}</p>
|
||||
</template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
var buildItemList =
|
||||
() => {
|
||||
fixture.debugElement.componentInstance.items = [{'id': 'a'}];
|
||||
fixture.detectChanges();
|
||||
return fixture.debugElement.queryAll(By.css('p'))[0];
|
||||
}
|
||||
var buildItemList = () => {
|
||||
fixture.debugElement.componentInstance.items = [{'id': 'a'}];
|
||||
fixture.detectChanges();
|
||||
return fixture.debugElement.queryAll(By.css('p'))[0];
|
||||
};
|
||||
|
||||
var firstP = buildItemList();
|
||||
var finalP = buildItemList();
|
||||
@ -487,11 +502,12 @@ export function main() {
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should update implicit local variable on view',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
const template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -509,7 +525,7 @@ export function main() {
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
const template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -530,7 +546,7 @@ export function main() {
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
const template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
@ -560,6 +576,7 @@ class TestComponent {
|
||||
constructor() { this.items = [1, 2]; }
|
||||
trackById(index: number, item: any): string { return item['id']; }
|
||||
trackByIndex(index: number, item: any): number { return index; }
|
||||
trackByContext(): void { thisArg = this; }
|
||||
}
|
||||
|
||||
@Component({selector: 'outer-cmp', directives: [TestComponent], template: ''})
|
||||
|
@ -1,14 +1,17 @@
|
||||
import {beforeEach, ddescribe, describe, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing';
|
||||
import {TestComponentBuilder} from '@angular/compiler/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {NgIf} from '@angular/common';
|
||||
|
||||
import {IS_DART} from '../../src/facade/lang';
|
||||
import {Component} from '@angular/core';
|
||||
import {TestComponentBuilder} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
export function main() {
|
||||
describe('ngIf directive', () => {
|
||||
@ -183,84 +186,58 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not add the element twice if the condition goes from true to true (JS)',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html = '<div><copy-me template="ngIf numberCondition">hello</copy-me></div>';
|
||||
|
||||
if (!IS_DART) {
|
||||
it('should not add the element twice if the condition goes from true to true (JS)',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html = '<div><copy-me template="ngIf numberCondition">hello</copy-me></div>';
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
fixture.debugElement.componentInstance.numberCondition = 2;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
|
||||
fixture.debugElement.componentInstance.numberCondition = 2;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
it('should not recreate the element if the condition goes from true to true (JS)',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html = '<div><copy-me template="ngIf numberCondition">hello</copy-me></div>';
|
||||
|
||||
it('should not recreate the element if the condition goes from true to true (JS)',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html = '<div><copy-me template="ngIf numberCondition">hello</copy-me></div>';
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
getDOM().addClass(
|
||||
getDOM().querySelector(fixture.debugElement.nativeElement, 'copy-me'),
|
||||
'foo');
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
getDOM().addClass(
|
||||
getDOM().querySelector(fixture.debugElement.nativeElement, 'copy-me'),
|
||||
'foo');
|
||||
|
||||
fixture.debugElement.componentInstance.numberCondition = 2;
|
||||
fixture.detectChanges();
|
||||
expect(
|
||||
getDOM().hasClass(
|
||||
getDOM().querySelector(fixture.debugElement.nativeElement, 'copy-me'),
|
||||
'foo'))
|
||||
.toBe(true);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
if (IS_DART) {
|
||||
it('should not create the element if the condition is not a boolean (DART)',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html = '<div><copy-me template="ngIf numberCondition">hello</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
expect(() => fixture.detectChanges()).toThrowError();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
}
|
||||
fixture.debugElement.componentInstance.numberCondition = 2;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().hasClass(
|
||||
getDOM().querySelector(fixture.debugElement.nativeElement, 'copy-me'),
|
||||
'foo'))
|
||||
.toBe(true);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,20 @@
|
||||
import {beforeEachProviders, beforeEach, ddescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder} from '@angular/compiler/testing';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgLocalization, NgPlural, NgPluralCase} from '@angular/common';
|
||||
import {Component, Injectable} from '@angular/core';
|
||||
import {NgPlural, NgPluralCase, NgLocalization} from '@angular/common';
|
||||
import {TestComponentBuilder} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
export function main() {
|
||||
describe('switch', () => {
|
||||
beforeEachProviders(() => [{provide: NgLocalization, useClass: TestLocalizationMap}]);
|
||||
beforeEachProviders(() => [{provide: NgLocalization, useClass: TestLocalization}]);
|
||||
|
||||
it('should display the template according to the exact value',
|
||||
inject(
|
||||
@ -34,6 +41,27 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
// https://github.com/angular/angular/issues/9868
|
||||
// https://github.com/angular/angular/issues/9882
|
||||
it('should not throw when ngPluralCase contains expressions',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="=0"><li>{{ switchValue }}</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.switchValue = 0;
|
||||
expect(() => fixture.detectChanges()).not.toThrow();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should be applicable to <ng-container> elements',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
@ -134,22 +162,21 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class TestLocalizationMap extends NgLocalization {
|
||||
class TestLocalization extends NgLocalization {
|
||||
getPluralCategory(value: number): string {
|
||||
if (value > 1 && value < 4) {
|
||||
return 'few';
|
||||
} else if (value >= 4 && value < 10) {
|
||||
return 'many';
|
||||
} else {
|
||||
return 'other';
|
||||
}
|
||||
if (value >= 4 && value < 10) {
|
||||
return 'many';
|
||||
}
|
||||
|
||||
return 'other';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'test-cmp', directives: [NgPluralCase, NgPlural], template: ''})
|
||||
@Component({selector: 'test-cmp', directives: [NgPlural, NgPluralCase], template: ''})
|
||||
class TestComponent {
|
||||
switchValue: number = null;
|
||||
}
|
||||
|
@ -1,14 +1,22 @@
|
||||
import {beforeEach, beforeEachProviders, ddescribe, xdescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {StringMapWrapper} from '../../src/facade/collection';
|
||||
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, xdescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/core/testing';
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {NgStyle} from '@angular/common/src/directives/ng_style';
|
||||
|
||||
function expectNativeEl(fixture: ComponentFixture<any>) {
|
||||
return <any>expect(fixture.debugElement.children[0].nativeElement);
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe('binding to CSS styles', () => {
|
||||
|
||||
@ -22,9 +30,7 @@ export function main() {
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||
|
||||
async.done();
|
||||
});
|
||||
@ -43,16 +49,88 @@ export function main() {
|
||||
|
||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||
|
||||
expr = fixture.debugElement.componentInstance.expr;
|
||||
(expr as any)['max-width'] = '30%';
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('30%');
|
||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '30%'});
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add and remove styles specified using style.unit notation',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div [ngStyle]="{'max-width.px': expr}"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.debugElement.componentInstance.expr = '40';
|
||||
fixture.detectChanges();
|
||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||
|
||||
fixture.debugElement.componentInstance.expr = null;
|
||||
fixture.detectChanges();
|
||||
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should update styles using style.unit notation when unit changes',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div [ngStyle]="expr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.debugElement.componentInstance.expr = {'max-width.px': '40'};
|
||||
fixture.detectChanges();
|
||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||
|
||||
fixture.debugElement.componentInstance.expr = {'max-width.em': '40'};
|
||||
fixture.detectChanges();
|
||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40em'});
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
// keyValueDiffer is sensitive to key order #9115
|
||||
it('should change styles specified in an object expression',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
const template = `<div [ngStyle]="expr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.expr = {
|
||||
// height, width order is important here
|
||||
height: '10px',
|
||||
width: '10px'
|
||||
};
|
||||
|
||||
fixture.detectChanges();
|
||||
expectNativeEl(fixture).toHaveCssStyle({'height': '10px', 'width': '10px'});
|
||||
|
||||
fixture.debugElement.componentInstance.expr = {
|
||||
// width, height order is important here
|
||||
width: '5px',
|
||||
height: '5px',
|
||||
};
|
||||
|
||||
fixture.detectChanges();
|
||||
expectNativeEl(fixture).toHaveCssStyle({'height': '5px', 'width': '5px'});
|
||||
|
||||
async.done();
|
||||
});
|
||||
@ -69,16 +147,11 @@ export function main() {
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
|
||||
|
||||
StringMapWrapper.delete(
|
||||
fixture.debugElement.componentInstance.expr, 'max-width');
|
||||
delete fixture.debugElement.componentInstance.expr['max-width'];
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('');
|
||||
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
|
||||
|
||||
async.done();
|
||||
});
|
||||
@ -95,22 +168,13 @@ export function main() {
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'font-size'))
|
||||
.toEqual('12px');
|
||||
expectNativeEl(fixture).toHaveCssStyle(
|
||||
{'max-width': '40px', 'font-size': '12px'});
|
||||
|
||||
StringMapWrapper.delete(
|
||||
fixture.debugElement.componentInstance.expr, 'max-width');
|
||||
delete fixture.debugElement.componentInstance.expr['max-width'];
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('');
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'font-size'))
|
||||
.toEqual('12px');
|
||||
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
|
||||
expectNativeEl(fixture).toHaveCssStyle({'font-size': '12px'});
|
||||
|
||||
async.done();
|
||||
});
|
||||
@ -127,28 +191,18 @@ export function main() {
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'font-size'))
|
||||
.toEqual('12px');
|
||||
|
||||
StringMapWrapper.delete(
|
||||
fixture.debugElement.componentInstance.expr, 'max-width');
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'font-size'))
|
||||
.toEqual('12px');
|
||||
expectNativeEl(fixture).toHaveCssStyle(
|
||||
{'max-width': '40px', 'font-size': '12px'});
|
||||
|
||||
delete fixture.debugElement.componentInstance.expr['max-width'];
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('');
|
||||
expectNativeEl(fixture).not.toHaveCssStyle('max-width');
|
||||
expectNativeEl(fixture).toHaveCssStyle({'font-size': '12px'});
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp', directives: [NgStyle], template: ''})
|
||||
|
@ -1,9 +1,16 @@
|
||||
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {Component} from '@angular/core';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from '@angular/common';
|
||||
import {Component} from '@angular/core';
|
||||
import {TestComponentBuilder} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
export function main() {
|
||||
describe('switch', () => {
|
||||
|
@ -1,8 +1,16 @@
|
||||
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {Component, Directive, TemplateRef, ContentChildren, QueryList} from '@angular/core';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgTemplateOutlet} from '@angular/common';
|
||||
import {Component, ContentChildren, Directive, QueryList, TemplateRef} from '@angular/core';
|
||||
import {TestComponentBuilder} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
export function main() {
|
||||
describe('insert', () => {
|
||||
|
@ -1,9 +1,17 @@
|
||||
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component, Directive} from '@angular/core';
|
||||
import {ElementRef} from '@angular/core/src/linker/element_ref';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
export function main() {
|
||||
describe('non-bindable', () => {
|
||||
@ -53,7 +61,7 @@ export function main() {
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@Directive({selector: '[test-dec]'})
|
||||
|
@ -1,102 +0,0 @@
|
||||
library angular2.test.directives.observable_list_iterable_diff_spec;
|
||||
|
||||
import 'package:angular2/testing_internal.dart';
|
||||
import 'package:observe/observe.dart' show ObservableList;
|
||||
import 'package:angular2/core.dart' show ChangeDetectorRef;
|
||||
import 'package:angular2/common.dart' show ObservableListDiffFactory;
|
||||
|
||||
@proxy
|
||||
class SpyChangeDetectorRef extends SpyObject implements ChangeDetectorRef {}
|
||||
|
||||
main() {
|
||||
describe('ObservableListDiff', () {
|
||||
var factory, changeDetectorRef;
|
||||
|
||||
beforeEach(() {
|
||||
factory = const ObservableListDiffFactory();
|
||||
changeDetectorRef = new SpyChangeDetectorRef();
|
||||
});
|
||||
|
||||
describe("supports", () {
|
||||
it("should be true for ObservableList", () {
|
||||
expect(factory.supports(new ObservableList())).toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () {
|
||||
expect(factory.supports([1, 2, 3])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("should return itself when called the first time", () {
|
||||
final d = factory.create(changeDetectorRef);
|
||||
final c = new ObservableList.from([1, 2]);
|
||||
expect(d.diff(c)).toBe(d);
|
||||
});
|
||||
|
||||
it("should return itself when no changes between the calls", () {
|
||||
final d = factory.create(changeDetectorRef);
|
||||
|
||||
final c = new ObservableList.from([1, 2]);
|
||||
|
||||
d.diff(c);
|
||||
|
||||
expect(d.diff(c)).toBe(null);
|
||||
});
|
||||
|
||||
it("should return the wrapped value once a change has been triggered",
|
||||
fakeAsync(() {
|
||||
final d = factory.create(changeDetectorRef);
|
||||
|
||||
final c = new ObservableList.from([1, 2]);
|
||||
|
||||
d.diff(c);
|
||||
|
||||
c.add(3);
|
||||
|
||||
// same value, because we have not detected the change yet
|
||||
expect(d.diff(c)).toBe(null);
|
||||
|
||||
// now we detect the change
|
||||
flushMicrotasks();
|
||||
expect(d.diff(c)).toBe(d);
|
||||
}));
|
||||
|
||||
it("should request a change detection check upon receiving a change",
|
||||
fakeAsync(() {
|
||||
final d = factory.create(changeDetectorRef);
|
||||
|
||||
final c = new ObservableList.from([1, 2]);
|
||||
d.diff(c);
|
||||
|
||||
c.add(3);
|
||||
flushMicrotasks();
|
||||
|
||||
expect(changeDetectorRef.spy("markForCheck")).toHaveBeenCalledOnce();
|
||||
}));
|
||||
|
||||
it("should return the wrapped value after changing a collection", () {
|
||||
final d = factory.create(changeDetectorRef);
|
||||
|
||||
final c1 = new ObservableList.from([1, 2]);
|
||||
final c2 = new ObservableList.from([3, 4]);
|
||||
|
||||
expect(d.diff(c1)).toBe(d);
|
||||
expect(d.diff(c2)).toBe(d);
|
||||
});
|
||||
|
||||
it("should not unbsubscribe from the stream of chagnes after changing a collection",
|
||||
() {
|
||||
final d = factory.create(changeDetectorRef);
|
||||
|
||||
final c1 = new ObservableList.from([1, 2]);
|
||||
expect(d.diff(c1)).toBe(d);
|
||||
|
||||
final c2 = new ObservableList.from([3, 4]);
|
||||
expect(d.diff(c2)).toBe(d);
|
||||
|
||||
// pushing into the first collection has no effect, and we do not see the change
|
||||
c1.add(3);
|
||||
expect(d.diff(c2)).toBe(null);
|
||||
});
|
||||
});
|
||||
}
|
@ -1,17 +1,19 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CheckboxControlValueAccessor, Control, ControlGroup, ControlValueAccessor, DefaultValueAccessor, NgControl, NgControlGroup, NgControlName, NgForm, NgFormControl, NgFormModel, NgModel, SelectControlValueAccessor, Validator, Validators} from '@angular/common/src/forms-deprecated';
|
||||
import {composeValidators, selectValueAccessor} from '@angular/common/src/forms-deprecated/directives/shared';
|
||||
import {SimpleChange} from '@angular/core/src/change_detection';
|
||||
import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {fakeAsync, flushMicrotasks, Log, tick,} from '@angular/core/testing';
|
||||
|
||||
import {SpyNgControl, SpyValueAccessor} from '../spies';
|
||||
|
||||
import {ControlGroup, Control, NgControlName, NgControlGroup, NgFormModel, ControlValueAccessor, Validators, NgForm, NgModel, NgFormControl, NgControl, DefaultValueAccessor, CheckboxControlValueAccessor, SelectControlValueAccessor, Validator} from '@angular/common/src/forms-deprecated';
|
||||
|
||||
|
||||
import {selectValueAccessor, composeValidators} from '@angular/common/src/forms-deprecated/directives/shared';
|
||||
import {TimerWrapper} from '../../src/facade/async';
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
import {SimpleChange} from '@angular/core/src/change_detection';
|
||||
|
||||
class DummyControlValueAccessor implements ControlValueAccessor {
|
||||
writtenValue: any /** TODO #9100 */;
|
||||
|
||||
@ -27,14 +29,14 @@ class CustomValidatorDirective implements Validator {
|
||||
|
||||
function asyncValidator(expected: any /** TODO #9100 */, timeout = 0) {
|
||||
return (c: any /** TODO #9100 */) => {
|
||||
var completer = PromiseWrapper.completer();
|
||||
var res = c.value != expected ? {'async': true} : null;
|
||||
if (timeout == 0) {
|
||||
completer.resolve(res);
|
||||
} else {
|
||||
TimerWrapper.setTimeout(() => { completer.resolve(res); }, timeout);
|
||||
}
|
||||
return completer.promise;
|
||||
return new Promise((resolve) => {
|
||||
var res = c.value != expected ? {'async': true} : null;
|
||||
if (timeout == 0) {
|
||||
resolve(res);
|
||||
} else {
|
||||
setTimeout(() => { resolve(res); }, timeout);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -142,7 +144,7 @@ export function main() {
|
||||
dir.name = 'invalidName';
|
||||
|
||||
expect(() => form.addControl(dir))
|
||||
.toThrowError(new RegExp('Cannot find control \'invalidName\''));
|
||||
.toThrowError(new RegExp(`Cannot find control with name: 'invalidName'`));
|
||||
});
|
||||
|
||||
it('should throw when no value accessor', () => {
|
||||
@ -150,7 +152,18 @@ export function main() {
|
||||
dir.name = 'login';
|
||||
|
||||
expect(() => form.addControl(dir))
|
||||
.toThrowError(new RegExp('No value accessor for \'login\''));
|
||||
.toThrowError(new RegExp(`No value accessor for form control with name: 'login'`));
|
||||
});
|
||||
|
||||
it('should throw when no value accessor with path', () => {
|
||||
const group = new NgControlGroup(form, null, null);
|
||||
const dir = new NgControlName(group, null, null, null);
|
||||
group.name = 'passwords';
|
||||
dir.name = 'password';
|
||||
|
||||
expect(() => form.addControl(dir))
|
||||
.toThrowError(new RegExp(
|
||||
`No value accessor for form control with path: 'passwords -> password'`));
|
||||
});
|
||||
|
||||
it('should set up validators', fakeAsync(() => {
|
||||
@ -426,6 +439,14 @@ export function main() {
|
||||
expect(ngModel.untouched).toBe(control.untouched);
|
||||
});
|
||||
|
||||
it('should throw when no value accessor with unnamed control', () => {
|
||||
const unnamedDir = new NgModel(null, null, null);
|
||||
|
||||
expect(() => unnamedDir.ngOnChanges({}))
|
||||
.toThrowError(new RegExp(`No value accessor for form control with unspecified name`));
|
||||
});
|
||||
|
||||
|
||||
it('should set up validator', fakeAsync(() => {
|
||||
// this will add the required validator and recalculate the validity
|
||||
ngModel.ngOnChanges({});
|
||||
|
@ -1,11 +1,17 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Control, FormBuilder} from '@angular/common/src/forms-deprecated';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
|
||||
export function main() {
|
||||
function syncValidator(_: any): any { return null; }
|
||||
function asyncValidator(_: any) { return PromiseWrapper.resolve(null); }
|
||||
function asyncValidator(_: any) { return Promise.resolve(null); }
|
||||
|
||||
describe('Form Builder', () => {
|
||||
var b: any /** TODO #9100 */;
|
||||
|
@ -1,23 +1,27 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgFor, NgIf} from '@angular/common';
|
||||
import {Control, ControlGroup, ControlValueAccessor, FORM_DIRECTIVES, FORM_PROVIDERS, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NgControl, NgForm, RadioButtonState, Validator, Validators} from '@angular/common/src/forms-deprecated';
|
||||
import {TestComponentBuilder} from '@angular/compiler/testing';
|
||||
import {ComponentFixture} from '@angular/compiler/testing';
|
||||
import {Component, Directive, EventEmitter, Output} from '@angular/core';
|
||||
import {Input, Provider, forwardRef} from '@angular/core';
|
||||
import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {Control, ControlGroup, ControlValueAccessor, DeprecatedFormsModule, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NgControl, NgForm, RadioButtonState, Validator, Validators} from '@angular/common/src/forms-deprecated';
|
||||
import {Component, Directive, EventEmitter, Input, Output, Provider, forwardRef} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, TestComponentBuilder, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {dispatchEvent} from '@angular/platform-browser/testing';
|
||||
import {dispatchEvent} from '@angular/platform-browser/testing/browser_util';
|
||||
|
||||
import {ObservableWrapper, TimerWrapper} from '../../src/facade/async';
|
||||
import {ListWrapper} from '../../src/facade/collection';
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
|
||||
export function main() {
|
||||
describe('integration tests', () => {
|
||||
|
||||
beforeEach(() => TestBed.configureTestingModule({imports: [DeprecatedFormsModule]}));
|
||||
|
||||
it('should initialize DOM elements with the given form object',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
@ -47,7 +51,7 @@ export function main() {
|
||||
|
||||
tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => {
|
||||
expect(() => fixture.detectChanges())
|
||||
.toThrowError(new RegExp(`ngFormModel expects a form. Please pass one in.`));
|
||||
.toThrowError(/ngFormModel expects a form\. Please pass one in/);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@ -92,8 +96,8 @@ export function main() {
|
||||
|
||||
input.nativeElement.value = 'updatedValue';
|
||||
|
||||
ObservableWrapper.subscribe(
|
||||
form.valueChanges, (value) => { throw 'Should not happen'; });
|
||||
|
||||
form.valueChanges.subscribe({next: (value: any) => { throw 'Should not happen'; }});
|
||||
dispatchEvent(input.nativeElement, 'change');
|
||||
|
||||
async.done();
|
||||
@ -123,55 +127,51 @@ export function main() {
|
||||
})));
|
||||
|
||||
it('should mark NgForm as submitted on submit event',
|
||||
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
|
||||
var t = `<div>
|
||||
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
var t = `<div>
|
||||
<form #f="ngForm" (ngSubmit)="data=f.submitted"></form>
|
||||
<span>{{data}}</span>
|
||||
</div>`;
|
||||
|
||||
var fixture: ComponentFixture<MyComp8>;
|
||||
var fixture: ComponentFixture<MyComp8>;
|
||||
|
||||
tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((root) => {
|
||||
fixture = root;
|
||||
});
|
||||
tick();
|
||||
tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((root) => { fixture = root; });
|
||||
tick();
|
||||
|
||||
fixture.debugElement.componentInstance.data = false;
|
||||
fixture.debugElement.componentInstance.data = false;
|
||||
|
||||
tick();
|
||||
tick();
|
||||
|
||||
var form = fixture.debugElement.query(By.css('form'));
|
||||
dispatchEvent(form.nativeElement, 'submit');
|
||||
var form = fixture.debugElement.query(By.css('form'));
|
||||
dispatchEvent(form.nativeElement, 'submit');
|
||||
|
||||
tick();
|
||||
expect(fixture.debugElement.componentInstance.data).toEqual(true);
|
||||
})));
|
||||
tick();
|
||||
expect(fixture.debugElement.componentInstance.data).toEqual(true);
|
||||
})));
|
||||
|
||||
it('should mark NgFormModel as submitted on submit event',
|
||||
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
|
||||
var t = `<div>
|
||||
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
var t = `<div>
|
||||
<form #f="ngForm" [ngFormModel]="form" (ngSubmit)="data=f.submitted"></form>
|
||||
<span>{{data}}</span>
|
||||
</div>`;
|
||||
|
||||
var fixture: ComponentFixture<MyComp8>;
|
||||
var fixture: ComponentFixture<MyComp8>;
|
||||
|
||||
tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((root) => {
|
||||
fixture = root;
|
||||
});
|
||||
tick();
|
||||
tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((root) => { fixture = root; });
|
||||
tick();
|
||||
|
||||
fixture.debugElement.componentInstance.form = new ControlGroup({});
|
||||
fixture.debugElement.componentInstance.data = false;
|
||||
fixture.debugElement.componentInstance.form = new ControlGroup({});
|
||||
fixture.debugElement.componentInstance.data = false;
|
||||
|
||||
tick();
|
||||
tick();
|
||||
|
||||
var form = fixture.debugElement.query(By.css('form'));
|
||||
dispatchEvent(form.nativeElement, 'submit');
|
||||
var form = fixture.debugElement.query(By.css('form'));
|
||||
dispatchEvent(form.nativeElement, 'submit');
|
||||
|
||||
tick();
|
||||
expect(fixture.debugElement.componentInstance.data).toEqual(true);
|
||||
})));
|
||||
tick();
|
||||
expect(fixture.debugElement.componentInstance.data).toEqual(true);
|
||||
})));
|
||||
|
||||
it('should work with single controls',
|
||||
inject(
|
||||
@ -604,7 +604,7 @@ export function main() {
|
||||
select.nativeElement.value = '2: Object';
|
||||
dispatchEvent(select.nativeElement, 'change');
|
||||
fixture.detectChanges();
|
||||
TimerWrapper.setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
expect(testComp.selectedCity['name']).toEqual('Buffalo');
|
||||
async.done();
|
||||
}, 0);
|
||||
@ -829,11 +829,13 @@ export function main() {
|
||||
expect(input.componentInstance.value).toEqual('!aa!');
|
||||
|
||||
input.componentInstance.value = '!bb!';
|
||||
ObservableWrapper.subscribe(input.componentInstance.onInput, (value) => {
|
||||
expect(fixture.debugElement.componentInstance.form.value).toEqual({
|
||||
'name': 'bb'
|
||||
});
|
||||
async.done();
|
||||
input.componentInstance.onInput.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(fixture.debugElement.componentInstance.form.value).toEqual({
|
||||
'name': 'bb'
|
||||
});
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
input.componentInstance.dispatchChangeEvent();
|
||||
});
|
||||
@ -1489,21 +1491,16 @@ class MyInput implements ControlValueAccessor {
|
||||
|
||||
writeValue(value: any /** TODO #9100 */) { this.value = `!${value}!`; }
|
||||
|
||||
registerOnChange(fn: any /** TODO #9100 */) { ObservableWrapper.subscribe(this.onInput, fn); }
|
||||
registerOnChange(fn: any /** TODO #9100 */) { this.onInput.subscribe({next: fn}); }
|
||||
|
||||
registerOnTouched(fn: any /** TODO #9100 */) {}
|
||||
|
||||
dispatchChangeEvent() {
|
||||
ObservableWrapper.callEmit(this.onInput, this.value.substring(1, this.value.length - 1));
|
||||
}
|
||||
dispatchChangeEvent() { this.onInput.emit(this.value.substring(1, this.value.length - 1)); }
|
||||
}
|
||||
|
||||
function uniqLoginAsyncValidator(expectedValue: string) {
|
||||
return (c: any /** TODO #9100 */) => {
|
||||
var completer = PromiseWrapper.completer();
|
||||
var res = (c.value == expectedValue) ? null : {'uniqLogin': true};
|
||||
completer.resolve(res);
|
||||
return completer.promise;
|
||||
return Promise.resolve((c.value == expectedValue) ? null : {'uniqLogin': true});
|
||||
};
|
||||
}
|
||||
|
||||
@ -1513,13 +1510,7 @@ function loginIsEmptyGroupValidator(c: ControlGroup) {
|
||||
|
||||
@Directive({
|
||||
selector: '[login-is-empty-validator]',
|
||||
providers: [
|
||||
/* @ts2dart_Provider */ {
|
||||
provide: NG_VALIDATORS,
|
||||
useValue: loginIsEmptyGroupValidator,
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
providers: [{provide: NG_VALIDATORS, useValue: loginIsEmptyGroupValidator, multi: true}]
|
||||
})
|
||||
class LoginIsEmptyValidator {
|
||||
}
|
||||
@ -1541,10 +1532,7 @@ class UniqLoginValidator implements Validator {
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: '',
|
||||
directives: [
|
||||
FORM_DIRECTIVES, WrappedValue, MyInput, NgIf, NgFor, LoginIsEmptyValidator, UniqLoginValidator
|
||||
],
|
||||
providers: [FORM_PROVIDERS]
|
||||
directives: [WrappedValue, MyInput, NgIf, NgFor, LoginIsEmptyValidator, UniqLoginValidator]
|
||||
})
|
||||
class MyComp8 {
|
||||
form: any;
|
||||
|
@ -1,33 +1,39 @@
|
||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, inject,} from '@angular/core/testing/testing_internal';
|
||||
import {fakeAsync, flushMicrotasks, Log, tick} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {ControlGroup, Control, ControlArray, Validators} from '@angular/common/src/forms-deprecated';
|
||||
import {IS_DART, isPresent} from '../../src/facade/lang';
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
import {TimerWrapper, ObservableWrapper, EventEmitter} from '../../src/facade/async';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Control, ControlArray, ControlGroup, Validators} from '@angular/common/src/forms-deprecated';
|
||||
import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {EventEmitter} from '../../src/facade/async';
|
||||
import {isPresent} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
function asyncValidator(expected: any /** TODO #9100 */, timeouts = /*@ts2dart_const*/ {}) {
|
||||
function asyncValidator(expected: any /** TODO #9100 */, timeouts = {}) {
|
||||
return (c: any /** TODO #9100 */) => {
|
||||
var completer = PromiseWrapper.completer();
|
||||
var t = isPresent((timeouts as any /** TODO #9100 */)[c.value]) ?
|
||||
(timeouts as any /** TODO #9100 */)[c.value] :
|
||||
0;
|
||||
var res = c.value != expected ? {'async': true} : null;
|
||||
return new Promise((resolve) => {
|
||||
var t = isPresent((timeouts as any /** TODO #9100 */)[c.value]) ?
|
||||
(timeouts as any /** TODO #9100 */)[c.value] :
|
||||
0;
|
||||
var res = c.value != expected ? {'async': true} : null;
|
||||
|
||||
if (t == 0) {
|
||||
completer.resolve(res);
|
||||
} else {
|
||||
TimerWrapper.setTimeout(() => { completer.resolve(res); }, t);
|
||||
}
|
||||
|
||||
return completer.promise;
|
||||
if (t == 0) {
|
||||
resolve(res);
|
||||
} else {
|
||||
setTimeout(() => { resolve(res); }, t);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function asyncValidatorReturningObservable(c: any /** TODO #9100 */) {
|
||||
var e = new EventEmitter();
|
||||
PromiseWrapper.scheduleMicrotask(() => ObservableWrapper.callEmit(e, {'async': true}));
|
||||
Promise.resolve(null).then(() => { e.emit({'async': true}); });
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -172,15 +178,16 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should fire an event', fakeAsync(() => {
|
||||
ObservableWrapper.subscribe(
|
||||
c.valueChanges, (value) => { expect(value).toEqual('newValue'); });
|
||||
|
||||
c.valueChanges.subscribe(
|
||||
{next: (value: any) => { expect(value).toEqual('newValue'); }});
|
||||
|
||||
c.updateValue('newValue');
|
||||
tick();
|
||||
}));
|
||||
|
||||
it('should not fire an event when explicitly specified', fakeAsync(() => {
|
||||
ObservableWrapper.subscribe(c.valueChanges, (value) => { throw 'Should not happen'; });
|
||||
c.valueChanges.subscribe({next: (value: any) => { throw 'Should not happen'; }});
|
||||
|
||||
c.updateValue('newValue', {emitEvent: false});
|
||||
|
||||
@ -195,18 +202,22 @@ export function main() {
|
||||
|
||||
it('should fire an event after the value has been updated',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
ObservableWrapper.subscribe(c.valueChanges, (value) => {
|
||||
expect(c.value).toEqual('new');
|
||||
expect(value).toEqual('new');
|
||||
async.done();
|
||||
c.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(c.value).toEqual('new');
|
||||
expect(value).toEqual('new');
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
c.updateValue('new');
|
||||
}));
|
||||
|
||||
it('should fire an event after the status has been updated to invalid', fakeAsync(() => {
|
||||
ObservableWrapper.subscribe(c.statusChanges, (status) => {
|
||||
expect(c.status).toEqual('INVALID');
|
||||
expect(status).toEqual('INVALID');
|
||||
c.statusChanges.subscribe({
|
||||
next: (status: any) => {
|
||||
expect(c.status).toEqual('INVALID');
|
||||
expect(status).toEqual('INVALID');
|
||||
}
|
||||
});
|
||||
|
||||
c.updateValue('');
|
||||
@ -217,9 +228,9 @@ export function main() {
|
||||
var c = new Control('old', Validators.required, asyncValidator('expected'));
|
||||
|
||||
var log: any[] /** TODO #9100 */ = [];
|
||||
ObservableWrapper.subscribe(c.valueChanges, (value) => log.push(`value: '${value}'`));
|
||||
ObservableWrapper.subscribe(
|
||||
c.statusChanges, (status) => log.push(`status: '${status}'`));
|
||||
c.valueChanges.subscribe({next: (value: any) => log.push(`value: '${value}'`)});
|
||||
|
||||
c.statusChanges.subscribe({next: (status: any) => log.push(`status: '${status}'`)});
|
||||
|
||||
c.updateValue('');
|
||||
tick();
|
||||
@ -244,24 +255,24 @@ export function main() {
|
||||
}));
|
||||
|
||||
// TODO: remove the if statement after making observable delivery sync
|
||||
if (!IS_DART) {
|
||||
it('should update set errors and status before emitting an event',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
c.valueChanges.subscribe((value: any /** TODO #9100 */) => {
|
||||
expect(c.valid).toEqual(false);
|
||||
expect(c.errors).toEqual({'required': true});
|
||||
async.done();
|
||||
});
|
||||
c.updateValue('');
|
||||
}));
|
||||
}
|
||||
it('should update set errors and status before emitting an event',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
c.valueChanges.subscribe((value: any /** TODO #9100 */) => {
|
||||
expect(c.valid).toEqual(false);
|
||||
expect(c.errors).toEqual({'required': true});
|
||||
async.done();
|
||||
});
|
||||
c.updateValue('');
|
||||
}));
|
||||
|
||||
it('should return a cold observable',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
c.updateValue('will be ignored');
|
||||
ObservableWrapper.subscribe(c.valueChanges, (value) => {
|
||||
expect(value).toEqual('new');
|
||||
async.done();
|
||||
c.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(value).toEqual('new');
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
c.updateValue('new');
|
||||
}));
|
||||
@ -422,7 +433,7 @@ export function main() {
|
||||
|
||||
// rename contains into has
|
||||
it('should return false when the component is not included',
|
||||
() => { expect(group.contains('optional')).toEqual(false); })
|
||||
() => { expect(group.contains('optional')).toEqual(false); });
|
||||
|
||||
it('should return false when there is no component with the given name',
|
||||
() => { expect(group.contains('something else')).toEqual(false); });
|
||||
@ -475,10 +486,12 @@ export function main() {
|
||||
|
||||
it('should fire an event after the value has been updated',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
ObservableWrapper.subscribe(g.valueChanges, (value) => {
|
||||
expect(g.value).toEqual({'one': 'new1', 'two': 'old2'});
|
||||
expect(value).toEqual({'one': 'new1', 'two': 'old2'});
|
||||
async.done();
|
||||
g.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(g.value).toEqual({'one': 'new1', 'two': 'old2'});
|
||||
expect(value).toEqual({'one': 'new1', 'two': 'old2'});
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
c1.updateValue('new1');
|
||||
}));
|
||||
@ -487,12 +500,14 @@ export function main() {
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var controlCallbackIsCalled = false;
|
||||
|
||||
ObservableWrapper.subscribe(
|
||||
c1.valueChanges, (value) => { controlCallbackIsCalled = true; });
|
||||
|
||||
ObservableWrapper.subscribe(g.valueChanges, (value) => {
|
||||
expect(controlCallbackIsCalled).toBe(true);
|
||||
async.done();
|
||||
c1.valueChanges.subscribe({next: (value: any) => { controlCallbackIsCalled = true; }});
|
||||
|
||||
g.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(controlCallbackIsCalled).toBe(true);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
c1.updateValue('new1');
|
||||
@ -500,9 +515,11 @@ export function main() {
|
||||
|
||||
it('should fire an event when a control is excluded',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
ObservableWrapper.subscribe(g.valueChanges, (value) => {
|
||||
expect(value).toEqual({'one': 'old1'});
|
||||
async.done();
|
||||
g.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(value).toEqual({'one': 'old1'});
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
g.exclude('two');
|
||||
@ -512,9 +529,11 @@ export function main() {
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
g.exclude('two');
|
||||
|
||||
ObservableWrapper.subscribe(g.valueChanges, (value) => {
|
||||
expect(value).toEqual({'one': 'old1', 'two': 'old2'});
|
||||
async.done();
|
||||
g.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(value).toEqual({'one': 'old1', 'two': 'old2'});
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
g.include('two');
|
||||
@ -524,14 +543,16 @@ export function main() {
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var loggedValues: any[] /** TODO #9100 */ = [];
|
||||
|
||||
ObservableWrapper.subscribe(g.valueChanges, (value) => {
|
||||
loggedValues.push(value);
|
||||
g.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
loggedValues.push(value);
|
||||
|
||||
if (loggedValues.length == 2) {
|
||||
expect(loggedValues).toEqual([
|
||||
{'one': 'new1', 'two': 'old2'}, {'one': 'new1', 'two': 'new2'}
|
||||
]);
|
||||
async.done();
|
||||
if (loggedValues.length == 2) {
|
||||
expect(loggedValues).toEqual([
|
||||
{'one': 'new1', 'two': 'old2'}, {'one': 'new1', 'two': 'new2'}
|
||||
]);
|
||||
async.done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -597,7 +618,7 @@ export function main() {
|
||||
expect(g.errors).toEqual({'async': true});
|
||||
expect(g.find(['one']).errors).toEqual({'async': true});
|
||||
}));
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe('ControlArray', () => {
|
||||
@ -730,10 +751,12 @@ export function main() {
|
||||
|
||||
it('should fire an event after the value has been updated',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
ObservableWrapper.subscribe(a.valueChanges, (value) => {
|
||||
expect(a.value).toEqual(['new1', 'old2']);
|
||||
expect(value).toEqual(['new1', 'old2']);
|
||||
async.done();
|
||||
a.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(a.value).toEqual(['new1', 'old2']);
|
||||
expect(value).toEqual(['new1', 'old2']);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
c1.updateValue('new1');
|
||||
}));
|
||||
@ -742,12 +765,14 @@ export function main() {
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var controlCallbackIsCalled = false;
|
||||
|
||||
ObservableWrapper.subscribe(
|
||||
c1.valueChanges, (value) => { controlCallbackIsCalled = true; });
|
||||
|
||||
ObservableWrapper.subscribe(a.valueChanges, (value) => {
|
||||
expect(controlCallbackIsCalled).toBe(true);
|
||||
async.done();
|
||||
c1.valueChanges.subscribe({next: (value: any) => { controlCallbackIsCalled = true; }});
|
||||
|
||||
a.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(controlCallbackIsCalled).toBe(true);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
c1.updateValue('new1');
|
||||
@ -755,9 +780,11 @@ export function main() {
|
||||
|
||||
it('should fire an event when a control is removed',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
ObservableWrapper.subscribe(a.valueChanges, (value) => {
|
||||
expect(value).toEqual(['old1']);
|
||||
async.done();
|
||||
a.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(value).toEqual(['old1']);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
a.removeAt(1);
|
||||
@ -767,9 +794,11 @@ export function main() {
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
a.removeAt(1);
|
||||
|
||||
ObservableWrapper.subscribe(a.valueChanges, (value) => {
|
||||
expect(value).toEqual(['old1', 'old2']);
|
||||
async.done();
|
||||
a.valueChanges.subscribe({
|
||||
next: (value: any) => {
|
||||
expect(value).toEqual(['old1', 'old2']);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
a.push(c2);
|
||||
@ -821,7 +850,7 @@ export function main() {
|
||||
expect(g.errors).toEqual({'async': true});
|
||||
expect(g.pending).toEqual(false);
|
||||
}));
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,16 +1,37 @@
|
||||
import {AbstractControl, Control, ControlArray, ControlGroup, Validators} from '@angular/common/src/forms-deprecated';
|
||||
import {Log, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {EventEmitter, ObservableWrapper, TimerWrapper} from '../../src/facade/async';
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
import {AbstractControl, Control, ControlArray, ControlGroup, Validators} from '@angular/common/src/forms-deprecated';
|
||||
import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
|
||||
import {EventEmitter} from '../../src/facade/async';
|
||||
import {normalizeAsyncValidator} from '../../src/forms-deprecated/directives/normalize_validator';
|
||||
|
||||
export function main() {
|
||||
function validator(key: string, error: any) {
|
||||
return function(c: AbstractControl) {
|
||||
var r = {};
|
||||
(r as any /** TODO #9100 */)[key] = error;
|
||||
(r as any)[key] = error;
|
||||
return r;
|
||||
};
|
||||
}
|
||||
|
||||
class AsyncValidatorDirective {
|
||||
constructor(private expected: string, private error: any) {}
|
||||
|
||||
validate(c: any): {[key: string]: any;} {
|
||||
return Observable.create((obs: any) => {
|
||||
const error = this.expected !== c.value ? this.error : null;
|
||||
obs.next(error);
|
||||
obs.complete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,6 +101,17 @@ export function main() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should normalize and evaluate async validator-directives correctly', fakeAsync(() => {
|
||||
const c = Validators.composeAsync(
|
||||
[normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))]);
|
||||
|
||||
let value: any = null;
|
||||
c(new Control()).then((v: any) => value = v);
|
||||
tick(1);
|
||||
|
||||
expect(value).toEqual({'one': true});
|
||||
}));
|
||||
|
||||
describe('compose', () => {
|
||||
it('should return null when given null',
|
||||
() => { expect(Validators.compose(null)).toBe(null); });
|
||||
@ -110,14 +142,14 @@ export function main() {
|
||||
return (c: any /** TODO #9100 */) => {
|
||||
var emitter = new EventEmitter();
|
||||
var res = c.value != expected ? response : null;
|
||||
|
||||
PromiseWrapper.scheduleMicrotask(() => {
|
||||
ObservableWrapper.callEmit(emitter, res);
|
||||
Promise.resolve(null).then(() => {
|
||||
emitter.emit(res);
|
||||
// this is required because of a bug in ObservableWrapper
|
||||
// where callComplete can fire before callEmit
|
||||
// remove this one the bug is fixed
|
||||
TimerWrapper.setTimeout(() => { ObservableWrapper.callComplete(emitter); }, 0);
|
||||
setTimeout(() => { emitter.complete(); }, 0);
|
||||
});
|
||||
|
||||
return emitter;
|
||||
};
|
||||
}
|
||||
|
@ -1,13 +1,20 @@
|
||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, inject,} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {SpyChangeDetectorRef} from '../spies';
|
||||
import {isBlank} from '../../src/facade/lang';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AsyncPipe} from '@angular/common';
|
||||
import {WrappedValue} from '@angular/core';
|
||||
import {EventEmitter, ObservableWrapper, PromiseWrapper, TimerWrapper} from '../../src/facade/async';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {PromiseCompleter} from '../../src/facade/promise';
|
||||
import {browserDetection} from '@angular/platform-browser/testing';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||
|
||||
import {EventEmitter} from '../../src/facade/async';
|
||||
import {isBlank} from '../../src/facade/lang';
|
||||
import {SpyChangeDetectorRef} from '../spies';
|
||||
|
||||
export function main() {
|
||||
describe('AsyncPipe', () => {
|
||||
@ -31,26 +38,25 @@ export function main() {
|
||||
it('should return the latest available value wrapped',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
pipe.transform(emitter);
|
||||
emitter.emit(message);
|
||||
|
||||
ObservableWrapper.callEmit(emitter, message);
|
||||
|
||||
TimerWrapper.setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
expect(pipe.transform(emitter)).toEqual(new WrappedValue(message));
|
||||
async.done();
|
||||
}, 0)
|
||||
}, 0);
|
||||
}));
|
||||
|
||||
|
||||
it('should return same value when nothing has changed since the last call',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
pipe.transform(emitter);
|
||||
ObservableWrapper.callEmit(emitter, message);
|
||||
emitter.emit(message);
|
||||
|
||||
TimerWrapper.setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
pipe.transform(emitter);
|
||||
expect(pipe.transform(emitter)).toBe(message);
|
||||
async.done();
|
||||
}, 0)
|
||||
}, 0);
|
||||
}));
|
||||
|
||||
it('should dispose of the existing subscription when subscribing to a new observable',
|
||||
@ -59,25 +65,24 @@ export function main() {
|
||||
|
||||
var newEmitter = new EventEmitter();
|
||||
expect(pipe.transform(newEmitter)).toBe(null);
|
||||
emitter.emit(message);
|
||||
|
||||
// this should not affect the pipe
|
||||
ObservableWrapper.callEmit(emitter, message);
|
||||
|
||||
TimerWrapper.setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
expect(pipe.transform(newEmitter)).toBe(null);
|
||||
async.done();
|
||||
}, 0)
|
||||
}, 0);
|
||||
}));
|
||||
|
||||
it('should request a change detection check upon receiving a new value',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
pipe.transform(emitter);
|
||||
ObservableWrapper.callEmit(emitter, message);
|
||||
emitter.emit(message);
|
||||
|
||||
TimerWrapper.setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
expect(ref.spy('markForCheck')).toHaveBeenCalled();
|
||||
async.done();
|
||||
}, 10)
|
||||
}, 10);
|
||||
}));
|
||||
});
|
||||
|
||||
@ -89,13 +94,12 @@ export function main() {
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
pipe.transform(emitter);
|
||||
pipe.ngOnDestroy();
|
||||
emitter.emit(message);
|
||||
|
||||
ObservableWrapper.callEmit(emitter, message);
|
||||
|
||||
TimerWrapper.setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
expect(pipe.transform(emitter)).toBe(null);
|
||||
async.done();
|
||||
}, 0)
|
||||
}, 0);
|
||||
}));
|
||||
});
|
||||
});
|
||||
@ -103,71 +107,76 @@ export function main() {
|
||||
describe('Promise', () => {
|
||||
var message = new Object();
|
||||
var pipe: AsyncPipe;
|
||||
var completer: PromiseCompleter<any>;
|
||||
var resolve: (result: any) => void;
|
||||
var reject: (error: any) => void;
|
||||
var promise: Promise<any>;
|
||||
var ref: SpyChangeDetectorRef;
|
||||
// adds longer timers for passing tests in IE
|
||||
var timer = (!isBlank(getDOM()) && browserDetection.isIE) ? 50 : 10;
|
||||
|
||||
beforeEach(() => {
|
||||
completer = PromiseWrapper.completer();
|
||||
promise = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
ref = new SpyChangeDetectorRef();
|
||||
pipe = new AsyncPipe(<any>ref);
|
||||
});
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return null when subscribing to a promise',
|
||||
() => { expect(pipe.transform(completer.promise)).toBe(null); });
|
||||
() => { expect(pipe.transform(promise)).toBe(null); });
|
||||
|
||||
it('should return the latest available value',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
pipe.transform(completer.promise);
|
||||
pipe.transform(promise);
|
||||
|
||||
completer.resolve(message);
|
||||
resolve(message);
|
||||
|
||||
TimerWrapper.setTimeout(() => {
|
||||
expect(pipe.transform(completer.promise)).toEqual(new WrappedValue(message));
|
||||
setTimeout(() => {
|
||||
expect(pipe.transform(promise)).toEqual(new WrappedValue(message));
|
||||
async.done();
|
||||
}, timer)
|
||||
}, timer);
|
||||
}));
|
||||
|
||||
it('should return unwrapped value when nothing has changed since the last call',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
pipe.transform(completer.promise);
|
||||
completer.resolve(message);
|
||||
pipe.transform(promise);
|
||||
resolve(message);
|
||||
|
||||
TimerWrapper.setTimeout(() => {
|
||||
pipe.transform(completer.promise);
|
||||
expect(pipe.transform(completer.promise)).toBe(message);
|
||||
setTimeout(() => {
|
||||
pipe.transform(promise);
|
||||
expect(pipe.transform(promise)).toBe(message);
|
||||
async.done();
|
||||
}, timer)
|
||||
}, timer);
|
||||
}));
|
||||
|
||||
it('should dispose of the existing subscription when subscribing to a new promise',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
pipe.transform(completer.promise);
|
||||
pipe.transform(promise);
|
||||
|
||||
var newCompleter = PromiseWrapper.completer();
|
||||
expect(pipe.transform(newCompleter.promise)).toBe(null);
|
||||
var promise = new Promise<any>(() => {});
|
||||
expect(pipe.transform(promise)).toBe(null);
|
||||
|
||||
// this should not affect the pipe, so it should return WrappedValue
|
||||
completer.resolve(message);
|
||||
resolve(message);
|
||||
|
||||
TimerWrapper.setTimeout(() => {
|
||||
expect(pipe.transform(newCompleter.promise)).toBe(null);
|
||||
setTimeout(() => {
|
||||
expect(pipe.transform(promise)).toBe(null);
|
||||
async.done();
|
||||
}, timer)
|
||||
}, timer);
|
||||
}));
|
||||
|
||||
it('should request a change detection check upon receiving a new value',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var markForCheck = ref.spy('markForCheck');
|
||||
pipe.transform(completer.promise);
|
||||
completer.resolve(message);
|
||||
pipe.transform(promise);
|
||||
resolve(message);
|
||||
|
||||
TimerWrapper.setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
expect(markForCheck).toHaveBeenCalled();
|
||||
async.done();
|
||||
}, timer)
|
||||
}, timer);
|
||||
}));
|
||||
|
||||
describe('ngOnDestroy', () => {
|
||||
@ -176,15 +185,15 @@ export function main() {
|
||||
|
||||
it('should dispose of the existing source',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
pipe.transform(completer.promise);
|
||||
expect(pipe.transform(completer.promise)).toBe(null);
|
||||
completer.resolve(message)
|
||||
pipe.transform(promise);
|
||||
expect(pipe.transform(promise)).toBe(null);
|
||||
resolve(message);
|
||||
|
||||
|
||||
TimerWrapper.setTimeout(() => {
|
||||
expect(pipe.transform(completer.promise)).toEqual(new WrappedValue(message));
|
||||
setTimeout(() => {
|
||||
expect(pipe.transform(promise)).toEqual(new WrappedValue(message));
|
||||
pipe.ngOnDestroy();
|
||||
expect(pipe.transform(completer.promise)).toBe(null);
|
||||
expect(pipe.transform(promise)).toBe(null);
|
||||
async.done();
|
||||
}, timer);
|
||||
}));
|
||||
|
@ -1,7 +1,15 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {DatePipe} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||
|
||||
import {DateWrapper} from '../../src/facade/lang';
|
||||
|
||||
@ -11,7 +19,7 @@ export function main() {
|
||||
var pipe: DatePipe;
|
||||
|
||||
beforeEach(() => {
|
||||
date = DateWrapper.create(2015, 6, 15, 21, 3, 1);
|
||||
date = DateWrapper.create(2015, 6, 15, 9, 3, 1);
|
||||
pipe = new DatePipe();
|
||||
});
|
||||
|
||||
@ -48,12 +56,15 @@ export function main() {
|
||||
expect(pipe.transform(date, 'd')).toEqual('15');
|
||||
expect(pipe.transform(date, 'E')).toEqual('Mon');
|
||||
expect(pipe.transform(date, 'EEEE')).toEqual('Monday');
|
||||
expect(pipe.transform(date, 'H')).toEqual('21');
|
||||
expect(pipe.transform(date, 'j')).toEqual('9 PM');
|
||||
expect(pipe.transform(date, 'h')).toEqual('9');
|
||||
expect(pipe.transform(date, 'hh')).toEqual('09');
|
||||
expect(pipe.transform(date, 'HH')).toEqual('09');
|
||||
expect(pipe.transform(date, 'j')).toEqual('9 AM');
|
||||
expect(pipe.transform(date, 'm')).toEqual('3');
|
||||
expect(pipe.transform(date, 's')).toEqual('1');
|
||||
expect(pipe.transform(date, 'mm')).toEqual('03');
|
||||
expect(pipe.transform(date, 'ss')).toEqual('01');
|
||||
expect(pipe.transform(date, 'Z')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should format common multi component patterns', () => {
|
||||
@ -66,22 +77,22 @@ export function main() {
|
||||
expect(pipe.transform(date, 'MEd')).toEqual('6Mon15');
|
||||
expect(pipe.transform(date, 'MMMd')).toEqual('Jun15');
|
||||
expect(pipe.transform(date, 'yMMMMEEEEd')).toEqual('Monday, June 15, 2015');
|
||||
expect(pipe.transform(date, 'jms')).toEqual('9:03:01 PM');
|
||||
|
||||
expect(pipe.transform(date, 'ms')).toEqual('31');
|
||||
expect(pipe.transform(date, 'jm')).toEqual('9:03 PM');
|
||||
expect(pipe.transform(date, 'jm')).toEqual('9:03 AM');
|
||||
});
|
||||
|
||||
it('should format with pattern aliases', () => {
|
||||
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015, 9:03:01 PM');
|
||||
expect(pipe.transform(date, 'short')).toEqual('6/15/2015, 9:03 PM');
|
||||
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015, 9:03:01 AM');
|
||||
expect(pipe.transform(date, 'short')).toEqual('6/15/2015, 9:03 AM');
|
||||
expect(pipe.transform(date, 'dd/MM/yyyy')).toEqual('15/06/2015');
|
||||
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
|
||||
expect(pipe.transform(date, 'fullDate')).toEqual('Monday, June 15, 2015');
|
||||
expect(pipe.transform(date, 'longDate')).toEqual('June 15, 2015');
|
||||
expect(pipe.transform(date, 'mediumDate')).toEqual('Jun 15, 2015');
|
||||
expect(pipe.transform(date, 'shortDate')).toEqual('6/15/2015');
|
||||
expect(pipe.transform(date, 'mediumTime')).toEqual('9:03:01 PM');
|
||||
expect(pipe.transform(date, 'shortTime')).toEqual('9:03 PM');
|
||||
expect(pipe.transform(date, 'mediumTime')).toEqual('9:03:01 AM');
|
||||
expect(pipe.transform(date, 'shortTime')).toEqual('9:03 AM');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,18 +1,31 @@
|
||||
import {I18nPluralPipe} from '@angular/common';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {I18nPluralPipe, NgLocalization} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('I18nPluralPipe', () => {
|
||||
var localization: NgLocalization;
|
||||
var pipe: I18nPluralPipe;
|
||||
var mapping = {'=0': 'No messages.', '=1': 'One message.', 'other': 'There are some messages.'};
|
||||
var interpolatedMapping = {
|
||||
|
||||
var mapping = {
|
||||
'=0': 'No messages.',
|
||||
'=1': 'One message.',
|
||||
'other': 'There are # messages, that is #.'
|
||||
'many': 'Many messages.',
|
||||
'other': 'There are # messages, that is #.',
|
||||
};
|
||||
|
||||
beforeEach(() => { pipe = new I18nPluralPipe(); });
|
||||
beforeEach(() => {
|
||||
localization = new TestLocalization();
|
||||
pipe = new I18nPluralPipe(localization);
|
||||
});
|
||||
|
||||
it('should be marked as pure',
|
||||
() => { expect(new PipeResolver().resolve(I18nPluralPipe).pure).toEqual(true); });
|
||||
@ -28,19 +41,19 @@ export function main() {
|
||||
expect(val).toEqual('One message.');
|
||||
});
|
||||
|
||||
it('should return other text if value is anything other than 0 or 1', () => {
|
||||
var val = pipe.transform(6, mapping);
|
||||
expect(val).toEqual('There are some messages.');
|
||||
it('should return category messages', () => {
|
||||
var val = pipe.transform(4, mapping);
|
||||
expect(val).toEqual('Many messages.');
|
||||
});
|
||||
|
||||
it('should interpolate the value into the text where indicated', () => {
|
||||
var val = pipe.transform(6, interpolatedMapping);
|
||||
var val = pipe.transform(6, mapping);
|
||||
expect(val).toEqual('There are 6 messages, that is 6.');
|
||||
});
|
||||
|
||||
it('should use \'other\' if value is undefined', () => {
|
||||
var val = pipe.transform(void(0), interpolatedMapping);
|
||||
expect(val).toEqual('There are messages, that is .');
|
||||
it('should use "" if value is undefined', () => {
|
||||
var val = pipe.transform(void(0), mapping);
|
||||
expect(val).toEqual('');
|
||||
});
|
||||
|
||||
it('should not support bad arguments',
|
||||
@ -49,3 +62,7 @@ export function main() {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class TestLocalization extends NgLocalization {
|
||||
getPluralCategory(value: number): string { return value > 1 && value < 6 ? 'many' : 'other'; }
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {I18nSelectPipe} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
@ -23,14 +31,14 @@ export function main() {
|
||||
expect(val).toEqual('Invite her.');
|
||||
});
|
||||
|
||||
it('should return other text if value is anything other than male or female', () => {
|
||||
it('should return "" if value is anything other than male or female', () => {
|
||||
var val = pipe.transform('Anything else', mapping);
|
||||
expect(val).toEqual('Invite them.');
|
||||
expect(val).toEqual('');
|
||||
});
|
||||
|
||||
it('should use \'other\' if value is undefined', () => {
|
||||
it('should use "" if value is undefined', () => {
|
||||
var val = pipe.transform(void(0), mapping);
|
||||
expect(val).toEqual('Invite them.');
|
||||
expect(val).toEqual('');
|
||||
});
|
||||
|
||||
it('should not support bad arguments',
|
||||
|
@ -1,10 +1,18 @@
|
||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, inject,} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder} from '@angular/compiler/testing';
|
||||
import {Json, StringWrapper} from '../../src/facade/lang';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {JsonPipe} from '@angular/common';
|
||||
import {Component} from '@angular/core';
|
||||
import {TestComponentBuilder} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
import {Json, StringWrapper} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('JsonPipe', () => {
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {LowerCasePipe} from '@angular/common';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
|
@ -1,7 +1,14 @@
|
||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach,} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {DecimalPipe, PercentPipe, CurrencyPipe} from '@angular/common';
|
||||
import {CurrencyPipe, DecimalPipe, PercentPipe} from '@angular/common';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||
|
||||
export function main() {
|
||||
describe('Number pipes', () => {
|
||||
@ -24,8 +31,19 @@ export function main() {
|
||||
expect(pipe.transform(1.1234)).toEqual('1.123');
|
||||
});
|
||||
|
||||
it('should not support other objects',
|
||||
() => { expect(() => pipe.transform(new Object())).toThrowError(); });
|
||||
it('should support strings', () => {
|
||||
expect(pipe.transform('12345')).toEqual('12,345');
|
||||
expect(pipe.transform('123', '.2')).toEqual('123.00');
|
||||
expect(pipe.transform('1', '3.')).toEqual('001');
|
||||
expect(pipe.transform('1.1', '3.4-5')).toEqual('001.1000');
|
||||
expect(pipe.transform('1.123456', '3.4-5')).toEqual('001.12346');
|
||||
expect(pipe.transform('1.1234')).toEqual('1.123');
|
||||
});
|
||||
|
||||
it('should not support other objects', () => {
|
||||
expect(() => pipe.transform(new Object())).toThrowError();
|
||||
expect(() => pipe.transform('123abc')).toThrowError();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -52,9 +70,9 @@ export function main() {
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return correct value for numbers', () => {
|
||||
expect(pipe.transform(123)).toEqual('USD123');
|
||||
expect(pipe.transform(12, 'EUR', false, '.2')).toEqual('EUR12.00');
|
||||
expect(pipe.transform(5.123, 'USD', false, '.0-2')).toEqual('USD5.12');
|
||||
expect(pipe.transform(123)).toEqual('USD123.00');
|
||||
expect(pipe.transform(12, 'EUR', false, '.1')).toEqual('EUR12.0');
|
||||
expect(pipe.transform(5.1234, 'USD', false, '.0-3')).toEqual('USD5.123');
|
||||
});
|
||||
|
||||
it('should not support other objects',
|
||||
|
@ -1,8 +1,13 @@
|
||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, inject,} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ReplacePipe} from '@angular/common';
|
||||
import {RegExpWrapper, StringJoiner} from '../../src/facade/lang';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('ReplacePipe', () => {
|
||||
@ -38,9 +43,9 @@ export function main() {
|
||||
it('should return a new string with the pattern replaced', () => {
|
||||
var result1 = pipe.transform(str, 'Douglas', 'Hugh');
|
||||
|
||||
var result2 = pipe.transform(str, RegExpWrapper.create('a'), '_');
|
||||
var result2 = pipe.transform(str, /a/g, '_');
|
||||
|
||||
var result3 = pipe.transform(str, RegExpWrapper.create('a', 'i'), '_');
|
||||
var result3 = pipe.transform(str, /a/gi, '_');
|
||||
|
||||
var f = ((x: any) => { return 'Adams!'; });
|
||||
|
||||
|
@ -1,11 +1,17 @@
|
||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, inject,} from '@angular/core/testing/testing_internal';
|
||||
import {} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {SlicePipe} from '@angular/common';
|
||||
import {Component} from '@angular/core';
|
||||
import {TestComponentBuilder} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter, afterEach, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/browser_util';
|
||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||
|
||||
export function main() {
|
||||
describe('SlicePipe', () => {
|
||||
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {UpperCasePipe} from '@angular/common';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
library core.spies;
|
||||
|
||||
import 'package:angular2/common.dart';
|
||||
import 'package:angular2/src/core/change_detection/change_detection.dart';
|
||||
import 'package:angular2/testing_internal.dart';
|
||||
|
||||
@proxy
|
||||
class SpyNgControl extends SpyObject implements NgControl {}
|
||||
|
||||
@proxy
|
||||
class SpyValueAccessor extends SpyObject implements ControlValueAccessor {}
|
||||
|
||||
@proxy
|
||||
class SpyChangeDetectorRef extends SpyObject implements ChangeDetectorRef {}
|
@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectorRef} from '@angular/core/src/change_detection/change_detector_ref';
|
||||
import {SpyObject, proxy} from '@angular/core/testing/testing_internal';
|
||||
|
||||
|
@ -1,2 +1,10 @@
|
||||
/**
|
||||
* @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 {SpyLocation} from './testing/location_mock';
|
||||
export {MockLocationStrategy} from './testing/mock_location_strategy';
|
||||
export {MockLocationStrategy} from './testing/mock_location_strategy';
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user